diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2013, Benjamin S. Scarlet and Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Benjamin S. Scarlet nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,189 @@
+{-# LANGUAGE FlexibleInstances #-}
+import Control.Exception (SomeException, try)
+import Control.Monad
+import Data.Functor
+import Data.Maybe
+import Data.List (isPrefixOf, (\\), intercalate, stripPrefix, find)
+import Data.Map (Map)
+import qualified Data.Map as Map
+import Data.Monoid
+import Data.Char
+import Distribution.Simple
+import Distribution.Simple.PreProcess
+import Distribution.Simple.Program
+import Distribution.Simple.Setup hiding (Flag)
+import Distribution.Simple.LocalBuildInfo
+import Distribution.PackageDescription
+import Distribution.Version
+import System.Environment
+import Distribution.System
+
+-- define these selectively in C files (we are _not_ using HsFFI.h),
+-- rather than universally in the ccOptions, because HsFFI.h currently defines them
+-- without checking they're already defined and so causes warnings.
+uncheckedHsFFIDefines = ["__STDC_LIMIT_MACROS"]
+
+llvmVersion = Version [4,0] []
+
+llvmConfigNames = [
+  "llvm-config-" ++ (intercalate "." . map show . versionBranch $ llvmVersion),
+  "llvm-config"
+ ]
+
+findJustBy :: Monad m => (a -> m (Maybe b)) -> [a] -> m (Maybe b)
+findJustBy f (x:xs) = do
+  x' <- f x
+  case x' of
+    Nothing -> findJustBy f xs
+    j -> return j
+findJustBy _ [] = return Nothing
+
+class ProgramSearch a where
+  programSearch :: (String -> a) -> a
+
+-- this instance is used before Cabal-1.18.0, when programFindLocation took one argument
+instance Monad m => ProgramSearch (v -> m (Maybe b)) where
+  programSearch checkName = \v -> findJustBy (\n -> checkName n v) llvmConfigNames
+
+-- this instance is used for and after Cabal-1.18.0, when programFindLocation took two arguments
+instance Monad m => ProgramSearch (v -> p -> m (Maybe b)) where
+  programSearch checkName = \v p -> findJustBy (\n -> checkName n v p) llvmConfigNames
+
+class OldHookable hook where
+  preHookOld :: (PackageDescription -> LocalBuildInfo -> UserHooks -> TestFlags -> IO ()) -> hook -> hook
+
+-- this instance is used before Cabal-1.22.0.0, when testHook took four arguments
+instance OldHookable (PackageDescription -> LocalBuildInfo -> UserHooks -> TestFlags -> IO ()) where
+  preHookOld f hook = \packageDescription localBuildInfo userHooks testFlags -> do
+    f packageDescription localBuildInfo userHooks testFlags
+    hook packageDescription localBuildInfo userHooks testFlags
+
+-- this instance is used for and after Cabal-1.22.0.0, when testHook took four five arguments
+instance OldHookable (Args -> PackageDescription -> LocalBuildInfo -> UserHooks -> TestFlags -> IO ()) where
+  preHookOld f hook = \args packageDescription localBuildInfo userHooks testFlags -> do
+    f packageDescription localBuildInfo userHooks testFlags
+    hook args packageDescription localBuildInfo userHooks testFlags
+
+llvmProgram :: Program
+llvmProgram = (simpleProgram "llvm-config") {
+  programFindLocation = programSearch (programFindLocation . simpleProgram),
+  programFindVersion =
+    let
+      stripSuffix suf str = let r = reverse in liftM r (stripPrefix (r suf) (r str))
+      svnToTag v = maybe v (++"-svn") (stripSuffix "svn" v)
+      trim = dropWhile isSpace . reverse . dropWhile isSpace . reverse
+    in
+      \v p -> findProgramVersion "--version" (svnToTag . trim) v p
+ }
+
+getLLVMConfig :: ConfigFlags -> IO ([String] -> IO String)
+getLLVMConfig configFlags = do
+  let verbosity = fromFlag $ configVerbosity configFlags
+  (program, _, _) <- requireProgramVersion verbosity llvmProgram
+                     (withinVersion llvmVersion)
+                     (configPrograms configFlags)
+  return $ getProgramOutput verbosity program
+
+addToLdLibraryPath :: String -> IO ()
+addToLdLibraryPath path = do
+  let (ldLibraryPathVar, ldLibraryPathSep) =
+        case buildOS of
+          OSX -> ("DYLD_LIBRARY_PATH",":")
+          _ -> ("LD_LIBRARY_PATH",":")
+  v <- try $ getEnv ldLibraryPathVar :: IO (Either SomeException String)
+  setEnv ldLibraryPathVar (path ++ either (const "") (ldLibraryPathSep ++) v)
+
+addLLVMToLdLibraryPath :: ConfigFlags -> IO ()
+addLLVMToLdLibraryPath configFlags = do
+  llvmConfig <- getLLVMConfig configFlags
+  [libDir] <- liftM lines $ llvmConfig ["--libdir"]
+  addToLdLibraryPath libDir
+
+-- | These flags are not relevant for us and dropping them allows
+-- linking against LLVM build with Clang using GCC
+ignoredCxxFlags :: [String]
+ignoredCxxFlags =
+  ["-Wcovered-switch-default", "-fcolor-diagnostics"] ++ map ("-D" ++) uncheckedHsFFIDefines
+
+ignoredCFlags :: [String]
+ignoredCFlags = ["-Wcovered-switch-default", "-Wdelete-non-virtual-dtor", "-fcolor-diagnostics"]
+
+-- | Header directories are added separately to configExtraIncludeDirs
+isIncludeFlag :: String -> Bool
+isIncludeFlag flag = "-I" `isPrefixOf` flag
+
+isIgnoredCFlag :: String -> Bool
+isIgnoredCFlag flag = flag `elem` ignoredCFlags || isIncludeFlag flag
+
+isIgnoredCxxFlag :: String -> Bool
+isIgnoredCxxFlag flag = flag `elem` ignoredCxxFlags || isIncludeFlag flag
+
+main = do
+  let origUserHooks = simpleUserHooks
+
+  defaultMainWithHooks origUserHooks {
+    hookedPrograms = [ llvmProgram ],
+
+    confHook = \(genericPackageDescription, hookedBuildInfo) configFlags -> do
+      llvmConfig <- getLLVMConfig configFlags
+      llvmCxxFlags <- do
+        rawLlvmCxxFlags <- llvmConfig ["--cxxflags"]
+        return . filter (not . isIgnoredCxxFlag) $ words rawLlvmCxxFlags
+      let stdLib = maybe "stdc++"
+                         (drop (length stdlibPrefix))
+                         (find (isPrefixOf stdlibPrefix) llvmCxxFlags)
+            where stdlibPrefix = "-stdlib=lib"
+      includeDirs <- liftM lines $ llvmConfig ["--includedir"]
+      libDirs@[libDir] <- liftM lines $ llvmConfig ["--libdir"]
+      [llvmVersion] <- liftM lines $ llvmConfig ["--version"]
+      let getLibs = liftM (map (fromJust . stripPrefix "-l") . words) . llvmConfig
+          flags    = configConfigurationsFlags configFlags
+          linkFlag = case lookup (FlagName "shared-llvm") flags of
+                       Nothing     -> "--link-static"
+                       Just shared -> if shared then "--link-shared" else "--link-static"
+      libs       <- getLibs ["--libs", linkFlag]
+      systemLibs <- getLibs ["--system-libs"]
+
+      let genericPackageDescription' = genericPackageDescription {
+            condLibrary = do
+              libraryCondTree <- condLibrary genericPackageDescription
+              return libraryCondTree {
+                condTreeData = condTreeData libraryCondTree <> mempty {
+                    libBuildInfo =
+                      mempty {
+                        ccOptions = llvmCxxFlags,
+                        extraLibs = stdLib : libs ++ systemLibs
+                      }
+                  }
+              }
+           }
+          configFlags' = configFlags {
+            configExtraLibDirs = libDirs ++ configExtraLibDirs configFlags,
+            configExtraIncludeDirs = includeDirs ++ configExtraIncludeDirs configFlags
+           }
+      addLLVMToLdLibraryPath configFlags'
+      confHook simpleUserHooks (genericPackageDescription', hookedBuildInfo) configFlags',
+
+    hookedPreProcessors =
+      let origHookedPreprocessors = hookedPreProcessors origUserHooks
+          newHsc buildInfo localBuildInfo =
+              PreProcessor {
+                  platformIndependent = platformIndependent (origHsc buildInfo localBuildInfo),
+                  runPreProcessor = \inFiles outFiles verbosity -> do
+                      llvmConfig <- getLLVMConfig (configFlags localBuildInfo)
+                      llvmCFlags <- do
+                          rawLlvmCFlags <- llvmConfig ["--cflags"]
+                          return . filter (not . isIgnoredCFlag) $ words rawLlvmCFlags
+                      let buildInfo' = buildInfo { ccOptions = "-Wno-variadic-macros" : llvmCFlags }
+                      runPreProcessor (origHsc buildInfo' localBuildInfo) inFiles outFiles verbosity
+              }
+              where origHsc = fromMaybe ppHsc2hs (lookup "hsc" origHookedPreprocessors)
+      in [("hsc", newHsc)] ++ origHookedPreprocessors,
+
+    buildHook = \packageDescription localBuildInfo userHooks buildFlags -> do
+          addLLVMToLdLibraryPath (configFlags localBuildInfo)
+          buildHook origUserHooks packageDescription localBuildInfo userHooks buildFlags,
+
+    testHook = preHookOld (\_ localBuildInfo _ _ -> addLLVMToLdLibraryPath (configFlags localBuildInfo))
+               (testHook origUserHooks)
+   }
diff --git a/llvm-hs.cabal b/llvm-hs.cabal
new file mode 100644
--- /dev/null
+++ b/llvm-hs.cabal
@@ -0,0 +1,273 @@
+name: llvm-hs
+version: 4.0.0.0
+license: BSD3
+license-file: LICENSE
+author: Anthony Cowley, Stephen Diehl, Moritz Kiefer <moritz.kiefer@purelyfunctional.org>, Benjamin S. Scarlet
+maintainer: Anthony Cowley, Stephen Diehl, Moritz Kiefer <moritz.kiefer@purelyfunctional.org>
+copyright: (c) 2013 Benjamin S. Scarlet and Google Inc.
+homepage: http://github.com/llvm-hs/llvm-hs/
+bug-reports: http://github.com/llvm-hs/llvm-hs/issues
+build-type: Custom
+stability: experimental
+cabal-version: >= 1.8
+category: Compilers/Interpreters, Code Generation
+synopsis: General purpose LLVM bindings
+description:
+  llvm-hs is a set of Haskell bindings for LLVM <http://llvm.org/>. Unlike other current Haskell bindings,
+  it uses an ADT to represent LLVM IR (<http://llvm.org/docs/LangRef.html>), and so offers two advantages: it
+  handles almost all of the stateful complexities of using the LLVM API to build IR; and it supports moving IR not
+  only from Haskell into LLVM C++ objects, but the other direction - from LLVM C++ into Haskell.
+extra-source-files:
+  src/LLVM/Internal/FFI/Analysis.h
+  src/LLVM/Internal/FFI/Attribute.h
+  src/LLVM/Internal/FFI/AttributeC.hpp
+  src/LLVM/Internal/FFI/BinaryOperator.h
+  src/LLVM/Internal/FFI/CallingConvention.h
+  src/LLVM/Internal/FFI/Constant.h
+  src/LLVM/Internal/FFI/GlobalValue.h
+  src/LLVM/Internal/FFI/InlineAssembly.h
+  src/LLVM/Internal/FFI/Instruction.h
+  src/LLVM/Internal/FFI/LibFunc.h
+  src/LLVM/Internal/FFI/Metadata.hpp
+  src/LLVM/Internal/FFI/OrcJIT.h
+  src/LLVM/Internal/FFI/SMDiagnostic.h
+  src/LLVM/Internal/FFI/Target.h
+  src/LLVM/Internal/FFI/Target.hpp
+  src/LLVM/Internal/FFI/Type.h
+  src/LLVM/Internal/FFI/Value.h
+tested-with: GHC == 7.8.4, GHC == 7.10.3, GHC == 8.0.2
+
+source-repository head
+  type: git
+  location: git://github.com/llvm-hs/llvm-hs.git
+  branch: llvm-4
+
+flag shared-llvm
+  description: link against llvm shared rather than static library
+  default: True
+
+flag debug
+  description: compile C(++) shims with debug info for ease of troubleshooting
+  default: False
+
+flag semigroups
+  description: Add semigroups to build-depends for Data.List.NonEmpty. This will be selected automatically by cabal.
+  default: False
+
+custom-setup
+  setup-depends: base
+               , Cabal
+               , containers
+
+library
+  build-tools: llvm-config
+  ghc-options: -Wall -fno-warn-name-shadowing -fno-warn-orphans -fno-warn-incomplete-patterns -fno-warn-missing-signatures -fno-warn-unused-matches -fno-warn-unused-do-bind -fno-warn-type-defaults
+  if flag(semigroups)
+    build-depends:
+      base >= 4.7 && < 4.9,
+      semigroups >= 0.18 && < 0.19
+  else
+    build-depends:
+      base >= 4.9 && < 5
+  build-depends:
+    utf8-string >= 0.3.7,
+    bytestring >= 0.9.1.10,
+    transformers >= 0.3 && < 0.6,
+    transformers-compat >= 0.4,
+    mtl >= 2.1.3,
+    template-haskell >= 2.5.0.0,
+    containers >= 0.4.2.1,
+    parsec >= 3.1.3,
+    array >= 0.4.0.0,
+    llvm-hs-pure == 4.0.0.0
+  hs-source-dirs: src
+  extensions:
+    NoImplicitPrelude
+    TupleSections
+    DeriveDataTypeable
+    EmptyDataDecls
+    FlexibleContexts
+    FlexibleInstances
+    StandaloneDeriving
+    ConstraintKinds
+  exposed-modules:
+    LLVM
+    LLVM.Analysis
+    LLVM.CodeGenOpt
+    LLVM.CodeModel
+    LLVM.CommandLine
+    LLVM.Context
+    LLVM.Diagnostic
+    LLVM.ExecutionEngine
+    LLVM.Internal.Analysis
+    LLVM.Internal.Atomicity
+    LLVM.Internal.Attribute
+    LLVM.Internal.BasicBlock
+    LLVM.Internal.CallingConvention
+    LLVM.Internal.Coding
+    LLVM.Internal.CommandLine
+    LLVM.Internal.Constant
+    LLVM.Internal.Context
+    LLVM.Internal.DataLayout
+    LLVM.Internal.DecodeAST
+    LLVM.Internal.Diagnostic
+    LLVM.Internal.EncodeAST
+    LLVM.Internal.ExecutionEngine
+    LLVM.Internal.FastMathFlags
+    LLVM.Internal.FloatingPointPredicate
+    LLVM.Internal.Function
+    LLVM.Internal.Global
+    LLVM.Internal.Inject
+    LLVM.Internal.InlineAssembly
+    LLVM.Internal.Instruction
+    LLVM.Internal.InstructionDefs
+    LLVM.Internal.IntegerPredicate
+    LLVM.Internal.LibraryFunction
+    LLVM.Internal.MemoryBuffer
+    LLVM.Internal.Metadata
+    LLVM.Internal.Module
+    LLVM.Internal.OrcJIT
+    LLVM.Internal.OrcJIT.CompileOnDemandLayer
+    LLVM.Internal.OrcJIT.IRCompileLayer
+    LLVM.Internal.Operand
+    LLVM.Internal.PassManager
+    LLVM.Internal.RawOStream
+    LLVM.Internal.RMWOperation
+    LLVM.Internal.String
+    LLVM.Internal.TailCallKind
+    LLVM.Internal.Target
+    LLVM.Internal.Threading
+    LLVM.Internal.Type
+    LLVM.Internal.Value
+    LLVM.Internal.FFI.Analysis
+    LLVM.Internal.FFI.Attribute
+    LLVM.Internal.FFI.Assembly
+    LLVM.Internal.FFI.BasicBlock
+    LLVM.Internal.FFI.BinaryOperator
+    LLVM.Internal.FFI.Bitcode
+    LLVM.Internal.FFI.Builder
+    LLVM.Internal.FFI.ByteRangeCallback
+    LLVM.Internal.FFI.Cleanup
+    LLVM.Internal.FFI.CommandLine
+    LLVM.Internal.FFI.Constant
+    LLVM.Internal.FFI.Context
+    LLVM.Internal.FFI.DataLayout
+    LLVM.Internal.FFI.ExecutionEngine
+    LLVM.Internal.FFI.Function
+    LLVM.Internal.FFI.GlobalAlias
+    LLVM.Internal.FFI.GlobalValue
+    LLVM.Internal.FFI.GlobalVariable
+    LLVM.Internal.FFI.InlineAssembly
+    LLVM.Internal.FFI.Instruction
+    LLVM.Internal.FFI.InstructionDefs
+    LLVM.Internal.FFI.Iterate
+    LLVM.Internal.FFI.LLVMCTypes
+    LLVM.Internal.FFI.MemoryBuffer
+    LLVM.Internal.FFI.Metadata
+    LLVM.Internal.FFI.Module
+    LLVM.Internal.FFI.OrcJIT
+    LLVM.Internal.FFI.OrcJIT.CompileOnDemandLayer
+    LLVM.Internal.FFI.OrcJIT.IRCompileLayer
+    LLVM.Internal.FFI.PassManager
+    LLVM.Internal.FFI.PtrHierarchy
+    LLVM.Internal.FFI.RawOStream
+    LLVM.Internal.FFI.SMDiagnostic
+    LLVM.Internal.FFI.Target
+    LLVM.Internal.FFI.Threading
+    LLVM.Internal.FFI.Transforms
+    LLVM.Internal.FFI.Type
+    LLVM.Internal.FFI.User
+    LLVM.Internal.FFI.Value
+    LLVM.Module
+    LLVM.OrcJIT
+    LLVM.OrcJIT.CompileOnDemandLayer
+    LLVM.OrcJIT.IRCompileLayer
+    LLVM.PassManager
+    LLVM.Relocation
+    LLVM.Target
+    LLVM.Target.LibraryFunction
+    LLVM.Target.Options
+    LLVM.Threading
+    LLVM.Transforms
+
+  other-modules:
+    Control.Monad.AnyCont
+    Control.Monad.AnyCont.Class
+    Control.Monad.Trans.AnyCont
+
+  include-dirs: src
+  c-sources:
+    src/LLVM/Internal/FFI/AssemblyC.cpp
+    src/LLVM/Internal/FFI/AttributeC.cpp
+    src/LLVM/Internal/FFI/BitcodeC.cpp
+    src/LLVM/Internal/FFI/BuilderC.cpp
+    src/LLVM/Internal/FFI/CallingConventionC.cpp
+    src/LLVM/Internal/FFI/ConstantC.cpp
+    src/LLVM/Internal/FFI/CommandLineC.cpp
+    src/LLVM/Internal/FFI/ExecutionEngineC.cpp
+    src/LLVM/Internal/FFI/FunctionC.cpp
+    src/LLVM/Internal/FFI/GlobalAliasC.cpp
+    src/LLVM/Internal/FFI/GlobalValueC.cpp
+    src/LLVM/Internal/FFI/InlineAssemblyC.cpp
+    src/LLVM/Internal/FFI/InstructionC.cpp
+    src/LLVM/Internal/FFI/MetadataC.cpp
+    src/LLVM/Internal/FFI/ModuleC.cpp
+    src/LLVM/Internal/FFI/OrcJITC.cpp
+    src/LLVM/Internal/FFI/RawOStreamC.cpp
+    src/LLVM/Internal/FFI/PassManagerC.cpp
+    src/LLVM/Internal/FFI/SMDiagnosticC.cpp
+    src/LLVM/Internal/FFI/TargetC.cpp
+    src/LLVM/Internal/FFI/TypeC.cpp
+    src/LLVM/Internal/FFI/ValueC.cpp
+
+  if flag(debug)
+    cc-options: -g
+
+test-suite test
+  type: exitcode-stdio-1.0
+  if flag(semigroups)
+    build-depends:
+      base >= 4.7 && < 4.9,
+      semigroups >= 0.18 && < 0.19
+  else
+    build-depends:
+      base >= 4.9 && < 5
+  build-depends:
+    bytestring >= 0.10 && < 0.11,
+    tasty >= 0.11,
+    tasty-hunit >= 0.9,
+    tasty-quickcheck >= 0.8,
+    QuickCheck >= 2.5.1.1,
+    llvm-hs,
+    llvm-hs-pure == 4.0.0.0,
+    containers >= 0.4.2.1,
+    mtl >= 2.1,
+    transformers >= 0.3.0.0,
+    transformers-compat,
+    temporary >= 1.2 && < 1.3,
+    pretty-show >= 1.6 && < 1.7
+  hs-source-dirs: test
+  extensions:
+    TupleSections
+    FlexibleInstances
+    FlexibleContexts
+  main-is: Test.hs
+  other-modules:
+    LLVM.Test.Analysis
+    LLVM.Test.CallingConvention
+    LLVM.Test.Constants
+    LLVM.Test.DataLayout
+    LLVM.Test.ExecutionEngine
+    LLVM.Test.Global
+    LLVM.Test.InlineAssembly
+    LLVM.Test.Instructions
+    LLVM.Test.Instrumentation
+    LLVM.Test.Linking
+    LLVM.Test.Metadata
+    LLVM.Test.Module
+    LLVM.Test.ObjectCode
+    LLVM.Test.OrcJIT
+    LLVM.Test.Optimization
+    LLVM.Test.Support
+    LLVM.Test.Target
+    LLVM.Test.Tests
diff --git a/src/Control/Monad/AnyCont.hs b/src/Control/Monad/AnyCont.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Monad/AnyCont.hs
@@ -0,0 +1,30 @@
+{-# LANGUAGE
+  MultiParamTypeClasses,
+  UndecidableInstances
+  #-}
+module Control.Monad.AnyCont (
+    MonadAnyCont(..),
+    ScopeAnyCont(..),
+    AnyContT(..),
+    MonadTransAnyCont(..),
+    runAnyContT,
+    withAnyContT,
+    mapAnyContT
+  ) where
+
+import Prelude
+
+import Control.Monad.Trans.AnyCont
+import Control.Monad.AnyCont.Class
+import Control.Monad.Trans.Class
+import Control.Monad.State.Class
+import Control.Monad.Error.Class
+
+instance MonadState s m => MonadState s (AnyContT m) where
+  get = lift get
+  put = lift . put
+  state = lift . state
+
+instance MonadError e m => MonadError e (AnyContT m) where
+  throwError = lift . throwError
+  x `catchError` h = anyContT $ \f -> (runAnyContT x f) `catchError` (\e -> runAnyContT (h e) f)
diff --git a/src/Control/Monad/AnyCont/Class.hs b/src/Control/Monad/AnyCont/Class.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Monad/AnyCont/Class.hs
@@ -0,0 +1,54 @@
+{-# LANGUAGE
+  RankNTypes,
+  MultiParamTypeClasses,
+  UndecidableInstances
+  #-}
+module Control.Monad.AnyCont.Class where
+
+import Prelude
+
+import Control.Monad.Trans.Class
+import Control.Monad.Trans.AnyCont (AnyContT)
+import qualified Control.Monad.Trans.AnyCont as AnyCont
+import Control.Monad.Trans.Except as Except
+import Control.Monad.Trans.State as State
+
+class ScopeAnyCont m where
+  scopeAnyCont :: m a -> m a
+
+class MonadAnyCont b m where
+  anyContToM :: (forall r . (a -> b r) -> b r) -> m a
+
+
+instance MonadTransAnyCont b m => MonadAnyCont b (AnyContT m) where
+  anyContToM c = AnyCont.anyContT (liftAnyCont c)
+
+instance Monad m => ScopeAnyCont (AnyContT m) where
+  scopeAnyCont = lift . flip AnyCont.runAnyContT return
+
+
+instance (Monad m, MonadAnyCont b m) => MonadAnyCont b (StateT s m) where
+  anyContToM x = lift $ anyContToM x
+
+instance ScopeAnyCont m => ScopeAnyCont (StateT s m) where
+  scopeAnyCont = StateT . (scopeAnyCont .) . runStateT
+
+
+instance (Monad m, MonadAnyCont b m) => MonadAnyCont b (ExceptT e m) where
+  anyContToM x = lift $ anyContToM x
+
+
+instance ScopeAnyCont m => ScopeAnyCont (ExceptT e m) where
+  scopeAnyCont = mapExceptT scopeAnyCont
+
+class MonadTransAnyCont b m where
+  liftAnyCont :: (forall r . (a -> b r) -> b r) -> (forall r . (a -> m r) -> m r)
+
+instance MonadTransAnyCont b b where
+  liftAnyCont c = c
+
+instance MonadTransAnyCont b m => MonadTransAnyCont b (StateT s m) where
+  liftAnyCont c = (\c q -> StateT $ \s -> c $ ($ s) . runStateT . q) (liftAnyCont c)
+
+instance MonadTransAnyCont b m => MonadTransAnyCont b (ExceptT e m) where
+  liftAnyCont c = (\c q -> ExceptT . c $ runExceptT . q) (liftAnyCont c)
diff --git a/src/Control/Monad/Trans/AnyCont.hs b/src/Control/Monad/Trans/AnyCont.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Monad/Trans/AnyCont.hs
@@ -0,0 +1,40 @@
+{-# LANGUAGE
+  RankNTypes
+  #-}
+module Control.Monad.Trans.AnyCont where
+
+import LLVM.Prelude
+
+import Control.Monad.Cont
+
+newtype AnyContT m a = AnyContT { unAnyContT :: forall r . ContT r m a }
+
+instance Functor (AnyContT m) where
+  fmap f p = AnyContT $ fmap f . unAnyContT $ p
+
+instance Applicative (AnyContT m) where
+  pure a = AnyContT $ pure a
+  f <*> v = AnyContT $ unAnyContT f <*> unAnyContT v
+
+instance Monad m => Monad (AnyContT m) where
+  AnyContT f >>= k = AnyContT $ f >>= unAnyContT . k
+  return a = AnyContT $ return a
+  fail s = AnyContT (ContT (\_ -> fail s))
+  
+instance MonadIO m => MonadIO (AnyContT m) where
+  liftIO = lift . liftIO
+
+instance MonadTrans AnyContT where
+  lift ma = AnyContT (lift ma)
+
+runAnyContT :: AnyContT m a -> (forall r . (a -> m r) -> m r)
+runAnyContT = runContT . unAnyContT
+anyContT :: (forall r . (a -> m r) -> m r) -> AnyContT m a
+anyContT f = AnyContT (ContT f)
+
+withAnyContT :: (forall r . (b -> m r) -> (a -> m r)) -> AnyContT m a -> AnyContT m b
+withAnyContT f m = anyContT $ runAnyContT m . f
+
+mapAnyContT :: (forall r . m r -> m r) -> AnyContT m a -> AnyContT m a
+mapAnyContT f m = anyContT $ f . runAnyContT m
+
diff --git a/src/LLVM.hs b/src/LLVM.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM.hs
@@ -0,0 +1,7 @@
+-- | An interface to use LLVM in all capacities
+module LLVM (
+  module LLVM.Module
+  ) where
+
+import LLVM.Module
+
diff --git a/src/LLVM/Analysis.hs b/src/LLVM/Analysis.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Analysis.hs
@@ -0,0 +1,8 @@
+-- | functionality for analyzing 'LLVM.Module.Module's.  Much of the analysis
+-- possible with LLVM is managed internally, as needed by 'Transforms', and so is not
+-- yet exposed here.
+module LLVM.Analysis (
+  verify
+  ) where
+
+import LLVM.Internal.Analysis
diff --git a/src/LLVM/CodeGenOpt.hs b/src/LLVM/CodeGenOpt.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/CodeGenOpt.hs
@@ -0,0 +1,12 @@
+-- | Code generation options, used in specifying TargetMachine
+module LLVM.CodeGenOpt where
+
+import LLVM.Prelude
+
+-- | <http://llvm.org/doxygen/namespacellvm_1_1CodeGenOpt.html>
+data Level
+    = None
+    | Less
+    | Default
+    | Aggressive
+    deriving (Eq, Ord, Read, Show, Typeable, Data)
diff --git a/src/LLVM/CodeModel.hs b/src/LLVM/CodeModel.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/CodeModel.hs
@@ -0,0 +1,14 @@
+-- | Relocations, used in specifying TargetMachine
+module LLVM.CodeModel where
+
+import LLVM.Prelude
+
+-- | <http://llvm.org/doxygen/namespacellvm_1_1CodeModel.html>
+data Model
+    = Default
+    | JITDefault
+    | Small
+    | Kernel
+    | Medium
+    | Large
+    deriving (Eq, Read, Show, Typeable, Data)
diff --git a/src/LLVM/CommandLine.hs b/src/LLVM/CommandLine.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/CommandLine.hs
@@ -0,0 +1,8 @@
+-- | Tools for processing command line arguments, for command line tools build
+-- with llvm (or for other uses forced into pretending to be such to get at (ack)
+-- global state).
+module LLVM.CommandLine (
+  parseCommandLineOptions
+) where
+
+import LLVM.Internal.CommandLine
diff --git a/src/LLVM/Context.hs b/src/LLVM/Context.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Context.hs
@@ -0,0 +1,7 @@
+-- | functions for the LLVM Context object which holds thread-scope state
+module LLVM.Context (
+  Context,
+  withContext
+  ) where
+
+import LLVM.Internal.Context
diff --git a/src/LLVM/Diagnostic.hs b/src/LLVM/Diagnostic.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Diagnostic.hs
@@ -0,0 +1,33 @@
+-- | Diagnostics describe parse errors
+module LLVM.Diagnostic (
+  DiagnosticKind(..),
+  Diagnostic(..),
+  diagnosticDisplay
+ ) where
+
+import LLVM.Prelude
+
+-- | What kind of problem does a diagnostic describe?
+data DiagnosticKind 
+  = ErrorKind
+  | WarningKind
+  | NoteKind
+  deriving (Eq, Ord, Read, Show, Typeable, Data)
+
+-- | A 'Diagnostic' described a problem during parsing of LLVM IR
+data Diagnostic = Diagnostic {
+    lineNumber :: Int,
+    columnNumber :: Int,
+    diagnosticKind :: DiagnosticKind,
+    filename :: String,
+    message :: String,
+    lineContents :: String
+  }
+  deriving (Eq, Ord, Read, Show)
+
+-- | Convert a 'Diagnostic' to a printable form.
+diagnosticDisplay :: Diagnostic -> String
+diagnosticDisplay d = 
+  (filename d) ++ ":" ++ show (lineNumber d) ++ ":" ++ show (columnNumber d)
+  ++ ":\n" ++ show (diagnosticKind d) ++ ": " ++ (message d) ++ "\n"
+  ++ (lineContents d) ++ "\n"
diff --git a/src/LLVM/ExecutionEngine.hs b/src/LLVM/ExecutionEngine.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/ExecutionEngine.hs
@@ -0,0 +1,8 @@
+-- | Tools for JIT execution
+module LLVM.ExecutionEngine (
+  ExecutionEngine(..),
+  ExecutableModule,
+  MCJIT, withMCJIT
+  ) where
+
+import LLVM.Internal.ExecutionEngine
diff --git a/src/LLVM/Internal/Analysis.hs b/src/LLVM/Internal/Analysis.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/Analysis.hs
@@ -0,0 +1,24 @@
+module LLVM.Internal.Analysis where
+
+import LLVM.Prelude
+
+import Control.Monad.AnyCont
+import Control.Monad.Error.Class
+import Control.Monad.IO.Class
+import Control.Monad.Trans.Except
+
+import qualified LLVM.Internal.FFI.Analysis as FFI
+import qualified LLVM.Internal.FFI.LLVMCTypes as FFI
+
+import LLVM.Internal.Module
+import LLVM.Internal.Coding
+
+-- | Run basic sanity checks on a 'Module'. Note that the same checks will trigger assertions
+-- within LLVM if LLVM was built with them turned on, before this function can be is called.
+verify :: Module -> ExceptT String IO ()
+verify m = flip runAnyContT return $ do
+  errorPtr <- alloca
+  m' <- readModule m
+  result <- decodeM =<< (liftIO $ FFI.verifyModule m' FFI.verifierFailureActionReturnStatus errorPtr)
+  when result $ throwError =<< decodeM errorPtr
+
diff --git a/src/LLVM/Internal/Atomicity.hs b/src/LLVM/Internal/Atomicity.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/Atomicity.hs
@@ -0,0 +1,49 @@
+{-# LANGUAGE
+  TemplateHaskell,
+  MultiParamTypeClasses
+  #-}
+module LLVM.Internal.Atomicity where
+
+import LLVM.Prelude
+
+import Data.Maybe
+
+import qualified LLVM.Internal.FFI.LLVMCTypes as FFI
+
+import LLVM.Internal.Coding
+import qualified LLVM.AST as A
+
+genCodingInstance [t| Maybe A.MemoryOrdering |] ''FFI.MemoryOrdering [
+  (FFI.memoryOrderingNotAtomic, Nothing),
+  (FFI.memoryOrderingUnordered, Just A.Unordered),
+  (FFI.memoryOrderingMonotonic, Just A.Monotonic),
+  (FFI.memoryOrderingAcquire, Just A.Acquire),
+  (FFI.memoryOrderingRelease, Just A.Release),
+  (FFI.memoryOrderingAcquireRelease, Just A.AcquireRelease),
+  (FFI.memoryOrderingSequentiallyConsistent, Just A.SequentiallyConsistent)
+ ]
+
+genCodingInstance [t| A.SynchronizationScope |] ''FFI.SynchronizationScope [
+  (FFI.synchronizationScopeSingleThread, A.SingleThread),
+  (FFI.synchronizationScopeCrossThread, A.CrossThread)
+ ]
+
+instance Monad m => EncodeM m (Maybe A.Atomicity) (FFI.SynchronizationScope, FFI.MemoryOrdering) where
+  encodeM a =
+    return (,) `ap` encodeM (maybe A.SingleThread fst a) `ap` encodeM (liftM snd a)
+
+instance Monad m => DecodeM m (Maybe A.Atomicity) (FFI.SynchronizationScope, FFI.MemoryOrdering) where
+  decodeM (ss, ao) = return (liftM . (,)) `ap` decodeM ss `ap` decodeM ao
+
+instance Monad m => EncodeM m A.Atomicity (FFI.SynchronizationScope, FFI.MemoryOrdering) where
+  encodeM = encodeM . Just
+
+instance Monad m => DecodeM m A.Atomicity (FFI.SynchronizationScope, FFI.MemoryOrdering) where
+  decodeM = liftM fromJust . decodeM
+
+instance Monad m => EncodeM m A.MemoryOrdering FFI.MemoryOrdering where
+  encodeM = encodeM . Just
+
+instance Monad m => DecodeM m A.MemoryOrdering FFI.MemoryOrdering where
+  decodeM = liftM fromJust . decodeM
+
diff --git a/src/LLVM/Internal/Attribute.hs b/src/LLVM/Internal/Attribute.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/Attribute.hs
@@ -0,0 +1,279 @@
+{-# LANGUAGE
+  MultiParamTypeClasses,
+  ConstraintKinds,
+  QuasiQuotes,
+  UndecidableInstances,
+  RankNTypes
+  #-}
+module LLVM.Internal.Attribute where
+
+import LLVM.Prelude
+
+import Control.Monad.AnyCont
+import Control.Monad.IO.Class
+import Control.Monad.State (gets)
+
+import Foreign.C (CUInt)
+import Foreign.Ptr
+import Data.Either
+import Data.Map (Map)
+import qualified Data.Map as Map
+import Data.Maybe
+
+import qualified LLVM.Internal.FFI.Attribute as FFI
+import qualified LLVM.Internal.FFI.LLVMCTypes as FFI
+import LLVM.Internal.FFI.LLVMCTypes (parameterAttributeKindP, functionAttributeKindP)  
+
+import qualified LLVM.AST.ParameterAttribute as A.PA  
+import qualified LLVM.AST.FunctionAttribute as A.FA  
+
+import LLVM.Internal.Coding
+import LLVM.Internal.Context  
+import LLVM.Internal.EncodeAST
+import LLVM.Internal.DecodeAST
+
+inconsistentCases :: Show a => String -> a -> b
+inconsistentCases name attr =
+  error $ "llvm-hs internal error: cases inconstistent in " ++ name ++ " encoding for " ++ show attr
+
+instance Monad m => EncodeM m A.PA.ParameterAttribute (Ptr FFI.ParameterAttrBuilder -> EncodeAST ()) where
+  encodeM a = return $ \b -> liftIO $ case a of
+    A.PA.Alignment v -> FFI.attrBuilderAddAlignment b v
+    A.PA.Dereferenceable v -> FFI.attrBuilderAddDereferenceable b v
+    A.PA.DereferenceableOrNull v -> FFI.attrBuilderAddDereferenceableOrNull b v
+    _ -> FFI.attrBuilderAddParameterAttributeKind b $ case a of
+      A.PA.ZeroExt -> FFI.parameterAttributeKindZExt
+      A.PA.SignExt -> FFI.parameterAttributeKindSExt
+      A.PA.InReg -> FFI.parameterAttributeKindInReg
+      A.PA.SRet -> FFI.parameterAttributeKindStructRet
+      A.PA.NoAlias -> FFI.parameterAttributeKindNoAlias
+      A.PA.ByVal -> FFI.parameterAttributeKindByVal
+      A.PA.NoCapture -> FFI.parameterAttributeKindNoCapture
+      A.PA.Nest -> FFI.parameterAttributeKindNest
+      A.PA.ReadOnly -> FFI.parameterAttributeKindReadOnly
+      A.PA.ReadNone -> FFI.parameterAttributeKindReadNone
+      A.PA.InAlloca -> FFI.parameterAttributeKindInAlloca
+      A.PA.NonNull -> FFI.parameterAttributeKindNonNull
+      A.PA.Returned -> FFI.parameterAttributeKindReturned
+      A.PA.SwiftSelf -> FFI.parameterAttributeKindSwiftSelf
+      A.PA.SwiftError -> FFI.parameterAttributeKindSwiftError
+      A.PA.WriteOnly -> FFI.parameterAttributeKindWriteOnly
+      A.PA.Alignment _ -> inconsistentCases "ParameterAttribute" a
+      A.PA.Dereferenceable _ -> inconsistentCases "ParameterAttribute" a
+      A.PA.DereferenceableOrNull _ -> inconsistentCases "ParameterAttribute" a
+
+instance Monad m => EncodeM m A.FA.FunctionAttribute (Ptr FFI.FunctionAttrBuilder -> EncodeAST ()) where
+  encodeM (A.FA.StringAttribute kind value) = return $ \b -> do
+    (kindP, kindLen) <- encodeM kind
+    (valueP, valueLen) <- encodeM value
+    liftIO $ FFI.attrBuilderAddStringAttribute b kindP kindLen valueP valueLen
+  encodeM a = return $ \b -> case a of
+    A.FA.StackAlignment v -> liftIO $ FFI.attrBuilderAddStackAlignment b v
+    A.FA.AllocSize x y -> do
+      x' <- encodeM x
+      y' <- encodeM y
+      liftIO $ FFI.attrBuilderAddAllocSize b x' y'
+    _ -> liftIO $ FFI.attrBuilderAddFunctionAttributeKind b $ case a of
+      A.FA.Convergent -> FFI.functionAttributeKindConvergent
+      A.FA.InaccessibleMemOnly -> FFI.functionAttributeKindInaccessibleMemOnly
+      A.FA.InaccessibleMemOrArgMemOnly -> FFI.functionAttributeKindInaccessibleMemOrArgMemOnly
+      A.FA.NoReturn -> FFI.functionAttributeKindNoReturn
+      A.FA.NoUnwind -> FFI.functionAttributeKindNoUnwind
+      A.FA.ReadNone -> FFI.functionAttributeKindReadNone
+      A.FA.ReadOnly -> FFI.functionAttributeKindReadOnly
+      A.FA.NoInline -> FFI.functionAttributeKindNoInline
+      A.FA.NoRecurse -> FFI.functionAttributeKindNoRecurse
+      A.FA.AlwaysInline -> FFI.functionAttributeKindAlwaysInline
+      A.FA.MinimizeSize -> FFI.functionAttributeKindMinSize
+      A.FA.OptimizeForSize -> FFI.functionAttributeKindOptimizeForSize
+      A.FA.OptimizeNone -> FFI.functionAttributeKindOptimizeForSize
+      A.FA.WriteOnly -> FFI.functionAttributeKindWriteOnly
+      A.FA.ArgMemOnly -> FFI.functionAttributeKindArgMemOnly
+      A.FA.StackProtect -> FFI.functionAttributeKindStackProtect
+      A.FA.StackProtectReq -> FFI.functionAttributeKindStackProtectReq
+      A.FA.StackProtectStrong -> FFI.functionAttributeKindStackProtectStrong
+      A.FA.NoRedZone -> FFI.functionAttributeKindNoRedZone
+      A.FA.NoImplicitFloat -> FFI.functionAttributeKindNoImplicitFloat
+      A.FA.Naked -> FFI.functionAttributeKindNaked
+      A.FA.InlineHint -> FFI.functionAttributeKindInlineHint
+      A.FA.ReturnsTwice -> FFI.functionAttributeKindReturnsTwice
+      A.FA.UWTable -> FFI.functionAttributeKindUWTable
+      A.FA.NonLazyBind -> FFI.functionAttributeKindNonLazyBind
+      A.FA.Builtin -> FFI.functionAttributeKindBuiltin
+      A.FA.NoBuiltin -> FFI.functionAttributeKindNoBuiltin
+      A.FA.Cold -> FFI.functionAttributeKindCold
+      A.FA.JumpTable -> FFI.functionAttributeKindJumpTable
+      A.FA.NoDuplicate -> FFI.functionAttributeKindNoDuplicate
+      A.FA.SanitizeAddress -> FFI.functionAttributeKindSanitizeAddress
+      A.FA.SanitizeThread -> FFI.functionAttributeKindSanitizeThread
+      A.FA.SanitizeMemory -> FFI.functionAttributeKindSanitizeMemory
+      A.FA.SafeStack -> FFI.functionAttributeKindSafeStack
+      A.FA.StackAlignment _ -> inconsistentCases "FunctionAttribute" a
+      A.FA.AllocSize _ _ -> inconsistentCases "FunctionAttribute" a
+      A.FA.StringAttribute _ _ -> inconsistentCases "FunctionAttribute" a
+
+instance DecodeM DecodeAST A.PA.ParameterAttribute FFI.ParameterAttribute where
+  decodeM a = do
+    enum <- liftIO $ FFI.parameterAttributeKindAsEnum a
+    case enum of
+      [parameterAttributeKindP|ZExt|] -> return A.PA.ZeroExt
+      [parameterAttributeKindP|SExt|] -> return A.PA.SignExt
+      [parameterAttributeKindP|InReg|] -> return A.PA.InReg
+      [parameterAttributeKindP|StructRet|] -> return A.PA.SRet
+      [parameterAttributeKindP|Alignment|] -> return A.PA.Alignment `ap` (liftIO $ FFI.attributeValueAsInt a)
+      [parameterAttributeKindP|NoAlias|] -> return A.PA.NoAlias
+      [parameterAttributeKindP|ByVal|] -> return A.PA.ByVal
+      [parameterAttributeKindP|NoCapture|] -> return A.PA.NoCapture
+      [parameterAttributeKindP|Nest|] -> return A.PA.Nest
+      [parameterAttributeKindP|ReadOnly|] -> return A.PA.ReadOnly
+      [parameterAttributeKindP|ReadNone|] -> return A.PA.ReadNone
+      [parameterAttributeKindP|WriteOnly|] -> return A.PA.WriteOnly
+      [parameterAttributeKindP|InAlloca|] -> return A.PA.InAlloca
+      [parameterAttributeKindP|NonNull|] -> return A.PA.NonNull
+      [parameterAttributeKindP|Dereferenceable|] -> return A.PA.Dereferenceable `ap` (liftIO $ FFI.attributeValueAsInt a)
+      [parameterAttributeKindP|DereferenceableOrNull|] -> return A.PA.DereferenceableOrNull `ap` (liftIO $ FFI.attributeValueAsInt a)
+      [parameterAttributeKindP|Returned|] -> return A.PA.Returned
+      [parameterAttributeKindP|SwiftSelf|] -> return A.PA.SwiftSelf
+      [parameterAttributeKindP|SwiftError|] -> return A.PA.SwiftError
+      _ -> error $ "unhandled parameter attribute enum value: " ++ show enum
+
+instance DecodeM DecodeAST A.FA.FunctionAttribute FFI.FunctionAttribute where
+  decodeM a = do
+    isString <- decodeM =<< (liftIO $ FFI.isStringAttribute a)
+    if isString
+       then
+         return A.FA.StringAttribute
+                  `ap` (decodeM $ FFI.attributeKindAsString a)
+                  `ap` (decodeM $ FFI.attributeValueAsString a)                   
+       else do
+         enum <- liftIO $ FFI.functionAttributeKindAsEnum a
+         case enum of
+           [functionAttributeKindP|AllocSize|] -> do
+             x <- alloca
+             y <- alloca
+             isJust <- liftIO $ FFI.attributeGetAllocSizeArgs a x y
+             x' <- decodeM =<< peek x
+             y' <- peek y
+             yM <- decodeM (y', isJust)
+             return (A.FA.AllocSize x' yM)
+           [functionAttributeKindP|NoReturn|] -> return A.FA.NoReturn
+           [functionAttributeKindP|NoUnwind|] -> return A.FA.NoUnwind
+           [functionAttributeKindP|ReadNone|] -> return A.FA.ReadNone
+           [functionAttributeKindP|ReadOnly|] -> return A.FA.ReadOnly
+           [functionAttributeKindP|NoInline|] -> return A.FA.NoInline
+           [functionAttributeKindP|NoRecurse|] -> return A.FA.NoRecurse
+           [functionAttributeKindP|AlwaysInline|] -> return A.FA.AlwaysInline
+           [functionAttributeKindP|MinSize|] -> return A.FA.MinimizeSize
+           [functionAttributeKindP|OptimizeForSize|] -> return A.FA.OptimizeForSize
+           [functionAttributeKindP|OptimizeNone|] -> return A.FA.OptimizeForSize
+           [functionAttributeKindP|StackProtect|] -> return A.FA.StackProtect
+           [functionAttributeKindP|StackProtectReq|] -> return A.FA.StackProtectReq
+           [functionAttributeKindP|StackProtectStrong|] -> return A.FA.StackProtectStrong
+           [functionAttributeKindP|NoRedZone|] -> return A.FA.NoRedZone
+           [functionAttributeKindP|NoImplicitFloat|] -> return A.FA.NoImplicitFloat
+           [functionAttributeKindP|Naked|] -> return A.FA.Naked
+           [functionAttributeKindP|InlineHint|] -> return A.FA.InlineHint
+           [functionAttributeKindP|StackAlignment|] -> return A.FA.StackAlignment `ap` (liftIO $ FFI.attributeValueAsInt a)
+           [functionAttributeKindP|ReturnsTwice|] -> return A.FA.ReturnsTwice
+           [functionAttributeKindP|UWTable|] -> return A.FA.UWTable
+           [functionAttributeKindP|NonLazyBind|] -> return A.FA.NonLazyBind
+           [functionAttributeKindP|Builtin|] -> return A.FA.Builtin
+           [functionAttributeKindP|NoBuiltin|] -> return A.FA.NoBuiltin
+           [functionAttributeKindP|Cold|] -> return A.FA.Cold
+           [functionAttributeKindP|JumpTable|] -> return A.FA.JumpTable
+           [functionAttributeKindP|NoDuplicate|] -> return A.FA.NoDuplicate
+           [functionAttributeKindP|SanitizeAddress|] -> return A.FA.SanitizeAddress
+           [functionAttributeKindP|SanitizeThread|] -> return A.FA.SanitizeThread
+           [functionAttributeKindP|SanitizeMemory|] -> return A.FA.SanitizeMemory
+           [functionAttributeKindP|ArgMemOnly|] -> return A.FA.ArgMemOnly
+           [functionAttributeKindP|Convergent|] -> return A.FA.Convergent
+           [functionAttributeKindP|InaccessibleMemOnly|] -> return A.FA.InaccessibleMemOnly
+           [functionAttributeKindP|InaccessibleMemOrArgMemOnly|] -> return A.FA.InaccessibleMemOrArgMemOnly
+           [functionAttributeKindP|SafeStack|] -> return A.FA.SafeStack
+           [functionAttributeKindP|WriteOnly|] -> return A.FA.WriteOnly
+           _ -> error $ "unhandled function attribute enum value: " ++ show enum
+
+allocaAttrBuilder :: (Monad m, MonadAnyCont IO m) => m (Ptr (FFI.AttrBuilder a))
+allocaAttrBuilder = do
+  p <- allocaArray FFI.getAttrBuilderSize
+  anyContToM $ \f -> do
+    ab <- FFI.constructAttrBuilder p
+    r <- f ab
+    FFI.destroyAttrBuilder ab
+    return r
+
+instance EncodeM EncodeAST a (Ptr (FFI.AttrBuilder b) -> EncodeAST ()) => EncodeM EncodeAST (FFI.Index, [a]) (FFI.AttributeSet b) where
+  encodeM (index, as) = scopeAnyCont $ do
+    ab <- allocaAttrBuilder
+    builds <- mapM encodeM as
+    forM builds ($ ab) :: EncodeAST [()]
+    Context context <- gets encodeStateContext
+    liftIO $ FFI.getAttributeSet context index ab
+
+instance EncodeM EncodeAST [A.FA.FunctionAttribute] FFI.FunctionAttributeSet where
+  encodeM fas = encodeM (FFI.functionIndex, fas)
+
+instance DecodeM DecodeAST a (FFI.Attribute b) => DecodeM DecodeAST [a] (FFI.AttributeSet b) where
+  decodeM as = do
+    np <- alloca
+    as <- liftIO $ FFI.attributeSetGetAttributes as 0 np
+    n <- peek np
+    decodeM (n, as)
+            
+data MixedAttributeSet = MixedAttributeSet {
+    functionAttributes :: [Either A.FA.GroupID A.FA.FunctionAttribute],
+    returnAttributes :: [A.PA.ParameterAttribute],
+    parameterAttributes :: Map CUInt [A.PA.ParameterAttribute]
+  }
+  deriving (Eq, Show)
+
+data PreSlot
+  = IndirectFunctionAttributes A.FA.GroupID
+  | DirectFunctionAttributes [A.FA.FunctionAttribute]
+  | ReturnAttributes [A.PA.ParameterAttribute]
+  | ParameterAttributes CUInt [A.PA.ParameterAttribute]    
+
+instance EncodeM EncodeAST PreSlot FFI.MixedAttributeSet where
+  encodeM preSlot = do
+    let forget = liftM FFI.forgetAttributeType
+    case preSlot of
+      IndirectFunctionAttributes gid -> forget (referAttributeGroup gid)
+      DirectFunctionAttributes fas -> forget (encodeM fas :: EncodeAST FFI.FunctionAttributeSet)
+      ReturnAttributes as -> forget (encodeM (FFI.returnIndex, as) :: EncodeAST FFI.ParameterAttributeSet)
+      ParameterAttributes i as -> forget (encodeM (fromIntegral (i + 1) :: FFI.Index, as) :: EncodeAST FFI.ParameterAttributeSet)
+
+instance EncodeM EncodeAST MixedAttributeSet FFI.MixedAttributeSet where
+  encodeM (MixedAttributeSet fAttrs rAttrs pAttrs) = do
+    let directP = DirectFunctionAttributes (rights fAttrs)
+        indirectPs = map IndirectFunctionAttributes (lefts fAttrs)
+        returnP = ReturnAttributes rAttrs
+        paramPs = [ ParameterAttributes x as | (x, as) <- Map.toList pAttrs ]
+    (nAttrs, attrs) <- encodeM ([directP, returnP] ++ indirectPs ++ paramPs)
+    Context context <- gets encodeStateContext
+    liftIO $ FFI.mixAttributeSets context attrs nAttrs
+
+instance DecodeM DecodeAST MixedAttributeSet FFI.MixedAttributeSet where
+  decodeM mas = do
+    numSlots <- if mas == nullPtr then return 0 else liftIO $ FFI.attributeSetNumSlots mas
+    slotIndexes <- forM (take (fromIntegral numSlots) [0..]) $ \s -> do
+      i <- liftIO $ FFI.attributeSetSlotIndex mas s
+      return (i, s)
+    let separate :: Ord k => k -> Map k a -> (Maybe a, Map k a)
+        separate = Map.updateLookupWithKey (\_ _ -> Nothing)
+        indexedSlots = Map.fromList slotIndexes
+    unless (Map.size indexedSlots == length slotIndexes) $
+           fail "unexpected slot index collision decoding mixed AttributeSet"
+    let (functionSlot, otherSlots) = separate FFI.functionIndex (Map.fromList slotIndexes)
+    functionAnnotation <- for (maybeToList functionSlot) $ \slot -> do
+      a <- liftIO $ FFI.attributeSetSlotAttributes mas slot
+      getAttributeGroupID a
+    otherAttributeSets <- for otherSlots $ \slot -> do
+      a <- liftIO $ FFI.attributeSetSlotAttributes mas slot
+      decodeM (a :: FFI.ParameterAttributeSet)
+    let (returnAttributeSet, shiftedParameterAttributeSets) = separate FFI.returnIndex otherAttributeSets
+    return $ MixedAttributeSet {
+                  functionAttributes = fmap Left functionAnnotation,
+                  returnAttributes = join . maybeToList $ returnAttributeSet,
+                  parameterAttributes = Map.mapKeysMonotonic (\x -> fromIntegral x - 1) shiftedParameterAttributeSets
+                }
+
diff --git a/src/LLVM/Internal/BasicBlock.hs b/src/LLVM/Internal/BasicBlock.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/BasicBlock.hs
@@ -0,0 +1,27 @@
+module LLVM.Internal.BasicBlock where
+
+import LLVM.Prelude
+
+import Control.Monad.Trans
+import Foreign.Ptr
+
+import qualified LLVM.Internal.FFI.PtrHierarchy as FFI
+import qualified LLVM.Internal.FFI.BasicBlock as FFI
+import qualified LLVM.Internal.FFI.Iterate as FFI
+
+import LLVM.Internal.DecodeAST
+import LLVM.Internal.Coding
+import LLVM.Internal.Instruction ()
+
+import qualified LLVM.AST.Instruction as A
+
+getBasicBlockTerminator :: Ptr FFI.BasicBlock -> DecodeAST (DecodeAST (A.Named A.Terminator))
+getBasicBlockTerminator = decodeM <=< (liftIO . FFI.getBasicBlockTerminator)
+
+getNamedInstructions :: Ptr FFI.BasicBlock -> DecodeAST (DecodeAST [A.Named A.Instruction])
+getNamedInstructions b = do
+  ffiInstructions <- liftIO $ FFI.getXs (FFI.getFirstInstruction b) FFI.getNextInstruction
+  let n = length ffiInstructions
+  liftM sequence . forM (take (n-1) ffiInstructions) $ decodeM
+
+  
diff --git a/src/LLVM/Internal/CallingConvention.hs b/src/LLVM/Internal/CallingConvention.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/CallingConvention.hs
@@ -0,0 +1,71 @@
+{-# LANGUAGE
+  MultiParamTypeClasses,
+  TemplateHaskell,
+  QuasiQuotes
+  #-}
+module LLVM.Internal.CallingConvention where
+
+import LLVM.Prelude
+
+import LLVM.Internal.Coding
+import Foreign.C.Types (CUInt(..))
+
+import qualified LLVM.Internal.FFI.LLVMCTypes as FFI
+import LLVM.Internal.FFI.LLVMCTypes (callingConventionP)
+
+import qualified LLVM.AST.CallingConvention as A.CC
+
+instance Monad m => EncodeM m A.CC.CallingConvention FFI.CallingConvention where
+  encodeM cc = return $ 
+        case cc of
+          A.CC.C -> FFI.callingConventionC
+          A.CC.Fast -> FFI.callingConventionFast
+          A.CC.Cold -> FFI.callingConventionCold
+          A.CC.GHC ->  FFI.callingConventionGHC
+          A.CC.HiPE -> FFI.callingConventionHiPE
+          A.CC.WebKit_JS -> FFI.callingConventionWebKit_JS
+          A.CC.AnyReg -> FFI.callingConventionAnyReg
+          A.CC.PreserveMost -> FFI.callingConventionPreserveMost
+          A.CC.PreserveAll -> FFI.callingConventionPreserveAll
+          A.CC.X86_StdCall -> FFI.callingConventionX86_StdCall
+          A.CC.X86_FastCall -> FFI.callingConventionX86_FastCall
+          A.CC.ARM_APCS -> FFI.callingConventionARM_APCS
+          A.CC.ARM_AAPCS -> FFI.callingConventionARM_AAPCS
+          A.CC.ARM_AAPCS_VFP -> FFI.callingConventionARM_AAPCS_VFP
+          A.CC.MSP430_INTR -> FFI.callingConventionMSP430_INTR
+          A.CC.X86_ThisCall -> FFI.callingConventionX86_ThisCall
+          A.CC.PTX_Kernel -> FFI.callingConventionPTX_Kernel
+          A.CC.PTX_Device -> FFI.callingConventionPTX_Device
+          A.CC.SPIR_FUNC -> FFI.callingConventionSPIR_FUNC
+          A.CC.SPIR_KERNEL -> FFI.callingConventionSPIR_KERNEL
+          A.CC.Intel_OCL_BI -> FFI.callingConventionIntel_OCL_BI
+          A.CC.X86_64_SysV -> FFI.callingConventionX86_64_SysV
+          A.CC.X86_64_Win64 -> FFI.callingConventionX86_64_Win64
+          A.CC.Numbered cc' -> FFI.CallingConvention (fromIntegral cc')
+
+instance Monad m => DecodeM m A.CC.CallingConvention FFI.CallingConvention where
+  decodeM cc = return $ case cc of
+    [callingConventionP|C|] -> A.CC.C
+    [callingConventionP|Fast|] -> A.CC.Fast
+    [callingConventionP|Cold|] -> A.CC.Cold
+    [callingConventionP|GHC|] -> A.CC.GHC
+    [callingConventionP|HiPE|] -> A.CC.HiPE
+    [callingConventionP|WebKit_JS|] -> A.CC.WebKit_JS
+    [callingConventionP|AnyReg|] -> A.CC.AnyReg
+    [callingConventionP|PreserveMost|] -> A.CC.PreserveMost
+    [callingConventionP|PreserveAll|] -> A.CC.PreserveAll
+    [callingConventionP|X86_StdCall|] -> A.CC.X86_StdCall
+    [callingConventionP|X86_FastCall|] -> A.CC.X86_FastCall
+    [callingConventionP|ARM_APCS|] -> A.CC.ARM_APCS
+    [callingConventionP|ARM_AAPCS|] -> A.CC.ARM_AAPCS
+    [callingConventionP|ARM_AAPCS_VFP|] -> A.CC.ARM_AAPCS_VFP
+    [callingConventionP|MSP430_INTR|] -> A.CC.MSP430_INTR
+    [callingConventionP|X86_ThisCall|] -> A.CC.X86_ThisCall
+    [callingConventionP|PTX_Kernel|] -> A.CC.PTX_Kernel
+    [callingConventionP|PTX_Device|] -> A.CC.PTX_Device
+    [callingConventionP|SPIR_FUNC|] -> A.CC.SPIR_FUNC
+    [callingConventionP|SPIR_KERNEL|] -> A.CC.SPIR_KERNEL
+    [callingConventionP|Intel_OCL_BI|] -> A.CC.Intel_OCL_BI
+    [callingConventionP|X86_64_SysV|] -> A.CC.X86_64_SysV
+    [callingConventionP|X86_64_Win64|] -> A.CC.X86_64_Win64
+    FFI.CallingConvention (CUInt ci) | ci >= 64 -> A.CC.Numbered (fromIntegral ci)
diff --git a/src/LLVM/Internal/Coding.hs b/src/LLVM/Internal/Coding.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/Coding.hs
@@ -0,0 +1,140 @@
+{-# LANGUAGE
+  TemplateHaskell,
+  MultiParamTypeClasses,
+  FunctionalDependencies,
+  UndecidableInstances
+  #-}
+module LLVM.Internal.Coding where
+
+import LLVM.Prelude
+
+import Language.Haskell.TH
+import Language.Haskell.TH.Quote
+
+import Control.Monad.AnyCont
+import Control.Monad.IO.Class
+
+import Foreign.C
+import Foreign.Ptr
+import Foreign.Storable
+import Foreign.Marshal.Alloc
+import Foreign.Marshal.Array
+
+import qualified LLVM.Internal.FFI.LLVMCTypes as FFI
+
+class EncodeM e h c where
+  encodeM :: h -> e c
+
+class DecodeM d h c where
+  decodeM :: c -> d h
+
+genCodingInstance :: (Data c, Data h) => TypeQ -> Name -> [(c, h)] -> Q [Dec]
+genCodingInstance ht ctn chs = do
+  let n = const Nothing
+  [d| 
+    instance Monad m => EncodeM m $(ht) $(conT ctn) where
+      encodeM h = return $ $(
+        caseE [| h |] [ match (dataToPatQ n h) (normalB (dataToExpQ n c)) [] | (c,h) <- chs ] 
+       )
+
+    instance Monad m => DecodeM m $(ht) $(conT ctn) where
+      decodeM c = return $ $(
+        caseE [| c |] [ match (dataToPatQ n c) (normalB (dataToExpQ n h)) [] | (c,h) <- chs ]
+       )
+   |]
+
+allocaArray :: (Integral i, Storable a, MonadAnyCont IO m) => i -> m (Ptr a)
+allocaArray p = anyContToM $ Foreign.Marshal.Array.allocaArray (fromIntegral p)
+
+alloca :: (Storable a, MonadAnyCont IO m) => m (Ptr a)
+alloca = anyContToM Foreign.Marshal.Alloc.alloca
+
+peek :: (Storable a, MonadIO m) => Ptr a -> m a
+peek p = liftIO $ Foreign.Storable.peek p
+
+peekByteOff :: (Storable a, MonadIO m) => Ptr a -> Int -> m a
+peekByteOff p i = liftIO $ Foreign.Storable.peekByteOff p i
+
+poke :: (Storable a, MonadIO m) => Ptr a -> a -> m ()
+poke p a = liftIO $ Foreign.Storable.poke p a
+
+pokeByteOff :: (Storable a, MonadIO m) => Ptr a -> Int -> a -> m ()
+pokeByteOff p i a = liftIO $ Foreign.Storable.pokeByteOff p i a
+
+peekArray :: (Integral i, Storable a, MonadIO m) => i -> Ptr a -> m [a]
+peekArray n p = liftIO $ Foreign.Marshal.Array.peekArray (fromIntegral n) p
+
+instance (Monad m, EncodeM m h c, Storable c, MonadAnyCont IO m) => EncodeM m [h] (CUInt, Ptr c) where
+  encodeM hs = do
+    hs <- mapM encodeM hs
+    (anyContToM $ \x -> Foreign.Marshal.Array.withArrayLen hs $ \n hs -> x (fromIntegral n, hs))
+
+instance (Monad m, DecodeM m h c, Storable c, MonadIO m) => DecodeM m [h] (CUInt, Ptr c) where
+  decodeM (n, ca) = do
+    cs <- liftIO $ Foreign.Marshal.Array.peekArray (fromIntegral n) ca
+    mapM decodeM cs
+
+instance Monad m => EncodeM m Bool FFI.LLVMBool where
+  encodeM False = return $ FFI.LLVMBool 0
+  encodeM True = return $ FFI.LLVMBool 1
+
+instance Monad m => DecodeM m Bool FFI.LLVMBool where
+  decodeM (FFI.LLVMBool 0) = return $ False
+  decodeM (FFI.LLVMBool _) = return $ True
+
+instance (Monad m, EncodeM m h (Ptr c)) => EncodeM m (Maybe h) (Ptr c) where
+  encodeM = maybe (return nullPtr) encodeM
+
+instance (Monad m, DecodeM m h (Ptr c)) => DecodeM m (Maybe h) (Ptr c) where
+  decodeM p | p == nullPtr = return Nothing
+            | otherwise = liftM Just $ decodeM p
+
+instance Monad m => EncodeM m (Maybe Bool) (FFI.NothingAsMinusOne Bool) where
+  encodeM = return . FFI.NothingAsMinusOne . maybe (-1) (fromIntegral . fromEnum)
+
+instance Monad m => EncodeM m (Maybe Word) (FFI.NothingAsMinusOne Word) where
+  encodeM = return . FFI.NothingAsMinusOne . maybe (-1) fromIntegral
+
+instance Monad m => EncodeM m (Maybe Word) (CUInt, FFI.LLVMBool) where
+  encodeM (Just a) = liftM2 (,) (encodeM a) (encodeM True)
+  encodeM Nothing = return (0,) `ap` (encodeM False)
+
+instance Monad m => DecodeM m (Maybe Word) (CUInt, FFI.LLVMBool) where
+  decodeM (a, isJust) = do
+    isJust' <- decodeM isJust
+    if isJust'
+       then liftM Just (decodeM a)
+       else return Nothing
+
+instance Monad m => EncodeM m Word CUInt where
+  encodeM = return . fromIntegral
+
+instance Monad m => EncodeM m Word32 CUInt where
+  encodeM = return . fromIntegral
+
+instance Monad m => EncodeM m Word64 CULong where
+  encodeM = return . fromIntegral
+
+instance Monad m => DecodeM m Word CUInt where
+  decodeM = return . fromIntegral
+
+instance Monad m => DecodeM m Word32 CUInt where
+  decodeM = return . fromIntegral
+
+instance Monad m => DecodeM m Word64 CULong where
+  decodeM = return . fromIntegral
+
+instance Monad m => EncodeM m Int32 CInt where
+  encodeM = return . fromIntegral
+
+instance Monad m => DecodeM m Int32 CInt where
+  decodeM = return . fromIntegral
+
+instance Monad m => DecodeM m Int CInt where
+  decodeM = return . fromIntegral
+
+instance Monad m => EncodeM m Word64 Word64 where
+  encodeM = return
+
+instance Monad m => DecodeM m Word64 Word64 where
+  decodeM = return
diff --git a/src/LLVM/Internal/CommandLine.hs b/src/LLVM/Internal/CommandLine.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/CommandLine.hs
@@ -0,0 +1,23 @@
+module LLVM.Internal.CommandLine where
+
+import LLVM.Prelude
+
+import Control.Monad.AnyCont
+import Control.Monad.IO.Class
+
+import Foreign.Ptr
+
+import qualified LLVM.Internal.FFI.CommandLine as FFI
+
+import LLVM.Internal.Coding
+import LLVM.Internal.String ()
+
+-- | <http://llvm.org/doxygen/namespacellvm_1_1cl.html#a992a39dae9eb8d4e54ffee5467902803>
+-- Sadly, there is occasionally some configuration one would like to control
+-- in LLVM which are accessible only as command line flags setting global state,
+-- as if the command line tools were the only use of LLVM. Very sad.
+parseCommandLineOptions :: [String] -> Maybe String -> IO ()
+parseCommandLineOptions args overview = flip runAnyContT return $ do
+  args <- encodeM args
+  overview <- maybe (return nullPtr) encodeM overview
+  liftIO $ FFI.parseCommandLineOptions args overview
diff --git a/src/LLVM/Internal/Constant.hs b/src/LLVM/Internal/Constant.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/Constant.hs
@@ -0,0 +1,248 @@
+{-# LANGUAGE
+  TemplateHaskell,
+  QuasiQuotes,
+  MultiParamTypeClasses,
+  ScopedTypeVariables
+  #-}
+module LLVM.Internal.Constant where
+
+import LLVM.Prelude
+
+import qualified Language.Haskell.TH as TH
+import qualified Language.Haskell.TH.Quote as TH
+import qualified LLVM.Internal.InstructionDefs as ID
+
+import Data.Bits
+import Control.Monad.State (get, gets, modify, evalState)
+import Control.Monad.AnyCont
+import Control.Monad.IO.Class
+
+import qualified Data.Map as Map
+import Foreign.Ptr
+import Foreign.Storable (Storable, sizeOf)
+
+import qualified LLVM.Internal.FFI.Constant as FFI
+import qualified LLVM.Internal.FFI.GlobalValue as FFI
+import qualified LLVM.Internal.FFI.Instruction as FFI
+import qualified LLVM.Internal.FFI.LLVMCTypes as FFI
+import LLVM.Internal.FFI.LLVMCTypes (valueSubclassIdP)
+import qualified LLVM.Internal.FFI.PtrHierarchy as FFI
+import qualified LLVM.Internal.FFI.Type as FFI
+import qualified LLVM.Internal.FFI.User as FFI
+import qualified LLVM.Internal.FFI.Value as FFI
+import qualified LLVM.Internal.FFI.BinaryOperator as FFI
+
+import qualified LLVM.AST.Constant as A (Constant)
+import qualified LLVM.AST.Constant as A.C hiding (Constant)
+import qualified LLVM.AST.Type as A
+import qualified LLVM.AST.IntegerPredicate as A (IntegerPredicate)
+import qualified LLVM.AST.FloatingPointPredicate as A (FloatingPointPredicate)
+import qualified LLVM.AST.Float as A.F
+
+import LLVM.Internal.Coding
+import LLVM.Internal.DecodeAST
+import LLVM.Internal.EncodeAST
+import LLVM.Internal.Context
+import LLVM.Internal.Type ()
+import LLVM.Internal.IntegerPredicate ()
+import LLVM.Internal.FloatingPointPredicate ()
+
+allocaWords :: forall a m . (Storable a, MonadAnyCont IO m, Monad m, MonadIO m) => Word32 -> m (Ptr a)
+allocaWords nBits = do
+  allocaArray (((nBits-1) `div` (8*(fromIntegral (sizeOf (undefined :: a))))) + 1)
+
+instance EncodeM EncodeAST A.Constant (Ptr FFI.Constant) where
+  encodeM c = scopeAnyCont $ case c of
+    A.C.Int { A.C.integerBits = bits, A.C.integerValue = v } -> do
+      t <- encodeM (A.IntegerType bits)
+      words <- encodeM [
+        fromIntegral ((v `shiftR` (w*64)) .&. 0xffffffffffffffff) :: Word64
+        | w <- [0 .. ((fromIntegral bits-1) `div` 64)] 
+       ]
+      liftIO $ FFI.constantIntOfArbitraryPrecision t words
+    A.C.Float { A.C.floatValue = v } -> do
+      Context context <- gets encodeStateContext
+      let poke1 f = do
+            let nBits = fromIntegral $ 8*(sizeOf f)
+            words <- allocaWords nBits
+            poke (castPtr words) f
+            return (nBits, words)
+          poke2 fh fl = do
+             let nBits = fromIntegral $ 8*(sizeOf fh) + 8*(sizeOf fl)
+             words <- allocaWords nBits
+             pokeByteOff (castPtr words) 0 fl
+             pokeByteOff (castPtr words) (sizeOf fl) fh
+             return (nBits, words)
+      (nBits, words) <- case v of
+        A.F.Half f -> poke1 f
+        A.F.Single f -> poke1 f
+        A.F.Double f -> poke1 f
+        A.F.X86_FP80 high low -> poke2 high low
+        A.F.Quadruple high low -> poke2 high low
+        A.F.PPC_FP128 high low -> poke2 high low
+      let fpSem = case v of
+                    A.F.Half _ -> FFI.floatSemanticsIEEEhalf
+                    A.F.Single _ -> FFI.floatSemanticsIEEEsingle
+                    A.F.Double _ -> FFI.floatSemanticsIEEEdouble
+                    A.F.Quadruple _ _ -> FFI.floatSemanticsIEEEquad
+                    A.F.X86_FP80 _ _ -> FFI.floatSemanticsx87DoubleExtended
+                    A.F.PPC_FP128 _ _ -> FFI.floatSemanticsPPCDoubleDouble
+      nBits <- encodeM nBits
+      liftIO $ FFI.constantFloatOfArbitraryPrecision context nBits words fpSem
+    A.C.GlobalReference _ n -> FFI.upCast <$> referGlobal n
+    A.C.BlockAddress f b -> do
+      f' <- referGlobal f
+      b' <- getBlockForAddress f b
+      liftIO $ FFI.blockAddress (FFI.upCast f') b'
+    A.C.Struct nm p ms -> do
+      p <- encodeM p
+      ms <- encodeM ms
+      case nm of
+        Nothing -> do
+          Context context <- gets encodeStateContext
+          liftIO $ FFI.constStructInContext context ms p
+        Just nm -> do
+          t <- lookupNamedType nm
+          liftIO $ FFI.constNamedStruct t ms
+    A.C.TokenNone -> do
+      Context context <- gets encodeStateContext
+      liftIO $ FFI.getConstTokenNone context
+    o -> $(do
+      let constExprInfo =  ID.outerJoin ID.astConstantRecs (ID.innerJoin ID.astInstructionRecs ID.instructionDefs)
+      TH.caseE [| o |] $ do
+        (name, (Just (TH.RecC n fs), instrInfo)) <- Map.toList constExprInfo
+        let fns = [ TH.mkName . TH.nameBase $ fn | (fn, _, _) <- fs ]
+            coreCall n = TH.dyn $ "FFI.constant" ++ n
+            buildBody c = [ TH.bindS (TH.varP fn) [| encodeM $(TH.varE fn) |] | fn <- fns ]
+                          ++ [ TH.noBindS [| liftIO $(foldl TH.appE c (map TH.varE fns)) |] ]
+            hasFlags = any (== ''Bool) [ h | (_, _, TH.ConT h) <- fs ]
+        core <- case instrInfo of
+          Just (_, iDef) -> do
+            let opcode = TH.dataToExpQ (const Nothing) (ID.cppOpcode iDef)
+            case ID.instructionKind iDef of
+              ID.Binary | hasFlags -> return $ coreCall name
+                        | True -> return [| $(coreCall "BinaryOperator") $(opcode) |]
+              ID.Cast -> return [| $(coreCall "Cast") $(opcode) |]
+              _ -> return $ coreCall name
+          Nothing -> if (name `elem` ["Vector", "Null", "Array", "Undef"])
+                      then return $ coreCall name
+                      else []
+        return $ TH.match
+          (TH.recP n [(fn,) <$> (TH.varP . TH.mkName . TH.nameBase $ fn) | (fn, _, _) <- fs])
+          (TH.normalB (TH.doE (buildBody core)))
+          []
+      )
+
+instance DecodeM DecodeAST A.Constant (Ptr FFI.Constant) where
+  decodeM c = scopeAnyCont $ do
+    let v = FFI.upCast c :: Ptr FFI.Value
+        u = FFI.upCast c :: Ptr FFI.User
+    ft <- liftIO (FFI.typeOf v)
+    t <- decodeM ft
+    valueSubclassId <- liftIO $ FFI.getValueSubclassId v
+    nOps <- liftIO $ FFI.getNumOperands u
+    let globalRef = return A.C.GlobalReference 
+                    `ap` (return t)
+                    `ap` (getGlobalName =<< liftIO (FFI.isAGlobalValue v))
+        op = decodeM <=< liftIO . FFI.getConstantOperand c
+        getConstantOperands = mapM op [0..nOps-1] 
+        getConstantData = do
+          let nElements = case t of
+                            A.VectorType n _ -> n
+                            A.ArrayType n _ | n <= (fromIntegral (maxBound :: Word32)) -> fromIntegral n
+          forM [0..nElements-1] $ do
+             decodeM <=< liftIO . FFI.getConstantDataSequentialElementAsConstant c . fromIntegral
+
+    case valueSubclassId of
+      [valueSubclassIdP|Function|] -> globalRef
+      [valueSubclassIdP|GlobalAlias|] -> globalRef
+      [valueSubclassIdP|GlobalVariable|] -> globalRef
+      [valueSubclassIdP|ConstantInt|] -> do
+        np <- alloca
+        wsp <- liftIO $ FFI.getConstantIntWords c np
+        n <- peek np
+        words <- decodeM (n, wsp)
+        return $ A.C.Int (A.typeBits t) (foldr (\b a -> (a `shiftL` 64) .|. fromIntegral b) 0 (words :: [Word64]))
+      [valueSubclassIdP|ConstantFP|] -> do
+        let A.FloatingPointType nBits fmt = t
+        ws <- allocaWords nBits
+        liftIO $ FFI.getConstantFloatWords c ws
+        A.C.Float <$> (
+          case (nBits, fmt) of
+            (16, A.IEEE) -> A.F.Half <$> peek (castPtr ws)
+            (32, A.IEEE) -> A.F.Single <$> peek (castPtr ws)
+            (64, A.IEEE) -> A.F.Double <$> peek (castPtr ws)
+            (128, A.IEEE) -> A.F.Quadruple <$> peekByteOff (castPtr ws) 8 <*> peekByteOff (castPtr ws) 0
+            (80, A.DoubleExtended) -> A.F.X86_FP80 <$> peekByteOff (castPtr ws) 8 <*> peekByteOff (castPtr ws) 0
+            (128, A.PairOfFloats) -> A.F.PPC_FP128 <$> peekByteOff (castPtr ws) 8 <*> peekByteOff (castPtr ws) 0
+            _ -> error $ "don't know how to decode floating point constant of type: " ++ show t
+          )
+      [valueSubclassIdP|ConstantPointerNull|] -> return $ A.C.Null t
+      [valueSubclassIdP|ConstantAggregateZero|] -> return $ A.C.Null t
+      [valueSubclassIdP|UndefValue|] -> return $ A.C.Undef t
+      [valueSubclassIdP|BlockAddress|] -> 
+            return A.C.BlockAddress 
+               `ap` (getGlobalName =<< do liftIO $ FFI.isAGlobalValue =<< FFI.getBlockAddressFunction c)
+               `ap` (getLocalName =<< do liftIO $ FFI.getBlockAddressBlock c)
+      [valueSubclassIdP|ConstantStruct|] -> do
+            return A.C.Struct
+               `ap` (return $ case t of A.NamedTypeReference n -> Just n; _ -> Nothing)
+               `ap` (decodeM =<< liftIO (FFI.isPackedStruct ft))
+               `ap` getConstantOperands
+      [valueSubclassIdP|ConstantDataArray|] -> 
+            return A.C.Array `ap` (return $ A.elementType t) `ap` getConstantData
+      [valueSubclassIdP|ConstantArray|] -> 
+            return A.C.Array `ap` (return $ A.elementType t) `ap` getConstantOperands
+      [valueSubclassIdP|ConstantDataVector|] -> 
+            return A.C.Vector `ap` getConstantData
+      [valueSubclassIdP|ConstantExpr|] -> do
+            cppOpcode <- liftIO $ FFI.getConstantCPPOpcode c
+            $(
+              TH.caseE [| cppOpcode |] $ do
+                (name, ((TH.RecC n fs, _), iDef)) <- Map.toList $
+                      ID.innerJoin (ID.innerJoin ID.astConstantRecs ID.astInstructionRecs) ID.instructionDefs
+                let apWrapper o (fn, _, ct) = do
+                      a <- case ct of
+                             TH.ConT h
+                               | h == ''A.Constant -> do
+                                               operandNumber <- get
+                                               modify (+1)
+                                               return [| op $(TH.litE . TH.integerL $ operandNumber) |]
+                               | h == ''A.Type -> return [| pure t |]
+                               | h == ''A.IntegerPredicate -> 
+                                 return [| liftIO $ decodeM =<< FFI.getConstantICmpPredicate c |]
+                               | h == ''A.FloatingPointPredicate -> 
+                                 return [| liftIO $ decodeM =<< FFI.getConstantFCmpPredicate c |]
+                               | h == ''Bool -> case TH.nameBase fn of
+                                                  "inBounds" -> return [| liftIO $ decodeM =<< FFI.getInBounds v |]
+                                                  "exact" -> return [| liftIO $ decodeM =<< FFI.isExact v |]
+                                                  "nsw" -> return [| liftIO $ decodeM =<< FFI.hasNoSignedWrap v |]
+                                                  "nuw" -> return [| liftIO $ decodeM =<< FFI.hasNoUnsignedWrap v |]
+                                                  x -> error $ "constant bool field " ++ show x ++ " not handled yet"
+                             TH.AppT TH.ListT (TH.ConT h) 
+                               | h == ''Word32 -> 
+                                  return [|
+                                        do
+                                          np <- alloca
+                                          isp <- liftIO $ FFI.getConstantIndices c np
+                                          n <- peek np
+                                          decodeM (n, isp)
+                                        |]
+                               | h == ''A.Constant -> 
+                                  case TH.nameBase fn of
+                                    "indices" -> do
+                                      operandNumber <- get
+                                      return [| mapM op [$(TH.litE . TH.integerL $ operandNumber)..nOps-1] |]
+                             _ -> error $ "unhandled constant expr field type: " ++ show fn ++ " - " ++ show ct
+                      return [| $(o) `ap` $(a) |]
+                return $ TH.match 
+                          (TH.dataToPatQ (const Nothing) (ID.cppOpcode iDef))
+                          (TH.normalB (evalState (foldM apWrapper [| return $(TH.conE n) |] fs) 0))
+                          []
+             )
+      [valueSubclassIdP|ConstantTokenNone|] -> return A.C.TokenNone
+      _ -> error $ "unhandled constant valueSubclassId: " ++ show valueSubclassId
+
+
+  
+  
diff --git a/src/LLVM/Internal/Context.hs b/src/LLVM/Internal/Context.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/Context.hs
@@ -0,0 +1,20 @@
+module LLVM.Internal.Context where
+
+import LLVM.Prelude
+
+import Control.Exception
+import Control.Concurrent
+
+import Foreign.Ptr
+
+import qualified LLVM.Internal.FFI.Context as FFI
+
+-- | a Context object holds the state the of LLVM system needs for one thread of
+-- | LLVM compilation. Once upon a time, in early versions of LLVM, this state was global.
+-- | Then it got packed up in this object to allow multiple threads to compile at once.
+data Context = Context (Ptr FFI.Context)
+
+-- | Create a Context, run an action (to which it is provided), then destroy the Context.
+withContext :: (Context -> IO a) -> IO a
+withContext = runBound . bracket FFI.contextCreate FFI.contextDispose . (. Context)
+  where runBound = if rtsSupportsBoundThreads then runInBoundThread else id
diff --git a/src/LLVM/Internal/DataLayout.hs b/src/LLVM/Internal/DataLayout.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/DataLayout.hs
@@ -0,0 +1,23 @@
+module LLVM.Internal.DataLayout where
+
+import LLVM.Prelude
+
+import Control.Exception
+import Control.Monad.AnyCont
+import Control.Monad.IO.Class
+
+import Foreign.Ptr
+
+import qualified LLVM.Internal.FFI.DataLayout as FFI
+
+import LLVM.AST.DataLayout
+import LLVM.DataLayout
+
+import LLVM.Internal.Coding
+import LLVM.Internal.String ()
+
+withFFIDataLayout :: DataLayout -> (Ptr FFI.DataLayout -> IO a) -> IO a
+withFFIDataLayout dl f = flip runAnyContT return $ do
+  dls <- encodeM (dataLayoutToString dl)
+  liftIO $ bracket (FFI.createDataLayout dls) FFI.disposeDataLayout f
+
diff --git a/src/LLVM/Internal/DecodeAST.hs b/src/LLVM/Internal/DecodeAST.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/DecodeAST.hs
@@ -0,0 +1,172 @@
+{-# LANGUAGE
+  GeneralizedNewtypeDeriving,
+  MultiParamTypeClasses,
+  UndecidableInstances
+  #-}
+module LLVM.Internal.DecodeAST where
+
+import LLVM.Prelude
+
+import Control.Monad.State
+import Control.Monad.AnyCont
+
+import Foreign.Ptr
+import Foreign.C
+
+import Data.Sequence (Seq)
+import qualified Data.Sequence as Seq
+import Data.Map (Map)
+import qualified Data.Map as Map
+import Data.Array (Array)
+import qualified Data.Array as Array
+
+import qualified LLVM.Internal.FFI.Attribute as FFI
+import qualified LLVM.Internal.FFI.GlobalValue as FFI
+import qualified LLVM.Internal.FFI.PtrHierarchy as FFI
+import qualified LLVM.Internal.FFI.Value as FFI
+import qualified LLVM.Internal.FFI.Type as FFI
+
+import qualified LLVM.AST.Name as A
+import qualified LLVM.AST.Operand as A (MetadataNodeID(..))
+import qualified LLVM.AST.Attribute as A.A
+import qualified LLVM.AST.COMDAT as A.COMDAT
+
+import LLVM.Internal.Coding
+import LLVM.Internal.String ()
+
+type NameMap a = Map (Ptr a) Word
+
+data DecodeState = DecodeState {
+    globalVarNum :: NameMap FFI.GlobalValue,
+    localVarNum :: NameMap FFI.Value,
+    localNameCounter :: Maybe Word,
+    namedTypeNum :: NameMap FFI.Type,
+    typesToDefine :: Seq (Ptr FFI.Type),
+    metadataNodesToDefine :: Seq (A.MetadataNodeID, Ptr FFI.MDNode),
+    metadataNodes :: Map (Ptr FFI.MDNode) A.MetadataNodeID,
+    metadataKinds :: Array Word String,
+    parameterAttributeSets :: Map FFI.ParameterAttributeSet [A.A.ParameterAttribute],
+    functionAttributeSetIDs :: Map FFI.FunctionAttributeSet A.A.GroupID,
+    comdats :: Map (Ptr FFI.COMDAT) (String, A.COMDAT.SelectionKind)
+  }
+initialDecode = DecodeState {
+    globalVarNum = Map.empty,
+    localVarNum = Map.empty,
+    localNameCounter = Nothing,
+    namedTypeNum = Map.empty,
+    typesToDefine = Seq.empty,
+    metadataNodesToDefine = Seq.empty,
+    metadataNodes = Map.empty,
+    metadataKinds = Array.listArray (1,0) [],
+    parameterAttributeSets = Map.empty,
+    functionAttributeSetIDs = Map.empty,
+    comdats = Map.empty
+  }
+newtype DecodeAST a = DecodeAST { unDecodeAST :: AnyContT (StateT DecodeState IO) a }
+  deriving (
+    Applicative,
+    Functor,
+    Monad,
+    MonadIO,
+    MonadState DecodeState,
+    MonadAnyCont IO,
+    ScopeAnyCont
+  )
+
+runDecodeAST :: DecodeAST a -> IO a
+runDecodeAST d = flip evalStateT initialDecode . flip runAnyContT return . unDecodeAST $ d
+
+localScope :: DecodeAST a -> DecodeAST a
+localScope (DecodeAST x) = DecodeAST (tweak x)
+  where tweak x = do
+          modify (\s@DecodeState { localNameCounter = Nothing } -> s { localNameCounter = Just 0 })
+          r <- x
+          modify (\s@DecodeState { localNameCounter = Just _ } -> s { localNameCounter = Nothing })
+          return r
+
+getName :: (Ptr a -> IO CString)
+           -> Ptr a
+           -> (DecodeState -> NameMap a)
+           -> DecodeAST Word
+           -> DecodeAST A.Name
+getName getCString v getNameMap generate = do
+  name <- liftIO $ do
+            n <- getCString v
+            if n == nullPtr then return "" else decodeM n
+  if name /= "" 
+     then
+       return $ A.Name name
+     else
+       A.UnName <$> do
+         nm <- gets getNameMap
+         maybe generate return $ Map.lookup v nm
+
+getValueName :: FFI.DescendentOf FFI.Value v => Ptr v -> (DecodeState -> NameMap v) -> DecodeAST Word -> DecodeAST A.Name
+getValueName = getName (FFI.getValueName . FFI.upCast)
+
+getLocalName :: FFI.DescendentOf FFI.Value v => Ptr v -> DecodeAST A.Name
+getLocalName v' = do
+  let v = FFI.upCast v'
+  getValueName v localVarNum $ do
+                    nm <- gets localVarNum
+                    Just n <- gets localNameCounter
+                    modify $ \s -> s { localNameCounter = Just (1 + n), localVarNum = Map.insert v n nm }
+                    return n
+
+getGlobalName :: FFI.DescendentOf FFI.GlobalValue v => Ptr v -> DecodeAST A.Name
+getGlobalName v' = do
+  let v = FFI.upCast v'
+  getValueName v globalVarNum $ do
+                     nm <- gets globalVarNum
+                     let n = fromIntegral $ Map.size nm
+                     modify $ \s -> s { globalVarNum = Map.insert v n nm }
+                     return n
+
+
+getTypeName :: Ptr FFI.Type -> DecodeAST A.Name
+getTypeName t = do
+  getName FFI.getStructName t namedTypeNum $ do
+                  nm <- gets namedTypeNum
+                  let n = fromIntegral $ Map.size nm
+                  modify $ \s -> s { namedTypeNum = Map.insert t n nm }
+                  return n
+
+saveNamedType :: Ptr FFI.Type -> DecodeAST ()
+saveNamedType t = do
+  modify $ \s -> s { typesToDefine = t Seq.<| typesToDefine s }
+
+getMetadataNodeID :: Ptr FFI.MDNode -> DecodeAST A.MetadataNodeID
+getMetadataNodeID p = do
+  mdns <- gets metadataNodes
+  case Map.lookup p mdns of
+    Just r -> return r
+    Nothing -> do
+      let r = A.MetadataNodeID (fromIntegral (Map.size mdns))
+      modify $ \s -> s { 
+        metadataNodesToDefine = (r, p) Seq.<| metadataNodesToDefine s,
+        metadataNodes = Map.insert p r (metadataNodes s)
+      }
+      return r
+
+takeTypeToDefine :: DecodeAST (Maybe (Ptr FFI.Type))
+takeTypeToDefine = state $ \s -> case Seq.viewr (typesToDefine s) of
+  remaining Seq.:> t -> (Just t, s { typesToDefine = remaining })
+  _ -> (Nothing, s)
+
+takeMetadataNodeToDefine :: DecodeAST (Maybe (A.MetadataNodeID, Ptr FFI.MDNode))
+takeMetadataNodeToDefine = state $ \s -> case Seq.viewr (metadataNodesToDefine s) of
+  remaining Seq.:> md -> (Just md, s { metadataNodesToDefine = remaining })
+  _ -> (Nothing, s)                              
+
+instance DecodeM DecodeAST A.Name (Ptr FFI.BasicBlock) where
+  decodeM = getLocalName
+
+getAttributeGroupID :: FFI.FunctionAttributeSet -> DecodeAST A.A.GroupID
+getAttributeGroupID p = do
+  ids <- gets functionAttributeSetIDs
+  case Map.lookup p ids of
+    Just r -> return r
+    Nothing -> do
+      let r = A.A.GroupID (fromIntegral (Map.size ids))
+      modify $ \s -> s { functionAttributeSetIDs = Map.insert p r (functionAttributeSetIDs s) }
+      return r
diff --git a/src/LLVM/Internal/Diagnostic.hs b/src/LLVM/Internal/Diagnostic.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/Diagnostic.hs
@@ -0,0 +1,40 @@
+{-# LANGUAGE
+  TemplateHaskell,
+  MultiParamTypeClasses
+  #-}  
+module LLVM.Internal.Diagnostic where
+
+import LLVM.Prelude
+
+import qualified LLVM.Internal.FFI.LLVMCTypes as FFI
+import qualified LLVM.Internal.FFI.SMDiagnostic as FFI
+
+import Control.Exception
+
+import Foreign.Ptr
+
+import LLVM.Diagnostic
+import LLVM.Internal.Coding
+import LLVM.Internal.String ()
+
+genCodingInstance [t| DiagnosticKind |] ''FFI.DiagnosticKind [
+    (FFI.diagnosticKindError, ErrorKind),
+    (FFI.diagnosticKindWarning, WarningKind),
+    (FFI.diagnosticKindNote, NoteKind)
+  ]
+
+withSMDiagnostic :: (Ptr FFI.SMDiagnostic -> IO a) -> IO a
+withSMDiagnostic = bracket FFI.createSMDiagnostic FFI.disposeSMDiagnostic
+
+getDiagnostic :: Ptr FFI.SMDiagnostic -> IO Diagnostic
+getDiagnostic p = do
+  l <- decodeM =<< FFI.getSMDiagnosticLineNo p
+  c <- decodeM =<< FFI.getSMDiagnosticColumnNo p
+  k <- decodeM =<< FFI.getSMDiagnosticKind p
+  f <- decodeM $ FFI.getSMDiagnosticFilename p
+  m <- decodeM $ FFI.getSMDiagnosticMessage p
+  lc <- decodeM $ FFI.getSMDiagnosticLineContents p
+  return $ Diagnostic { 
+    lineNumber = l, columnNumber = c, diagnosticKind = k, filename = f, message = m, lineContents = lc
+   }
+
diff --git a/src/LLVM/Internal/EncodeAST.hs b/src/LLVM/Internal/EncodeAST.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/EncodeAST.hs
@@ -0,0 +1,159 @@
+{-# LANGUAGE
+  GeneralizedNewtypeDeriving,
+  MultiParamTypeClasses,
+  UndecidableInstances
+  #-}
+module LLVM.Internal.EncodeAST where
+
+import LLVM.Prelude
+
+import Control.Exception
+import Control.Monad.AnyCont
+import Control.Monad.Error.Class
+import Control.Monad.State
+import Control.Monad.Trans.Except
+
+import Foreign.Ptr
+import Foreign.C
+
+import Data.Map (Map)
+import qualified Data.Map as Map
+
+import qualified LLVM.Internal.FFI.Attribute as FFI  
+import qualified LLVM.Internal.FFI.Builder as FFI
+import qualified LLVM.Internal.FFI.GlobalValue as FFI
+import qualified LLVM.Internal.FFI.PtrHierarchy as FFI
+import qualified LLVM.Internal.FFI.Value as FFI
+
+import qualified LLVM.AST as A
+import qualified LLVM.AST.Attribute as A.A
+
+import LLVM.Internal.Context
+import LLVM.Internal.Coding
+import LLVM.Internal.String ()
+
+data LocalValue
+  = ForwardValue (Ptr FFI.Value)
+  | DefinedValue (Ptr FFI.Value)
+
+data EncodeState = EncodeState {
+      encodeStateBuilder :: Ptr FFI.Builder,
+      encodeStateContext :: Context,
+      encodeStateLocals :: Map A.Name LocalValue,
+      encodeStateGlobals :: Map A.Name (Ptr FFI.GlobalValue),
+      encodeStateAllBlocks :: Map (A.Name, A.Name) (Ptr FFI.BasicBlock),
+      encodeStateBlocks :: Map A.Name (Ptr FFI.BasicBlock),
+      encodeStateMDNodes :: Map A.MetadataNodeID (Ptr FFI.MDNode),
+      encodeStateNamedTypes :: Map A.Name (Ptr FFI.Type),
+      encodeStateAttributeGroups :: Map A.A.GroupID FFI.FunctionAttributeSet,
+      encodeStateCOMDATs :: Map String (Ptr FFI.COMDAT)
+    }
+
+newtype EncodeAST a = EncodeAST { unEncodeAST :: AnyContT (ExceptT String (StateT EncodeState IO)) a }
+    deriving (
+       Functor,
+       Applicative,
+       Monad,
+       MonadIO,
+       MonadState EncodeState,
+       MonadError String,
+       MonadAnyCont IO,
+       ScopeAnyCont
+     )
+
+lookupNamedType :: A.Name -> EncodeAST (Ptr FFI.Type)
+lookupNamedType n = do
+  t <- gets $ Map.lookup n . encodeStateNamedTypes
+  maybe (throwError $ "reference to undefined type: " ++ show n) return t
+
+defineType :: A.Name -> Ptr FFI.Type -> EncodeAST ()
+defineType n t = modify $ \s -> s { encodeStateNamedTypes = Map.insert n t (encodeStateNamedTypes s) }
+
+runEncodeAST :: Context -> EncodeAST a -> ExceptT String IO a
+runEncodeAST context@(Context ctx) (EncodeAST a) = ExceptT $
+    bracket (FFI.createBuilderInContext ctx) FFI.disposeBuilder $ \builder -> do
+      let initEncodeState = EncodeState {
+              encodeStateBuilder = builder,
+              encodeStateContext = context,
+              encodeStateLocals = Map.empty,
+              encodeStateGlobals = Map.empty,
+              encodeStateAllBlocks = Map.empty,
+              encodeStateBlocks = Map.empty,
+              encodeStateMDNodes = Map.empty,
+              encodeStateNamedTypes = Map.empty,
+              encodeStateAttributeGroups = Map.empty,
+              encodeStateCOMDATs = Map.empty
+            }
+      flip evalStateT initEncodeState . runExceptT . flip runAnyContT return $ a
+
+withName :: A.Name -> (CString -> IO a) -> IO a
+withName (A.Name n) = withCString n
+withName (A.UnName _) = withCString ""
+
+instance MonadAnyCont IO m => EncodeM m A.Name CString where
+  encodeM (A.Name n) = encodeM n
+  encodeM _ = encodeM ""
+
+phase :: EncodeAST a -> EncodeAST (EncodeAST a)
+phase p = do
+  let s0 `withLocalsFrom` s1 = s0 {
+         encodeStateLocals = encodeStateLocals s1,
+         encodeStateBlocks = encodeStateBlocks s1
+        }
+  s <- get
+  return $ do
+    s' <- get
+    put $ s' `withLocalsFrom` s
+    r <- p
+    modify (`withLocalsFrom` s')
+    return r
+
+defineLocal :: FFI.DescendentOf FFI.Value v => A.Name -> Ptr v -> EncodeAST ()
+defineLocal n v' = do
+  let v = FFI.upCast v'
+  def <- gets $ Map.lookup n . encodeStateLocals
+  case def of
+    Just (ForwardValue dummy) -> liftIO $ FFI.replaceAllUsesWith dummy v
+    _ -> return ()
+  modify $ \b -> b { encodeStateLocals = Map.insert n (DefinedValue v) (encodeStateLocals b) }
+
+defineGlobal :: FFI.DescendentOf FFI.GlobalValue v => A.Name -> Ptr v -> EncodeAST ()
+defineGlobal n v = modify $ \b -> b { encodeStateGlobals =  Map.insert n (FFI.upCast v) (encodeStateGlobals b) }
+
+defineMDNode :: A.MetadataNodeID -> Ptr FFI.MDNode -> EncodeAST ()
+defineMDNode n v = modify $ \b -> b { encodeStateMDNodes = Map.insert n (FFI.upCast v) (encodeStateMDNodes b) }
+
+defineAttributeGroup :: A.A.GroupID -> FFI.FunctionAttributeSet -> EncodeAST ()
+defineAttributeGroup gid attrs = modify $ \b -> b { encodeStateAttributeGroups = Map.insert gid attrs (encodeStateAttributeGroups b) }
+
+defineCOMDAT :: String -> Ptr FFI.COMDAT -> EncodeAST ()
+defineCOMDAT name cd = modify $ \b -> b { encodeStateCOMDATs = Map.insert name cd (encodeStateCOMDATs b) }
+
+refer :: (Show n, Ord n) => (EncodeState -> Map n v) -> n -> EncodeAST v -> EncodeAST v
+refer r n f = do
+  mop <- gets $ Map.lookup n . r
+  maybe f return mop
+
+undefinedReference :: Show n => String -> n -> EncodeAST a
+undefinedReference m n = throwError $ "reference to undefined " ++ m ++ ": " ++ show n
+
+referOrThrow :: (Show n, Ord n) => (EncodeState -> Map n v) -> String -> n -> EncodeAST v
+referOrThrow r m n = refer r n $ undefinedReference m n
+
+referGlobal = referOrThrow encodeStateGlobals "global"
+referMDNode = referOrThrow encodeStateMDNodes "metadata node"
+referAttributeGroup = referOrThrow encodeStateAttributeGroups "attribute group"
+referCOMDAT = referOrThrow encodeStateCOMDATs "COMDAT"
+
+defineBasicBlock :: A.Name -> A.Name -> Ptr FFI.BasicBlock -> EncodeAST ()
+defineBasicBlock fn n b = modify $ \s -> s {
+  encodeStateBlocks = Map.insert n b (encodeStateBlocks s),
+  encodeStateAllBlocks = Map.insert (fn, n) b (encodeStateAllBlocks s)
+}
+
+instance EncodeM EncodeAST A.Name (Ptr FFI.BasicBlock) where
+  encodeM = referOrThrow encodeStateBlocks "block"
+
+getBlockForAddress :: A.Name -> A.Name -> EncodeAST (Ptr FFI.BasicBlock)
+getBlockForAddress fn n = referOrThrow encodeStateAllBlocks "blockaddress" (fn, n)
+
diff --git a/src/LLVM/Internal/ExecutionEngine.hs b/src/LLVM/Internal/ExecutionEngine.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/ExecutionEngine.hs
@@ -0,0 +1,134 @@
+{-# LANGUAGE
+  MultiParamTypeClasses,
+  FunctionalDependencies,
+  RankNTypes
+  #-}
+module LLVM.Internal.ExecutionEngine where
+
+import LLVM.Prelude
+
+import Control.Exception
+import Control.Monad.IO.Class
+import Control.Monad.AnyCont
+import Control.Monad.Trans.Except
+
+import Data.IORef
+import Foreign.Ptr
+import Foreign.C (CUInt, CString)
+import Foreign.Marshal.Alloc (allocaBytes)
+
+import qualified LLVM.Internal.FFI.PtrHierarchy as FFI
+import qualified LLVM.Internal.FFI.ExecutionEngine as FFI
+import qualified LLVM.Internal.FFI.Module as FFI
+import qualified LLVM.Internal.FFI.LLVMCTypes as FFI
+
+import LLVM.Internal.Module
+import LLVM.Internal.Context
+import LLVM.Internal.Coding
+import qualified LLVM.CodeModel as CodeModel
+import LLVM.Internal.Target
+import qualified LLVM.AST as A
+
+removeModule :: Ptr FFI.ExecutionEngine -> Ptr FFI.Module -> IO ()
+removeModule e m = flip runAnyContT return $ do
+  d0 <- alloca
+  d1 <- alloca
+  r <- liftIO $ FFI.removeModule e m d0 d1
+  when (r /= 0) $ fail "FFI.removeModule failure"
+
+-- | a 'ExecutableModule' e represents a 'Module' which is currently "in" an
+-- 'ExecutionEngine', and so the functions of which may be executed.
+data ExecutableModule e = ExecutableModule e (Ptr FFI.Module)
+
+-- | <http://llvm.org/doxygen/classllvm_1_1ExecutionEngine.html>
+class ExecutionEngine e f | e -> f where
+  withModuleInEngine :: e -> Module -> (ExecutableModule e -> IO a) -> IO a
+  getFunction :: ExecutableModule e -> A.Name -> IO (Maybe f)
+
+instance ExecutionEngine (Ptr FFI.ExecutionEngine) (FunPtr ()) where
+  withModuleInEngine e m f = do
+    m' <- readModule m
+    bracket_ (FFI.addModule e m') (removeModule e m') (f (ExecutableModule e m'))
+  getFunction (ExecutableModule e m) (A.Name name) = flip runAnyContT return $ do
+    name <- encodeM name
+    f <- liftIO $ FFI.getNamedFunction m name
+    if f == nullPtr 
+      then 
+        return Nothing
+      else
+        do
+          p <- liftIO $ FFI.getPointerToGlobal e (FFI.upCast f)
+          return $ if p == nullPtr then Nothing else Just (castPtrToFunPtr p)
+
+withExecutionEngine :: 
+  Context ->
+  Maybe (Ptr FFI.Module) -> 
+  (Ptr (Ptr FFI.ExecutionEngine) -> Ptr FFI.Module -> Ptr (FFI.OwnerTransfered CString) -> IO CUInt) ->
+  (Ptr FFI.ExecutionEngine -> IO a) ->
+  IO a
+withExecutionEngine c m createEngine f = flip runAnyContT return $ do
+  liftIO initializeNativeTarget
+  outExecutionEngine <- alloca
+  outErrorCStringPtr <- alloca
+  dummyModule <- maybe (anyContToM $ liftM (either undefined id) . runExceptT
+                            . withModuleFromAST c (A.Module "" "" Nothing Nothing []))
+                 (liftIO . newModule) m
+  dummyModule' <- readModule dummyModule
+  r <- liftIO $ createEngine outExecutionEngine dummyModule' outErrorCStringPtr
+  when (r /= 0) $ fail =<< decodeM outErrorCStringPtr
+  executionEngine <- anyContToM $ bracket (peek outExecutionEngine) FFI.disposeExecutionEngine
+  liftIO $ removeModule executionEngine dummyModule'
+  liftIO $ f executionEngine
+
+data MCJITState
+  = Deferred (forall a . Module -> (Ptr FFI.ExecutionEngine -> IO a) -> IO a)
+  | Constructed (Ptr FFI.ExecutionEngine)
+
+-- | <http://llvm.org/doxygen/classllvm_1_1MCJIT.html>
+-- <http://blog.llvm.org/2010/04/intro-to-llvm-mc-project.html>
+-- N.B. - the LLVM MCJIT does not current support adding multiple
+-- modules to any one instance of the MCJIT.
+newtype MCJIT = MCJIT (IORef MCJITState)
+
+-- | bracket the creation and destruction of an 'MCJIT'
+withMCJIT :: 
+  Context
+  -> Maybe Word -- ^ optimization level
+  -> Maybe CodeModel.Model
+  -> Maybe Bool -- ^ True to disable frame pointer elimination
+  -> Maybe Bool -- ^ True to enable fast instruction selection
+--  -> Maybe MemoryManager -- llvm-hs doesn't support this yet
+  -> (MCJIT -> IO a)
+  -> IO a
+withMCJIT c opt cm fpe fisel f = do
+  FFI.linkInMCJIT
+  let createMCJITCompilerForModule e m s = do
+        size <- FFI.getMCJITCompilerOptionsSize
+        allocaBytes (fromIntegral size) $ \p -> do
+          FFI.initializeMCJITCompilerOptions p size
+          maybe (return ()) (FFI.setMCJITCompilerOptionsOptLevel p <=< encodeM) opt
+          maybe (return ()) (FFI.setMCJITCompilerOptionsCodeModel p <=< encodeM) cm
+          maybe (return ()) (FFI.setMCJITCompilerOptionsNoFramePointerElim p <=< encodeM) fpe
+          maybe (return ()) (FFI.setMCJITCompilerOptionsEnableFastISel p <=< encodeM) fisel
+          FFI.createMCJITCompilerForModule e m p size s
+  t <- newIORef (Deferred $ \mod f -> do m' <- readModule mod
+                                         withExecutionEngine c (Just m') createMCJITCompilerForModule f)
+  f (MCJIT t)
+
+instance ExecutionEngine MCJIT (FunPtr ()) where
+  withModuleInEngine (MCJIT s) m f = do
+    jitState <- readIORef s
+    let f' (ExecutableModule _ m) = f (ExecutableModule (MCJIT s) m)
+    case jitState of
+      Deferred c -> c m $ \e -> 
+        bracket_ 
+         (writeIORef s (Constructed e))
+         (writeIORef s jitState)
+         (withModuleInEngine e m f')
+      Constructed e -> withModuleInEngine e m f'
+
+  getFunction (ExecutableModule (MCJIT r) m) n = do
+    s <- liftIO $ readIORef r
+    case s of
+      Deferred _ -> return Nothing
+      Constructed e -> getFunction (ExecutableModule e m) n
diff --git a/src/LLVM/Internal/FFI/Analysis.h b/src/LLVM/Internal/FFI/Analysis.h
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/Analysis.h
@@ -0,0 +1,11 @@
+#ifndef __LLVM_INTERNAL_FFI__ANALYSIS__H__
+#define __LLVM_INTERNAL_FFI__ANALYSIS__H__
+
+#include "llvm-c/Analysis.h"
+
+#define LLVM_HS_FOR_EACH_VERIFIER_FAILURE_ACTION(macro) \
+	macro(AbortProcess) \
+	macro(PrintMessage) \
+	macro(ReturnStatus)
+
+#endif
diff --git a/src/LLVM/Internal/FFI/Analysis.hs b/src/LLVM/Internal/FFI/Analysis.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/Analysis.hs
@@ -0,0 +1,15 @@
+{-# LANGUAGE
+  ForeignFunctionInterface
+  #-}
+module LLVM.Internal.FFI.Analysis where
+
+import LLVM.Prelude
+
+import Foreign.Ptr
+import Foreign.C
+
+import LLVM.Internal.FFI.LLVMCTypes
+import LLVM.Internal.FFI.Module
+
+foreign import ccall unsafe "LLVMVerifyModule" verifyModule ::
+  Ptr Module -> VerifierFailureAction -> Ptr (OwnerTransfered CString) -> IO LLVMBool
diff --git a/src/LLVM/Internal/FFI/Assembly.hs b/src/LLVM/Internal/FFI/Assembly.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/Assembly.hs
@@ -0,0 +1,29 @@
+{-# LANGUAGE
+  ForeignFunctionInterface
+  #-}
+
+-- | Functions to read and write textual LLVM assembly
+module LLVM.Internal.FFI.Assembly where
+
+import LLVM.Prelude
+
+import LLVM.Internal.FFI.Context
+import LLVM.Internal.FFI.LLVMCTypes
+import LLVM.Internal.FFI.MemoryBuffer
+import LLVM.Internal.FFI.Module
+import LLVM.Internal.FFI.PtrHierarchy
+
+import Foreign.C
+import Foreign.Ptr
+
+-- | Use LLVM's parser to parse a string of llvm assembly in a memory buffer to get a module
+foreign import ccall unsafe "LLVM_Hs_ParseLLVMAssembly" parseLLVMAssembly ::
+  Ptr Context -> OwnerTransfered (Ptr MemoryBuffer) -> Ptr (OwnerTransfered CString) -> IO (Ptr Module)
+
+
+-- | LLVM's serializer to generate a string of llvm assembly from a module
+foreign import ccall unsafe "LLVM_Hs_WriteLLVMAssembly" writeLLVMAssembly ::
+  Ptr Module -> Ptr RawOStream -> IO ()
+
+
+
diff --git a/src/LLVM/Internal/FFI/AssemblyC.cpp b/src/LLVM/Internal/FFI/AssemblyC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/AssemblyC.cpp
@@ -0,0 +1,28 @@
+#define __STDC_LIMIT_MACROS
+#include "llvm/AsmParser/Parser.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include "llvm-c/Core.h"
+#include "llvm-c/IRReader.h"
+
+using namespace llvm;
+
+extern "C" {
+
+LLVMModuleRef LLVM_Hs_ParseLLVMAssembly(LLVMContextRef context,
+                                             LLVMMemoryBufferRef memoryBuffer,
+                                             char **error) {
+  LLVMModuleRef M;
+  LLVMParseIRInContext(context, memoryBuffer, &M, error);
+  return M;
+}
+
+void LLVM_Hs_WriteLLVMAssembly(LLVMModuleRef module, raw_ostream &os) {
+  os << *unwrap(module);
+}
+}
diff --git a/src/LLVM/Internal/FFI/Attribute.h b/src/LLVM/Internal/FFI/Attribute.h
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/Attribute.h
@@ -0,0 +1,69 @@
+#ifndef __LLVM_INTERNAL_FFI__ATTRIBUTES__H__
+#define __LLVM_INTERNAL_FFI__ATTRIBUTES__H__
+
+
+// The last three arguments are flags indicating if this is a
+// parameter attribute, function result attribute or function attribute.
+#define LLVM_HS_FOR_EACH_ATTRIBUTE_KIND(macro)	\
+	macro(None,F,F,F)                                 \
+	macro(Alignment,T,T,F)                            \
+	macro(AllocSize,F,F,T)                            \
+	macro(AlwaysInline,F,F,T)                         \
+	macro(ArgMemOnly,F,F,T)                           \
+	macro(Builtin,F,F,T)                              \
+	macro(ByVal,T,F,F)                                \
+	macro(Cold,F,F,T)                                 \
+	macro(Convergent,F,F,T)                           \
+	macro(Dereferenceable,T,T,F)                      \
+	macro(DereferenceableOrNull,T,T,F)                \
+	macro(InAlloca,T,F,F)                             \
+	macro(InReg,T,T,F)                                \
+	macro(InaccessibleMemOnly,F,F,T) \
+	macro(InaccessibleMemOrArgMemOnly,F,F,T) \
+	macro(InlineHint,F,F,T)                           \
+	macro(JumpTable,F,F,T)                            \
+	macro(MinSize,F,F,T)                              \
+	macro(Naked,F,F,T)                                \
+	macro(Nest,T,F,F)                                 \
+	macro(NoAlias,T,T,F)                              \
+	macro(NoBuiltin,F,F,T)                            \
+	macro(NoCapture,T,F,F)                            \
+	macro(NoDuplicate,F,F,T)                          \
+	macro(NoImplicitFloat,F,F,T)                      \
+	macro(NoInline,F,F,T)                             \
+	macro(NoRecurse,F,F,T)                            \
+	macro(NoRedZone,F,F,T)                            \
+	macro(NoReturn,F,F,T)                             \
+	macro(NoUnwind,F,F,T)                             \
+	macro(NonLazyBind,F,F,T)                          \
+	macro(NonNull,T,T,F)                              \
+	macro(OptimizeForSize,F,F,T)                      \
+	macro(OptimizeNone,F,F,T)                         \
+	macro(ReadNone,T,F,T)                             \
+	macro(ReadOnly,T,F,T)                             \
+	macro(Returned,T,F,F)                             \
+	macro(ReturnsTwice,F,F,T)                         \
+	macro(SExt,T,T,F)                                 \
+	macro(SafeStack,F,F,T)                            \
+	macro(SanitizeAddress,F,F,T)                      \
+	macro(SanitizeMemory,F,F,T)                       \
+	macro(SanitizeThread,F,F,T)                       \
+	macro(StackAlignment,F,F,T)                       \
+	macro(StackProtect,F,F,T)                         \
+	macro(StackProtectReq,F,F,T)                      \
+	macro(StackProtectStrong,F,F,T)                   \
+	macro(StructRet,T,F,F)                            \
+	macro(SwiftError,T,F,F)                           \
+	macro(SwiftSelf,T,F,F)                            \
+	macro(UWTable,F,F,T)                              \
+	macro(WriteOnly,T,F,T)                            \
+	macro(ZExt,T,T,F)                                 \
+	macro(EndAttrKinds,F,F,F)
+
+typedef enum {
+#define ENUM_CASE(x,p,r,f) LLVM_Hs_AttributeKind_ ## x,
+LLVM_HS_FOR_EACH_ATTRIBUTE_KIND(ENUM_CASE)
+#undef ENUM_CASE
+} LLVM_Hs_AttributeKind;
+
+#endif
diff --git a/src/LLVM/Internal/FFI/Attribute.hs b/src/LLVM/Internal/FFI/Attribute.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/Attribute.hs
@@ -0,0 +1,143 @@
+{-# LANGUAGE
+  ForeignFunctionInterface
+  #-}
+module LLVM.Internal.FFI.Attribute where
+
+import LLVM.Prelude
+
+import Foreign.C
+import Foreign.Ptr
+
+import LLVM.Internal.FFI.Context
+import LLVM.Internal.FFI.LLVMCTypes
+
+type Index = CInt
+type Slot = CUInt
+type IntValue = Word64
+
+{-
+Data model:
+llvm::Attribute is one function or parameter attribute
+
+llvm::AttributeSet is a mess.
+It's used to represent, at different times:
+a) the set of parameter attributes on a parameter
+b) the set of parameter attributes for a functions return value
+c) the set of function attributes for a function
+d) All of the above
+
+It is only possible to enumerate the attributes in an attribute set
+given a "slot".
+
+Encode path:
+Use AttrBuilder on the C++ side only, to implement [Attribute] -> AttributeSet
+AttributeSets -> whole AttributeSet
+
+Decode strategy:
+Store maps of AttributeSetImpl (Mess | Parameter | Function),
+keyed by raw pointer. Expect Parameter and Function AttributeSetImpls
+to have only one slot. Use the per-slot iterators to decode them
+-}
+
+data MixedAttributeType
+data FunctionAttributeType
+data ParameterAttributeType
+data AttributeImpl a
+data AttributeSetImpl a
+
+type Attribute a = Ptr (AttributeImpl a)
+type FunctionAttribute = Attribute FunctionAttributeType
+type ParameterAttribute = Attribute ParameterAttributeType
+
+type AttributeSet a = Ptr (AttributeSetImpl a)
+type MixedAttributeSet = AttributeSet MixedAttributeType
+type FunctionAttributeSet = AttributeSet FunctionAttributeType
+type ParameterAttributeSet = AttributeSet ParameterAttributeType
+
+forgetAttributeType :: AttributeSet a -> AttributeSet MixedAttributeType
+forgetAttributeType = castPtr
+
+functionIndex :: Index
+functionIndex = -1
+returnIndex :: Index
+returnIndex = 0
+
+foreign import ccall unsafe "LLVM_Hs_AttributeKindAsEnum" parameterAttributeKindAsEnum ::
+  ParameterAttribute -> IO ParameterAttributeKind
+
+foreign import ccall unsafe "LLVM_Hs_AttributeKindAsEnum" functionAttributeKindAsEnum ::
+  FunctionAttribute -> IO FunctionAttributeKind
+
+foreign import ccall unsafe "LLVM_Hs_IsStringAttribute" isStringAttribute ::
+  Attribute a -> IO LLVMBool
+
+foreign import ccall unsafe "LLVM_Hs_AttributeKindAsString" attributeKindAsString ::
+  Attribute a -> Ptr CSize -> IO (Ptr CChar)
+
+foreign import ccall unsafe "LLVM_Hs_AttributeValueAsString" attributeValueAsString ::
+  Attribute a -> Ptr CSize -> IO (Ptr CChar)
+
+foreign import ccall unsafe "LLVM_Hs_AttributeValueAsInt" attributeValueAsInt ::
+  Attribute a -> IO Word64
+
+foreign import ccall unsafe "LLVM_Hs_AttributeSetNumSlots" attributeSetNumSlots ::
+  AttributeSet a -> IO Slot
+
+foreign import ccall unsafe "LLVM_Hs_AttributeSetSlotIndex" attributeSetSlotIndex ::
+  AttributeSet a -> Slot -> IO Index
+
+foreign import ccall unsafe "LLVM_Hs_AttributeSetSlotAttributes" attributeSetSlotAttributes ::
+  MixedAttributeSet -> Slot -> IO (AttributeSet a)
+
+foreign import ccall unsafe "LLVM_Hs_AttributeSetGetAttributes" attributeSetGetAttributes ::
+  AttributeSet a -> Slot -> Ptr CUInt -> IO (Ptr (Attribute a))
+
+foreign import ccall unsafe "LLVM_Hs_GetAttributeSet" getAttributeSet ::
+  Ptr Context -> Index -> Ptr (AttrBuilder a) -> IO (AttributeSet a)
+
+foreign import ccall unsafe "LLVM_Hs_MixAttributeSets" mixAttributeSets ::
+  Ptr Context -> Ptr MixedAttributeSet -> CUInt -> IO MixedAttributeSet
+
+data AttrBuilder a
+type FunctionAttrBuilder = AttrBuilder FunctionAttributeType
+type ParameterAttrBuilder = AttrBuilder ParameterAttributeType
+
+foreign import ccall unsafe "LLVM_Hs_GetAttrBuilderSize" getAttrBuilderSize ::
+  CSize
+
+foreign import ccall unsafe "LLVM_Hs_ConstructAttrBuilder" constructAttrBuilder ::
+  Ptr Word8 -> IO (Ptr (AttrBuilder a))
+
+foreign import ccall unsafe "LLVM_Hs_DestroyAttrBuilder" destroyAttrBuilder ::
+  Ptr (AttrBuilder a) -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_AttrBuilderAddAttributeKind" attrBuilderAddFunctionAttributeKind ::
+  Ptr FunctionAttrBuilder -> FunctionAttributeKind -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_AttrBuilderAddAttributeKind" attrBuilderAddParameterAttributeKind ::
+  Ptr ParameterAttrBuilder -> ParameterAttributeKind -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_AttrBuilderAddStringAttribute" attrBuilderAddStringAttribute ::
+  Ptr FunctionAttrBuilder -> Ptr CChar -> CSize -> Ptr CChar -> CSize -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_AttrBuilderAddAlignment" attrBuilderAddAlignment ::
+  Ptr ParameterAttrBuilder -> Word64 -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_AttrBuilderAddStackAlignment" attrBuilderAddStackAlignment ::
+  Ptr FunctionAttrBuilder -> Word64 -> IO ()
+
+-- The CInt is 0 if the last value is null and 1 otherwise
+foreign import ccall unsafe "LLVM_Hs_AttrBuilderAddAllocSize" attrBuilderAddAllocSize' ::
+  Ptr FunctionAttrBuilder -> CUInt -> CUInt -> LLVMBool -> IO ()
+
+attrBuilderAddAllocSize :: Ptr FunctionAttrBuilder -> CUInt -> (CUInt, LLVMBool) -> IO ()
+attrBuilderAddAllocSize b i (y, isJust) = attrBuilderAddAllocSize' b i y isJust
+
+foreign import ccall unsafe "LLVM_Hs_AttrBuilderAddDereferenceableAttr" attrBuilderAddDereferenceable ::
+  Ptr ParameterAttrBuilder -> Word64 -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_AttrBuilderAddDereferenceableOrNullAttr" attrBuilderAddDereferenceableOrNull ::
+  Ptr ParameterAttrBuilder -> Word64 -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_AttributeGetAllocSizeArgs" attributeGetAllocSizeArgs ::
+  FunctionAttribute -> Ptr CUInt -> Ptr CUInt -> IO LLVMBool
diff --git a/src/LLVM/Internal/FFI/AttributeC.cpp b/src/LLVM/Internal/FFI/AttributeC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/AttributeC.cpp
@@ -0,0 +1,145 @@
+#define __STDC_LIMIT_MACROS
+#include "llvm/IR/LLVMContext.h"
+#include "LLVM/Internal/FFI/AttributeC.hpp"
+
+extern "C" {
+
+static_assert(sizeof(AttributeSet) == sizeof(AttributeSetImpl *),
+              "AttributeSet implementation has changed");
+
+static_assert(sizeof(Attribute) == sizeof(AttributeImpl *),
+              "Attribute implementation has changed");
+
+unsigned LLVM_Hs_AttributeSetNumSlots(const AttributeSetImpl *a) {
+	return unwrap(a).getNumSlots();
+}
+
+int LLVM_Hs_AttributeSetSlotIndex(const AttributeSetImpl *a, unsigned slot) {
+	return unwrap(a).getSlotIndex(slot);
+}
+
+const AttributeSetImpl *LLVM_Hs_AttributeSetSlotAttributes(const AttributeSetImpl *a, unsigned slot) {
+	return wrap(unwrap(a).getSlotAttributes(slot));
+}
+
+const AttributeSetImpl *LLVM_Hs_GetSlotAttributeSet(
+	LLVMContextRef context,
+	unsigned index,
+	const AttributeImpl **attributes,
+	unsigned length
+) {
+	AttrBuilder builder;
+	for (unsigned i = 0; i < length; i++) builder.addAttribute(unwrap(attributes[i]));
+	return wrap(AttributeSet::get(*unwrap(context), index, builder));
+}
+
+const AttributeImpl *const *LLVM_Hs_AttributeSetGetAttributes(AttributeSetImpl *asi, unsigned slot, unsigned *length) {
+	AttributeSet as = unwrap(asi);
+	ArrayRef<Attribute>::iterator b = as.begin(slot), e = as.end(slot);
+	*length = e - b;
+	return reinterpret_cast<const AttributeImpl *const *>(b);
+}
+
+#define CHECK(name,p,r,f)																								\
+	static_assert(																												\
+		unsigned(llvm::Attribute::name) == unsigned(LLVM_Hs_AttributeKind_ ## name), \
+		"LLVM_Hs_AttributeKind enum out of sync w/ llvm::Attribute::AttrKind for " #name	\
+	);
+	LLVM_HS_FOR_EACH_ATTRIBUTE_KIND(CHECK)
+#undef CHECK
+
+unsigned LLVM_Hs_AttributeKindAsEnum(const AttributeImpl *a) {
+    return unwrap(a).getKindAsEnum();
+}
+
+uint64_t LLVM_Hs_AttributeValueAsInt(const AttributeImpl *a) {
+	return unwrap(a).getValueAsInt();
+}
+
+LLVMBool LLVM_Hs_IsStringAttribute(const AttributeImpl *a) {
+	return unwrap(a).isStringAttribute();
+}
+
+const char *LLVM_Hs_AttributeKindAsString(const AttributeImpl *a, size_t &l) {
+	const StringRef s = unwrap(a).getKindAsString();
+	l = s.size();
+	return s.data();
+}
+
+const char *LLVM_Hs_AttributeValueAsString(const AttributeImpl *a, size_t &l) {
+	const StringRef s = unwrap(a).getValueAsString();
+	l = s.size();
+	return s.data();
+}
+
+const AttributeSetImpl *LLVM_Hs_GetAttributeSet(LLVMContextRef context, unsigned index, const AttrBuilder &ab) {
+	return wrap(AttributeSet::get(*unwrap(context), index, ab));
+}
+
+const AttributeSetImpl *LLVM_Hs_MixAttributeSets(
+	LLVMContextRef context, const AttributeSetImpl **as, unsigned n
+) {
+	return wrap(
+		AttributeSet::get(
+			*unwrap(context),
+			ArrayRef<AttributeSet>(reinterpret_cast<const AttributeSet *>(as), n)
+		)
+	);
+}
+
+size_t LLVM_Hs_GetAttrBuilderSize() { return sizeof(AttrBuilder); }
+
+AttrBuilder *LLVM_Hs_ConstructAttrBuilder(char *p) {
+	return new(p) AttrBuilder();
+}
+
+void LLVM_Hs_DestroyAttrBuilder(AttrBuilder *a) {
+	a->~AttrBuilder();
+}
+
+void LLVM_Hs_AttrBuilderAddAttributeKind(AttrBuilder &ab, unsigned kind) {
+    ab.addAttribute(Attribute::AttrKind(kind));
+}
+
+void LLVM_Hs_AttrBuilderAddStringAttribute(
+	AttrBuilder &ab, const char *kind, size_t kind_len, const char *value, size_t value_len
+) {
+	ab.addAttribute(StringRef(kind, kind_len), StringRef(value, value_len));
+}
+
+void LLVM_Hs_AttrBuilderAddAlignment(AttrBuilder &ab, uint64_t v) {
+	ab.addAlignmentAttr(v);
+}
+
+void LLVM_Hs_AttrBuilderAddStackAlignment(AttrBuilder &ab, uint64_t v) {
+	ab.addStackAlignmentAttr(v);
+}
+
+void LLVM_Hs_AttrBuilderAddAllocSize(AttrBuilder &ab, unsigned x, LLVMBool optionalIsThere, unsigned y) {
+    if (optionalIsThere) {
+        ab.addAllocSizeAttr(x, y);
+    } else {
+        ab.addAllocSizeAttr(x, Optional<unsigned>());
+    }
+}
+
+void LLVM_Hs_AttrBuilderAddDereferenceableAttr(AttrBuilder &ab, uint64_t v) {
+	ab.addDereferenceableAttr(v);
+}
+
+void LLVM_Hs_AttrBuilderAddDereferenceableOrNullAttr(AttrBuilder &ab, uint64_t v) {
+    ab.addDereferenceableOrNullAttr(v);
+}
+
+LLVMBool LLVM_Hs_AttributeGetAllocSizeArgs(const AttributeImpl* a, unsigned* x, unsigned* y) {
+    auto pair = unwrap(a).getAllocSizeArgs();
+    *x = pair.first;
+    if (pair.second.hasValue()) {
+        *y = pair.second.getValue();
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+}
diff --git a/src/LLVM/Internal/FFI/AttributeC.hpp b/src/LLVM/Internal/FFI/AttributeC.hpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/AttributeC.hpp
@@ -0,0 +1,24 @@
+#ifndef __LLVM_ATTRIBUTE_C_HPP__
+#define __LLVM_ATTRIBUTE_C_HPP__
+#define __STDC_LIMIT_MACROS
+#include "llvm/IR/Attributes.h"
+#include "LLVM/Internal/FFI/Attribute.h"
+using namespace llvm;
+
+inline AttributeSet unwrap(const AttributeSetImpl *asi) {
+    return *reinterpret_cast<const AttributeSet *>(&asi);
+}
+
+inline const AttributeSetImpl *wrap(AttributeSet as) {
+    return *reinterpret_cast<const AttributeSetImpl **>(&as);
+}
+
+inline Attribute unwrap(const AttributeImpl *ai) {
+    return *reinterpret_cast<const Attribute *>(&ai);
+}
+
+inline const AttributeImpl *wrap(Attribute a) {
+    return *reinterpret_cast<const AttributeImpl **>(&a);
+}
+
+#endif
diff --git a/src/LLVM/Internal/FFI/BasicBlock.hs b/src/LLVM/Internal/FFI/BasicBlock.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/BasicBlock.hs
@@ -0,0 +1,34 @@
+{-# LANGUAGE
+  ForeignFunctionInterface,
+  MultiParamTypeClasses
+  #-}
+
+-- | <http://llvm.org/doxygen/classllvm_1_1BasicBlock.html>
+module LLVM.Internal.FFI.BasicBlock where
+
+import LLVM.Prelude
+
+import Foreign.Ptr
+
+import LLVM.Internal.FFI.PtrHierarchy
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreValueBasicBlock.html#gab57c996ff697ef40966432055ae47a4e>
+foreign import ccall unsafe "LLVMIsABasicBlock" isABasicBlock ::
+    Ptr Value -> IO (Ptr BasicBlock)
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreValueBasicBlock.html#ga754e45f69f4b784b658d9e379943f354>
+foreign import ccall unsafe "LLVMGetBasicBlockTerminator" getBasicBlockTerminator ::
+    Ptr BasicBlock -> IO (Ptr Instruction)
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreValueBasicBlock.html#ga9baf824cd325ad211027b23fce8a7494>
+foreign import ccall unsafe "LLVMGetFirstInstruction" getFirstInstruction ::
+    Ptr BasicBlock -> IO (Ptr Instruction)
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreValueBasicBlock.html#gaa0bb2c95802d06bf94f4c55e61fc3477>
+foreign import ccall unsafe "LLVMGetLastInstruction" getLastInstruction ::
+    Ptr BasicBlock -> IO (Ptr Instruction)
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreValueInstruction.html#ga1b4c3bd197e86e8bffdda247ddf8ec5e>
+foreign import ccall unsafe "LLVMGetNextInstruction" getNextInstruction ::
+    Ptr Instruction -> IO (Ptr Instruction)
+
diff --git a/src/LLVM/Internal/FFI/BinaryOperator.h b/src/LLVM/Internal/FFI/BinaryOperator.h
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/BinaryOperator.h
@@ -0,0 +1,16 @@
+#ifndef __LLVM_INTERNAL_FFI__BINARY_OPERATOR__H__
+#define __LLVM_INTERNAL_FFI__BINARY_OPERATOR__H__
+
+#define LLVM_HS_FOR_EACH_POSSIBLY_EXACT_BINARY_OPERATOR(macro) \
+	macro(UDiv) \
+	macro(SDiv) \
+	macro(LShr) \
+	macro(AShr)
+
+#define LLVM_HS_FOR_EACH_OVERFLOWING_BINARY_OPERATOR(macro) \
+	macro(Add) \
+	macro(Mul) \
+	macro(Shl) \
+	macro(Sub) \
+
+#endif
diff --git a/src/LLVM/Internal/FFI/BinaryOperator.hs b/src/LLVM/Internal/FFI/BinaryOperator.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/BinaryOperator.hs
@@ -0,0 +1,30 @@
+{-# LANGUAGE
+  ForeignFunctionInterface,
+  MultiParamTypeClasses,
+  UndecidableInstances
+  #-}
+-- | FFI functions for handling the LLVM BinaryOperator class
+module LLVM.Internal.FFI.BinaryOperator where
+
+import LLVM.Prelude
+
+import Foreign.Ptr
+import Foreign.C
+
+import LLVM.Internal.FFI.PtrHierarchy
+import LLVM.Internal.FFI.LLVMCTypes
+
+foreign import ccall unsafe "LLVMIsABinaryOperator" isABinaryOperator ::
+    Ptr Value -> IO (Ptr BinaryOperator)
+
+foreign import ccall unsafe "LLVM_Hs_HasNoSignedWrap" hasNoSignedWrap ::
+    Ptr Value -> IO LLVMBool
+
+foreign import ccall unsafe "LLVM_Hs_HasNoUnsignedWrap" hasNoUnsignedWrap ::
+    Ptr Value -> IO LLVMBool
+
+foreign import ccall unsafe "LLVM_Hs_IsExact" isExact ::
+    Ptr Value -> IO LLVMBool
+
+foreign import ccall unsafe "LLVM_Hs_GetFastMathFlags" getFastMathFlags ::
+    Ptr Value -> IO FastMathFlags
diff --git a/src/LLVM/Internal/FFI/Bitcode.hs b/src/LLVM/Internal/FFI/Bitcode.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/Bitcode.hs
@@ -0,0 +1,26 @@
+{-# LANGUAGE
+  ForeignFunctionInterface
+  #-}
+
+-- | Functions to read and write LLVM bitcode
+module LLVM.Internal.FFI.Bitcode where
+
+import LLVM.Prelude
+
+import LLVM.Internal.FFI.Context
+import LLVM.Internal.FFI.LLVMCTypes
+import LLVM.Internal.FFI.MemoryBuffer
+import LLVM.Internal.FFI.Module
+import LLVM.Internal.FFI.PtrHierarchy
+
+import Foreign.C
+import Foreign.Ptr
+
+foreign import ccall unsafe "LLVM_Hs_ParseBitcode" parseBitcode ::
+  Ptr Context -> Ptr MemoryBuffer -> Ptr (OwnerTransfered CString) -> IO (Ptr Module)
+
+foreign import ccall unsafe "LLVM_Hs_WriteBitcode" writeBitcode ::
+  Ptr Module -> Ptr RawOStream -> IO ()
+
+                               
+
diff --git a/src/LLVM/Internal/FFI/BitcodeC.cpp b/src/LLVM/Internal/FFI/BitcodeC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/BitcodeC.cpp
@@ -0,0 +1,34 @@
+#define __STDC_LIMIT_MACROS
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm-c/Core.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Bitcode/BitcodeReader.h"
+#include "llvm/Bitcode/BitcodeWriter.h"
+
+using namespace llvm;
+
+extern "C" {
+
+LLVMModuleRef LLVM_Hs_ParseBitcode(
+	LLVMContextRef c,
+	LLVMMemoryBufferRef mb, 
+	char **error
+) {
+    Expected<std::unique_ptr<Module>> moduleOrErr = parseBitcodeFile(unwrap(mb)->getMemBufferRef(), *unwrap(c));
+    if (Error err = moduleOrErr.takeError()) {
+        handleAllErrors(std::move(err), [&](ErrorInfoBase &eib) {
+                *error = strdup(eib.message().c_str());
+            });
+        return nullptr;
+    }
+    return wrap(moduleOrErr.get().release());
+}
+
+void LLVM_Hs_WriteBitcode(LLVMModuleRef m, raw_ostream &os) {
+	WriteBitcodeToFile(unwrap(m), os);
+}
+
+}
+
diff --git a/src/LLVM/Internal/FFI/Builder.hs b/src/LLVM/Internal/FFI/Builder.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/Builder.hs
@@ -0,0 +1,184 @@
+{-#
+  LANGUAGE
+  ForeignFunctionInterface,
+  TemplateHaskell,
+  ViewPatterns
+  #-}
+-- | FFI glue for llvm::IRBuilder - llvm's IR construction state object
+module LLVM.Internal.FFI.Builder where
+
+import LLVM.Prelude
+
+import qualified Language.Haskell.TH as TH
+
+import Foreign.Ptr
+import Foreign.C
+
+import qualified Data.List as List
+import qualified Data.Map as Map
+
+import qualified LLVM.AST.Instruction as A
+import LLVM.Internal.InstructionDefs as ID
+
+import LLVM.Internal.FFI.Cleanup
+import LLVM.Internal.FFI.Context
+import LLVM.Internal.FFI.LLVMCTypes
+import LLVM.Internal.FFI.PtrHierarchy
+
+data Builder
+
+foreign import ccall unsafe "LLVMCreateBuilderInContext" createBuilderInContext ::
+  Ptr Context -> IO (Ptr Builder)
+
+foreign import ccall unsafe "LLVMDisposeBuilder" disposeBuilder ::
+  Ptr Builder -> IO ()
+
+foreign import ccall unsafe "LLVMPositionBuilderAtEnd" positionBuilderAtEnd ::
+  Ptr Builder -> Ptr BasicBlock -> IO ()
+
+
+foreign import ccall unsafe "LLVMBuildRet" buildRet ::
+  Ptr Builder -> Ptr Value -> IO (Ptr Instruction)
+
+foreign import ccall unsafe "LLVMBuildBr" buildBr ::
+  Ptr Builder -> Ptr BasicBlock -> IO (Ptr Instruction)
+
+foreign import ccall unsafe "LLVMBuildCondBr" buildCondBr ::
+  Ptr Builder -> Ptr Value -> Ptr BasicBlock -> Ptr BasicBlock -> IO (Ptr Instruction)
+
+foreign import ccall unsafe "LLVMBuildSwitch" buildSwitch ::
+  Ptr Builder -> Ptr Value -> Ptr BasicBlock -> CUInt -> IO (Ptr Instruction)
+
+foreign import ccall unsafe "LLVMBuildIndirectBr" buildIndirectBr ::
+  Ptr Builder -> Ptr Value -> CUInt -> IO (Ptr Instruction)
+
+foreign import ccall unsafe "LLVMBuildInvoke" buildInvoke ::
+  Ptr Builder -> Ptr Value -> Ptr (Ptr Value) -> CUInt
+              -> Ptr BasicBlock -> Ptr BasicBlock -> CString -> IO (Ptr Instruction)
+
+foreign import ccall unsafe "LLVMBuildResume" buildResume ::
+  Ptr Builder -> Ptr Value -> IO (Ptr Instruction)
+
+foreign import ccall unsafe "LLVMBuildUnreachable" buildUnreachable ::
+  Ptr Builder -> IO (Ptr Instruction)
+
+foreign import ccall unsafe "LLVM_Hs_BuildCleanupRet" buildCleanupRet ::
+  Ptr Builder -> Ptr Value -> Ptr BasicBlock -> IO (Ptr Instruction)
+
+foreign import ccall unsafe "LLVM_Hs_BuildCatchRet" buildCatchRet ::
+  Ptr Builder -> Ptr Value -> Ptr BasicBlock -> IO (Ptr Instruction)
+
+foreign import ccall unsafe "LLVM_Hs_BuildCatchSwitch" buildCatchSwitch ::
+  Ptr Builder -> Ptr Value -> Ptr BasicBlock -> CUInt -> IO (Ptr Instruction)
+
+$(do
+  liftM concat $ sequence $ do
+    let instrInfo = ID.outerJoin ID.astInstructionRecs ID.instructionDefs
+    (lrn, ii) <- Map.toList instrInfo
+    (TH.RecC _ (unzip3 -> (_, _, fieldTypes)), ID.InstructionDef { ID.cAPIName = a, ID.instructionKind = k }) <- case ii of
+      (Just r, Just d) -> return (r,d)
+      (Just _, Nothing) -> error $ "An AST instruction was not found in the LLVM instruction defs"
+      (Nothing, Just ID.InstructionDef { ID.instructionKind = k }) | k /= ID.Terminator ->
+        error $ "LLVM instruction def " ++ lrn ++ " not found in the AST"
+      _ -> []
+
+    let ats = map typeMapping (fieldTypes List.\\ [TH.ConT ''A.InstructionMetadata, TH.ConT ''A.FastMathFlags])
+        cName = (if hasFlags fieldTypes then "LLVM_Hs_" else "LLVM") ++ "Build" ++ a
+    rt <- case k of
+            ID.Binary -> [[t| BinaryOperator |]]
+            ID.Cast -> [[t| Instruction |]]
+            _ -> []
+    return $ foreignDecl cName ("build" ++ a) ([[t| Ptr Builder |]] ++ ats ++ [[t| CString |]]) [t| Ptr $(rt) |]
+ )
+
+foreign import ccall unsafe "LLVMBuildArrayAlloca" buildAlloca ::
+  Ptr Builder -> Ptr Type -> Ptr Value -> CString -> IO (Ptr Instruction)
+
+foreign import ccall unsafe "LLVM_Hs_BuildLoad" buildLoad' ::
+  Ptr Builder -> LLVMBool -> Ptr Value -> MemoryOrdering -> SynchronizationScope -> CUInt -> CString -> IO (Ptr Instruction)
+
+buildLoad :: Ptr Builder -> LLVMBool -> Ptr Value -> (SynchronizationScope, MemoryOrdering) -> CUInt -> CString -> IO (Ptr Instruction)
+buildLoad builder vol a' (ss, mo) al s = buildLoad' builder vol a' mo ss al s
+
+foreign import ccall unsafe "LLVM_Hs_BuildStore" buildStore' ::
+  Ptr Builder -> LLVMBool -> Ptr Value -> Ptr Value -> MemoryOrdering -> SynchronizationScope -> CUInt -> CString -> IO (Ptr Instruction)
+
+buildStore :: Ptr Builder -> LLVMBool -> Ptr Value -> Ptr Value -> (SynchronizationScope, MemoryOrdering) -> CUInt -> CString -> IO (Ptr Instruction)
+buildStore builder vol a' v' (ss, mo) al s = buildStore' builder vol a' v' mo ss al s
+
+foreign import ccall unsafe "LLVMBuildGEP" buildGetElementPtr' ::
+  Ptr Builder -> Ptr Value -> Ptr (Ptr Value) -> CUInt -> CString -> IO (Ptr Instruction)
+
+foreign import ccall unsafe "LLVMBuildInBoundsGEP" buildInBoundsGetElementPtr' ::
+  Ptr Builder -> Ptr Value -> Ptr (Ptr Value) -> CUInt -> CString -> IO (Ptr Instruction)
+
+buildGetElementPtr :: Ptr Builder -> LLVMBool -> Ptr Value -> (CUInt, Ptr (Ptr Value)) -> CString -> IO (Ptr Instruction)
+buildGetElementPtr builder (LLVMBool 1) a (n, is) s = buildInBoundsGetElementPtr' builder a is n s
+buildGetElementPtr builder (LLVMBool 0) a (n, is) s = buildGetElementPtr' builder a is n s
+
+foreign import ccall unsafe "LLVM_Hs_BuildFence" buildFence' ::
+  Ptr Builder -> MemoryOrdering -> SynchronizationScope -> CString -> IO (Ptr Instruction)
+
+buildFence :: Ptr Builder -> (SynchronizationScope, MemoryOrdering) -> CString -> IO (Ptr Instruction)
+buildFence builder (ss, mo) s = buildFence' builder mo ss s
+
+foreign import ccall unsafe "LLVM_Hs_BuildAtomicCmpXchg" buildCmpXchg' ::
+  Ptr Builder -> LLVMBool -> Ptr Value -> Ptr Value -> Ptr Value -> MemoryOrdering -> MemoryOrdering -> SynchronizationScope -> CString -> IO (Ptr Instruction)
+
+buildCmpXchg :: Ptr Builder -> LLVMBool -> Ptr Value -> Ptr Value -> Ptr Value -> (SynchronizationScope, MemoryOrdering) -> MemoryOrdering -> CString -> IO (Ptr Instruction)
+buildCmpXchg builder vol a e r (ss, smo) fmo s =  buildCmpXchg' builder vol a e r smo fmo ss s
+
+foreign import ccall unsafe "LLVM_Hs_BuildAtomicRMW" buildAtomicRMW' ::
+  Ptr Builder -> LLVMBool -> RMWOperation -> Ptr Value -> Ptr Value -> MemoryOrdering -> SynchronizationScope -> CString -> IO (Ptr Instruction)
+
+buildAtomicRMW :: Ptr Builder -> LLVMBool -> RMWOperation -> Ptr Value -> Ptr Value -> (SynchronizationScope, MemoryOrdering) -> CString -> IO (Ptr Instruction)
+buildAtomicRMW builder vol rmwOp a v (ss, mo) s = buildAtomicRMW' builder vol rmwOp a v mo ss s
+
+foreign import ccall unsafe "LLVMBuildICmp" buildICmp ::
+  Ptr Builder -> ICmpPredicate -> Ptr Value -> Ptr Value -> CString -> IO (Ptr Instruction)
+
+foreign import ccall unsafe "LLVMBuildFCmp" buildFCmp ::
+  Ptr Builder -> FCmpPredicate -> Ptr Value -> Ptr Value -> CString -> IO (Ptr Instruction)
+
+foreign import ccall unsafe "LLVMBuildPhi" buildPhi ::
+  Ptr Builder -> Ptr Type -> CString -> IO (Ptr Instruction)
+
+foreign import ccall unsafe "LLVMBuildCall" buildCall ::
+  Ptr Builder -> Ptr Value -> Ptr (Ptr Value) -> CUInt -> CString -> IO (Ptr Instruction)
+
+foreign import ccall unsafe "LLVMBuildSelect" buildSelect ::
+  Ptr Builder -> Ptr Value -> Ptr Value -> Ptr Value -> CString -> IO (Ptr Instruction)
+
+foreign import ccall unsafe "LLVMBuildVAArg" buildVAArg ::
+  Ptr Builder -> Ptr Value -> Ptr Type -> CString -> IO (Ptr Instruction)
+
+foreign import ccall unsafe "LLVMBuildExtractElement" buildExtractElement ::
+  Ptr Builder -> Ptr Value -> Ptr Value -> CString -> IO (Ptr Instruction)
+
+foreign import ccall unsafe "LLVMBuildInsertElement" buildInsertElement ::
+  Ptr Builder -> Ptr Value -> Ptr Value -> Ptr Value -> CString -> IO (Ptr Instruction)
+
+foreign import ccall unsafe "LLVMBuildShuffleVector" buildShuffleVector ::
+  Ptr Builder -> Ptr Value -> Ptr Value -> Ptr Constant -> CString -> IO (Ptr Instruction)
+
+foreign import ccall unsafe "LLVM_Hs_BuildExtractValue" buildExtractValue ::
+  Ptr Builder -> Ptr Value -> Ptr CUInt -> CUInt -> CString -> IO (Ptr Instruction)
+
+foreign import ccall unsafe "LLVM_Hs_BuildInsertValue" buildInsertValue ::
+  Ptr Builder -> Ptr Value -> Ptr Value -> Ptr CUInt -> CUInt -> CString -> IO (Ptr Instruction)
+
+foreign import ccall unsafe "LLVMBuildLandingPad" buildLandingPad' ::
+  Ptr Builder -> Ptr Type -> Ptr Value -> CUInt -> CString -> IO (Ptr Instruction)
+
+-- | The personality should be set via the function
+buildLandingPad :: Ptr Builder -> Ptr Type -> CUInt -> CString -> IO (Ptr Instruction)
+buildLandingPad builder ty numClauses name = buildLandingPad' builder ty nullPtr numClauses name
+
+foreign import ccall unsafe "LLVM_Hs_BuildCleanupPad" buildCleanupPad ::
+  Ptr Builder -> Ptr Value -> Ptr (Ptr Value) -> CUInt -> CString -> IO (Ptr Instruction)
+
+foreign import ccall unsafe "LLVM_Hs_BuildCatchPad" buildCatchPad ::
+  Ptr Builder -> Ptr Value -> Ptr (Ptr Value) -> CUInt -> CString -> IO (Ptr Instruction)
+
+foreign import ccall unsafe "LLVM_Hs_SetFastMathFlags" setFastMathFlags ::
+  Ptr Builder -> FastMathFlags -> IO ()
diff --git a/src/LLVM/Internal/FFI/BuilderC.cpp b/src/LLVM/Internal/FFI/BuilderC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/BuilderC.cpp
@@ -0,0 +1,223 @@
+#define __STDC_LIMIT_MACROS
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/IRBuilder.h"
+
+#include "llvm-c/Core.h"
+
+#include "LLVM/Internal/FFI/Instruction.h"
+#include "LLVM/Internal/FFI/BinaryOperator.h"
+
+using namespace llvm;
+
+namespace llvm {
+static AtomicOrdering unwrap(LLVMAtomicOrdering l) {
+	switch(l) {
+#define ENUM_CASE(x) case LLVMAtomicOrdering ## x: return AtomicOrdering::x;
+LLVM_HS_FOR_EACH_ATOMIC_ORDERING(ENUM_CASE)
+#undef ENUM_CASE
+	default: return AtomicOrdering(0);
+	}
+}
+
+static SynchronizationScope unwrap(LLVMSynchronizationScope l) {
+	switch(l) {
+#define ENUM_CASE(x) case LLVM ## x ## SynchronizationScope: return x;
+LLVM_HS_FOR_EACH_SYNCRONIZATION_SCOPE(ENUM_CASE)
+#undef ENUM_CASE
+	default: return SynchronizationScope(0);
+	}
+}
+
+
+static AtomicRMWInst::BinOp unwrap(LLVMAtomicRMWBinOp l) {
+	switch(l) {
+#define ENUM_CASE(x) case LLVMAtomicRMWBinOp ## x: return AtomicRMWInst::x;
+LLVM_HS_FOR_EACH_RMW_OPERATION(ENUM_CASE)
+#undef ENUM_CASE
+	default: return AtomicRMWInst::BinOp(0);
+	}
+}
+
+static FastMathFlags unwrap(LLVMFastMathFlags f) {
+	FastMathFlags r = FastMathFlags();
+#define ENUM_CASE(x,l) if (f & LLVM ## x) r.set ## x();
+LLVM_HS_FOR_EACH_FAST_MATH_FLAG(ENUM_CASE)
+#undef ENUM_CASE
+	return r;
+}
+
+}
+
+extern "C" {
+
+#define ENUM_CASE(Op)																										\
+LLVMValueRef LLVM_Hs_Build ## Op(																	\
+	LLVMBuilderRef b,																											\
+	LLVMBool nsw,																													\
+	LLVMBool nuw,																													\
+	LLVMValueRef o0,																											\
+	LLVMValueRef o1,																											\
+	const char *s																													\
+) {																																			\
+	return wrap(unwrap(b)->Create ## Op(unwrap(o0), unwrap(o1), s, nuw, nsw)); \
+}
+LLVM_HS_FOR_EACH_OVERFLOWING_BINARY_OPERATOR(ENUM_CASE)
+#undef ENUM_CASE
+
+#define ENUM_CASE(Op)																										\
+LLVMValueRef LLVM_Hs_Build ## Op(																	\
+	LLVMBuilderRef b,																											\
+	LLVMBool exact,																												\
+	LLVMValueRef o0,																											\
+	LLVMValueRef o1,																											\
+	const char *s																													\
+) {																																			\
+	return wrap(unwrap(b)->Create ## Op(unwrap(o0), unwrap(o1), s, exact)); \
+}
+LLVM_HS_FOR_EACH_POSSIBLY_EXACT_BINARY_OPERATOR(ENUM_CASE)
+#undef ENUM_CASE
+
+void LLVM_Hs_SetFastMathFlags(LLVMBuilderRef b, LLVMFastMathFlags f) {
+	unwrap(b)->setFastMathFlags(unwrap(f));
+}
+
+LLVMValueRef LLVM_Hs_BuildLoad(
+	LLVMBuilderRef b,
+	LLVMBool isVolatile,
+	LLVMValueRef p,
+	LLVMAtomicOrdering atomicOrdering,
+	LLVMSynchronizationScope synchScope,
+	unsigned align,
+	const char *name
+) {
+	LoadInst *i = unwrap(b)->CreateAlignedLoad(unwrap(p), align, isVolatile, name);
+	i->setOrdering(unwrap(atomicOrdering));
+	if (atomicOrdering != LLVMAtomicOrderingNotAtomic) i->setSynchScope(unwrap(synchScope));
+	return wrap(i);
+}
+
+LLVMValueRef LLVM_Hs_BuildStore(
+	LLVMBuilderRef b,
+	LLVMBool isVolatile,
+	LLVMValueRef p,
+	LLVMValueRef v,
+	LLVMAtomicOrdering atomicOrdering,
+	LLVMSynchronizationScope synchScope,
+	unsigned align,
+	const char *name
+) {
+	StoreInst *i = unwrap(b)->CreateAlignedStore(unwrap(v), unwrap(p), align, isVolatile);
+	i->setName(name);
+	i->setOrdering(unwrap(atomicOrdering));
+	if (atomicOrdering != LLVMAtomicOrderingNotAtomic) i->setSynchScope(unwrap(synchScope));
+	return wrap(i);
+}
+
+LLVMValueRef LLVM_Hs_BuildFence(
+	LLVMBuilderRef b, LLVMAtomicOrdering lao, LLVMSynchronizationScope lss, const char *name
+) {
+	FenceInst *i = unwrap(b)->CreateFence(unwrap(lao), unwrap(lss));
+	i->setName(name);
+	return wrap(i);
+}
+
+LLVMValueRef LLVM_Hs_BuildAtomicCmpXchg(
+	LLVMBuilderRef b,
+	LLVMBool v,
+	LLVMValueRef ptr, 
+	LLVMValueRef cmp, 
+	LLVMValueRef n, 
+	LLVMAtomicOrdering successOrdering,
+	LLVMAtomicOrdering failureOrdering,
+	LLVMSynchronizationScope lss,
+	const char *name
+) {
+	AtomicCmpXchgInst *a = unwrap(b)->CreateAtomicCmpXchg(
+		unwrap(ptr), unwrap(cmp), unwrap(n), unwrap(successOrdering), unwrap(failureOrdering), unwrap(lss)
+	);
+	a->setVolatile(v);
+	a->setName(name);
+	return wrap(a);
+}
+
+LLVMValueRef LLVM_Hs_BuildAtomicRMW(
+	LLVMBuilderRef b,
+	LLVMBool v,
+	LLVMAtomicRMWBinOp rmwOp,
+	LLVMValueRef ptr, 
+	LLVMValueRef val, 
+	LLVMAtomicOrdering lao,
+	LLVMSynchronizationScope lss,
+	const char *name
+) {
+	AtomicRMWInst *a = unwrap(b)->CreateAtomicRMW(
+		unwrap(rmwOp), unwrap(ptr), unwrap(val), unwrap(lao), unwrap(lss)
+	);
+	a->setVolatile(v);
+	a->setName(name);
+	return wrap(a);
+}
+
+LLVMValueRef LLVM_Hs_BuildExtractValue(
+	LLVMBuilderRef b,
+	LLVMValueRef a,
+	unsigned *idxs,
+	unsigned n,
+	const char *name
+) {
+	return wrap(unwrap(b)->CreateExtractValue(unwrap(a), ArrayRef<unsigned>(idxs, n), name));
+}
+
+LLVMValueRef LLVM_Hs_BuildInsertValue(
+	LLVMBuilderRef b,
+	LLVMValueRef a,
+	LLVMValueRef v,
+	unsigned *idxs,
+	unsigned n,
+	const char *name
+) {
+	return wrap(unwrap(b)->CreateInsertValue(unwrap(a), unwrap(v), ArrayRef<unsigned>(idxs, n), name));
+}
+
+LLVMValueRef LLVM_Hs_BuildCleanupPad(LLVMBuilderRef b, LLVMValueRef parentPad,
+                                     LLVMValueRef *args, unsigned numArgs,
+                                     const char *name) {
+  return wrap(unwrap(b)->CreateCleanupPad(unwrap(parentPad),
+                                          makeArrayRef(unwrap(args), numArgs),
+                                          name));
+}
+
+LLVMValueRef LLVM_Hs_BuildCatchPad(LLVMBuilderRef b, LLVMValueRef catchSwitch,
+                                   LLVMValueRef *args, unsigned numArgs,
+                                   const char *name) {
+    return wrap(unwrap(b)->CreateCatchPad(unwrap(catchSwitch),
+                                          makeArrayRef(unwrap(args), numArgs),
+                                          name));
+}
+
+LLVMValueRef LLVM_Hs_BuildCleanupRet(LLVMBuilderRef b, LLVMValueRef cleanupPad,
+                                     LLVMBasicBlockRef unwindDest) {
+    // Due to the way name resolution works in llvm-hs, cleanupPad might not
+    // actually be a CleanupPadInst. However, it will later be replaced by one.
+    // Pretending that we have one is thus ok here.
+    auto cleanupPad_ = static_cast<CleanupPadInst*>(unwrap<Value>(cleanupPad));
+    return wrap(unwrap(b)->CreateCleanupRet(cleanupPad_,
+                                            unwrap(unwindDest)));
+}
+
+LLVMValueRef LLVM_Hs_BuildCatchRet(LLVMBuilderRef b, LLVMValueRef catchPad,
+                                   LLVMBasicBlockRef successor) {
+    // Due to the way name resolution works in llvm-hs, catchPad might not
+    // actually be a CatchPadInst. However, it will later be replaced by one.
+    // Pretending that we have one is thus ok here.
+    auto catchPad_ = static_cast<CatchPadInst *>(unwrap<Value>(catchPad));
+    return wrap(unwrap(b)->CreateCatchRet(catchPad_, unwrap(successor)));
+}
+
+LLVMValueRef LLVM_Hs_BuildCatchSwitch(LLVMBuilderRef b, LLVMValueRef parentPad,
+                                      LLVMBasicBlockRef unwindDest,
+                                      unsigned numHandlers) {
+    return wrap(unwrap(b)->CreateCatchSwitch(unwrap(parentPad),
+                                             unwrap(unwindDest), numHandlers));
+}
+}
diff --git a/src/LLVM/Internal/FFI/ByteRangeCallback.hs b/src/LLVM/Internal/FFI/ByteRangeCallback.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/ByteRangeCallback.hs
@@ -0,0 +1,14 @@
+{-# LANGUAGE
+  ForeignFunctionInterface
+  #-}
+module LLVM.Internal.FFI.ByteRangeCallback where
+
+import LLVM.Prelude
+
+import Foreign.C
+import Foreign.Ptr
+
+type ByteRangeCallback = Ptr CChar -> CSize -> IO ()
+foreign import ccall "wrapper" wrapByteRangeCallback :: 
+  ByteRangeCallback -> IO (FunPtr ByteRangeCallback)
+
diff --git a/src/LLVM/Internal/FFI/CallingConvention.h b/src/LLVM/Internal/FFI/CallingConvention.h
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/CallingConvention.h
@@ -0,0 +1,35 @@
+#ifndef __LLVM_INTERNAL_FFI__CALLING_CONVENTION__H__
+#define __LLVM_INTERNAL_FFI__CALLING_CONVENTION__H__
+
+#define LLVM_HS_FOR_EACH_CALLING_CONVENTION(macro) \
+  macro(C, 0)                                           \
+  macro(Fast, 8)                                        \
+  macro(Cold, 9)                                        \
+  macro(GHC, 10)                                        \
+  macro(HiPE, 11)                                       \
+  macro(WebKit_JS, 12)                                  \
+  macro(AnyReg, 13)                                     \
+  macro(PreserveMost, 14)                               \
+  macro(PreserveAll, 15)                                \
+  macro(X86_StdCall, 64)                                \
+  macro(X86_FastCall, 65)                               \
+  macro(ARM_APCS, 66)                                   \
+  macro(ARM_AAPCS, 67)                                  \
+  macro(ARM_AAPCS_VFP, 68)                              \
+  macro(MSP430_INTR, 69)                                \
+  macro(X86_ThisCall, 70)                               \
+  macro(PTX_Kernel, 71)                                 \
+  macro(PTX_Device, 72)                                 \
+  macro(SPIR_FUNC, 75)                                  \
+  macro(SPIR_KERNEL, 76)                                \
+  macro(Intel_OCL_BI, 77)                               \
+  macro(X86_64_SysV, 78)                                \
+  macro(X86_64_Win64, 79)
+
+typedef enum {
+#define ENUM_CASE(l,n) LLVM_Hs_CallingConvention_ ## l = n,
+  LLVM_HS_FOR_EACH_CALLING_CONVENTION(ENUM_CASE)
+#undef ENUM_CASE
+} LLVM_Hs_CallingConvention;
+
+#endif
diff --git a/src/LLVM/Internal/FFI/CallingConventionC.cpp b/src/LLVM/Internal/FFI/CallingConventionC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/CallingConventionC.cpp
@@ -0,0 +1,10 @@
+#include "LLVM/Internal/FFI/CallingConvention.h"
+#include "llvm/IR/CallingConv.h"
+
+#define CHECK(l, n)                                                            \
+    static_assert(unsigned(llvm::CallingConv::l) ==                            \
+                      unsigned(LLVM_Hs_CallingConvention_##l),            \
+                  "LLVM_Hs_CallingConvention enum out of sync w/ "        \
+                  "llvm::CallingConv::ID for " #l);
+LLVM_HS_FOR_EACH_CALLING_CONVENTION(CHECK)
+#undef CHECK
diff --git a/src/LLVM/Internal/FFI/Cleanup.hs b/src/LLVM/Internal/FFI/Cleanup.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/Cleanup.hs
@@ -0,0 +1,83 @@
+{-# LANGUAGE
+  TemplateHaskell
+  #-}
+module LLVM.Internal.FFI.Cleanup where
+
+import LLVM.Prelude
+
+import Language.Haskell.TH
+import Data.Sequence as Seq
+
+import Foreign.C
+import Foreign.Ptr
+
+import LLVM.Internal.FFI.LLVMCTypes
+import qualified LLVM.Internal.FFI.PtrHierarchy as FFI
+
+import qualified LLVM.AST.IntegerPredicate as A (IntegerPredicate) 
+import qualified LLVM.AST.FloatingPointPredicate as A (FloatingPointPredicate) 
+import qualified LLVM.AST.Constant as A.C (Constant)
+import qualified LLVM.AST.Operand as A (Operand)
+import qualified LLVM.AST.Type as A (Type)
+import qualified LLVM.AST.Instruction as A (FastMathFlags)
+
+foreignDecl :: String -> String -> [TypeQ] -> TypeQ -> DecsQ
+foreignDecl cName hName argTypeQs returnTypeQ = do
+  let foreignDecl' hName argTypeQs = 
+        forImpD cCall unsafe cName (mkName hName) 
+                  (foldr (\a b -> appT (appT arrowT a) b) (appT (conT ''IO) returnTypeQ) argTypeQs)
+      splitTuples :: [Type] -> Q ([Type], [Pat], [Exp])
+      splitTuples ts = do
+        let f :: Type -> Q (Seq Type, Pat, Seq Exp)
+            f x@(AppT _ _) = maybe (d x) (\q -> q >>= \(ts, ps, es) -> return (ts, TupP (toList ps), es)) (g 0 x)
+            f x = d x
+            g :: Int -> Type -> Maybe (Q (Seq Type, Seq Pat, Seq Exp))
+            g n (TupleT m) | m == n = return (return (Seq.empty, Seq.empty, Seq.empty))
+            g n (AppT a b) = do
+              k <- g (n+1) a
+              return $ do
+                (ts, ps, es) <- k
+                (ts', p', es') <- f b
+                return (ts >< ts', ps |> p', es >< es')
+            g _ _ = Nothing
+            d :: Type -> Q (Seq Type, Pat, Seq Exp)
+            d x = do
+              n <- newName "v"
+              return (Seq.singleton x, VarP n, Seq.singleton (VarE n))
+            seqsToList :: [Seq a] -> [a]
+            seqsToList = toList . foldr (><) Seq.empty
+                
+        (tss, ps, ess) <- liftM unzip3 . mapM f $ ts
+        return (seqsToList tss, ps, seqsToList ess)
+
+                                
+  argTypes <- sequence argTypeQs
+  (ts, ps, es) <- splitTuples argTypes
+  let phName = hName ++ "'"
+  sequence [
+    foreignDecl' phName (map return ts),
+    funD (mkName hName) [
+     clause (map return ps) (normalB (foldl appE (varE (mkName phName)) (map return es))) []
+    ]
+   ]
+
+-- | The LLVM C-API for instructions with boolean flags (e.g. nsw) and is weak, so they get
+-- separated out for different handling. This check is an accurate but crude test for whether
+-- an instruction needs such handling.
+hasFlags :: [Type] -> Bool
+hasFlags = any (== ConT ''Bool)
+
+typeMapping :: Type -> TypeQ
+typeMapping t = case t of
+  ConT h | h == ''Bool -> [t| LLVMBool |]
+         | h == ''Int32 -> [t| CInt |]
+         | h == ''Word32 -> [t| CUInt |]
+         | h == ''String -> [t| CString |]
+         | h == ''A.Operand -> [t| Ptr FFI.Value |]
+         | h == ''A.Type -> [t| Ptr FFI.Type |]
+         | h == ''A.C.Constant -> [t| Ptr FFI.Constant |]
+         | h == ''A.FloatingPointPredicate -> [t| FCmpPredicate |]
+         | h == ''A.IntegerPredicate -> [t| ICmpPredicate |]
+         | h == ''A.FastMathFlags -> [t| FastMathFlags |]
+  AppT ListT x -> foldl1 appT [tupleT 2, [t| CUInt |], appT [t| Ptr |] (typeMapping x)]
+  x -> error $ "type not handled in Cleanup typeMapping: " ++ show x
diff --git a/src/LLVM/Internal/FFI/CommandLine.hs b/src/LLVM/Internal/FFI/CommandLine.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/CommandLine.hs
@@ -0,0 +1,15 @@
+{-# LANGUAGE
+  ForeignFunctionInterface
+  #-}
+module LLVM.Internal.FFI.CommandLine where
+
+import LLVM.Prelude
+
+import Foreign.Ptr
+import Foreign.C
+
+foreign import ccall unsafe "LLVM_Hs_ParseCommandLineOptions" parseCommandLineOptions' ::
+  CUInt -> Ptr (Ptr CChar) -> Ptr CChar -> IO ()
+
+parseCommandLineOptions :: (CUInt, Ptr (Ptr CChar)) -> Ptr CChar -> IO ()
+parseCommandLineOptions = uncurry parseCommandLineOptions'
diff --git a/src/LLVM/Internal/FFI/CommandLineC.cpp b/src/LLVM/Internal/FFI/CommandLineC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/CommandLineC.cpp
@@ -0,0 +1,12 @@
+#define __STDC_LIMIT_MACROS
+#include "llvm/Support/CommandLine.h"
+
+using namespace llvm;
+
+extern "C" {
+
+void LLVM_Hs_ParseCommandLineOptions(unsigned argc, const char * const *argv, const char *overview) {
+	cl::ParseCommandLineOptions(argc, argv, overview);
+}
+
+}
diff --git a/src/LLVM/Internal/FFI/Constant.h b/src/LLVM/Internal/FFI/Constant.h
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/Constant.h
@@ -0,0 +1,20 @@
+#ifndef __LLVM_INTERNAL_FFI__CONSTANT__H__
+#define __LLVM_INTERNAL_FFI__CONSTANT__H__
+
+#define LLVM_HS_FOR_EACH_FLOAT_SEMANTICS(macro) \
+	macro(IEEEhalf) \
+	macro(IEEEsingle) \
+	macro(IEEEdouble) \
+	macro(IEEEquad) \
+	macro(PPCDoubleDouble) \
+	macro(x87DoubleExtended) \
+	macro(Bogus)
+
+typedef enum {
+#define ENUM_CASE(x) LLVMFloatSemantics ## x,
+LLVM_HS_FOR_EACH_FLOAT_SEMANTICS(ENUM_CASE)
+#undef ENUM_CASE
+} LLVMFloatSemantics;
+
+
+#endif
diff --git a/src/LLVM/Internal/FFI/Constant.hs b/src/LLVM/Internal/FFI/Constant.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/Constant.hs
@@ -0,0 +1,145 @@
+{-# LANGUAGE
+  TemplateHaskell,
+  ForeignFunctionInterface,
+  MultiParamTypeClasses,
+  UndecidableInstances,
+  ViewPatterns
+  #-}
+-- | FFI functions for handling the LLVM Constant class
+module LLVM.Internal.FFI.Constant where
+
+import LLVM.Prelude
+
+import qualified Language.Haskell.TH as TH
+import qualified LLVM.Internal.InstructionDefs as ID
+
+import qualified Data.Map as Map
+
+import Foreign.Ptr
+import Foreign.C
+
+import LLVM.Internal.FFI.PtrHierarchy
+import LLVM.Internal.FFI.Context
+import LLVM.Internal.FFI.Cleanup
+import LLVM.Internal.FFI.LLVMCTypes
+
+foreign import ccall unsafe "LLVMIsConstant" isConstant ::
+  Ptr Value -> IO (CUInt)
+
+foreign import ccall unsafe "LLVMIsAConstant" isAConstant ::
+  Ptr Value -> IO (Ptr Constant)
+
+foreign import ccall unsafe "LLVMIsAConstantInt" isAConstantInt ::
+  Ptr Value -> IO (Ptr Constant)
+
+foreign import ccall unsafe "LLVMGetOperand" getConstantOperand ::
+  Ptr Constant -> CUInt -> IO (Ptr Constant)
+
+foreign import ccall unsafe "LLVMIsAConstantPointerNull" isAConstantPointerNull ::
+  Ptr Value -> IO (Ptr Constant)
+
+foreign import ccall unsafe "LLVM_Hs_GetConstantIntWords" getConstantIntWords ::
+  Ptr Constant -> Ptr CUInt -> IO (Ptr Word64)
+
+foreign import ccall unsafe "LLVM_Hs_ConstFloatDoubleValue" constFloatDoubleValue ::
+  Ptr Constant -> IO CDouble
+
+foreign import ccall unsafe "LLVM_Hs_ConstFloatFloatValue" constFloatFloatValue ::
+  Ptr Constant -> IO CFloat
+
+foreign import ccall unsafe "LLVMConstStructInContext" constStructInContext' ::
+  Ptr Context -> Ptr (Ptr Constant) -> CUInt -> LLVMBool -> IO (Ptr Constant)
+
+constStructInContext :: Ptr Context -> (CUInt, Ptr (Ptr Constant)) -> LLVMBool -> IO (Ptr Constant)
+constStructInContext ctx (n, cs) p = constStructInContext' ctx cs n p
+
+foreign import ccall unsafe "LLVMConstNamedStruct" constNamedStruct' ::
+  Ptr Type -> Ptr (Ptr Constant) -> CUInt -> IO (Ptr Constant)
+
+constNamedStruct :: Ptr Type -> (CUInt, Ptr (Ptr Constant)) -> IO (Ptr Constant)
+constNamedStruct ty (n, cs) = constNamedStruct' ty cs n
+
+foreign import ccall unsafe "LLVM_Hs_GetConstantDataSequentialElementAsConstant" getConstantDataSequentialElementAsConstant ::
+  Ptr Constant -> CUInt -> IO (Ptr Constant)
+
+foreign import ccall unsafe "LLVMConstIntOfArbitraryPrecision" constantIntOfArbitraryPrecision' ::
+  Ptr Type -> CUInt -> Ptr Word64 -> IO (Ptr Constant)
+
+constantIntOfArbitraryPrecision :: Ptr Type -> (CUInt, Ptr Word64) -> IO (Ptr Constant)
+constantIntOfArbitraryPrecision t = uncurry (constantIntOfArbitraryPrecision' t)
+
+foreign import ccall unsafe "LLVM_Hs_ConstFloatOfArbitraryPrecision" constantFloatOfArbitraryPrecision ::
+  Ptr Context -> CUInt -> Ptr Word64 -> FloatSemantics -> IO (Ptr Constant)
+
+foreign import ccall unsafe "LLVM_Hs_GetConstantFloatWords" getConstantFloatWords ::
+  Ptr Constant -> Ptr Word64 -> IO ()
+
+foreign import ccall unsafe "LLVMConstVector" constantVector' ::
+  Ptr (Ptr Constant) -> CUInt -> IO (Ptr Constant)
+
+constantVector :: (CUInt, Ptr (Ptr Constant)) -> IO (Ptr Constant)
+constantVector (n, cs) = constantVector' cs n
+
+foreign import ccall unsafe "LLVMConstNull" constantNull ::
+  Ptr Type -> IO (Ptr Constant)
+
+foreign import ccall unsafe "LLVMConstArray" constantArray' ::
+  Ptr Type -> Ptr (Ptr Constant) -> CUInt -> IO (Ptr Constant)
+
+constantArray :: Ptr Type -> (CUInt, Ptr (Ptr Constant)) -> IO (Ptr Constant)
+constantArray t (n, cs) = constantArray' t cs n
+
+foreign import ccall unsafe "LLVM_Hs_ConstCast" constantCast ::
+  CPPOpcode -> Ptr Constant -> Ptr Type -> IO (Ptr Constant)
+
+foreign import ccall unsafe "LLVM_Hs_ConstBinaryOperator" constantBinaryOperator ::
+  CPPOpcode -> Ptr Constant -> Ptr Constant -> IO (Ptr Constant)
+
+$(do
+   let constExprInfo = ID.innerJoin (ID.innerJoin ID.astConstantRecs ID.astInstructionRecs) ID.instructionDefs
+   liftM concat $ sequence $ do
+     (name, ((TH.RecC _ (unzip3 -> (_, _, fieldTypes)),_), ID.InstructionDef { ID.instructionKind = ik })) <- Map.toList constExprInfo
+     prefix <- case ik of
+                 ID.Other -> return "LLVM"
+                 ID.Binary | hasFlags fieldTypes -> return "LLVM_Hs_"
+                 _ -> []
+     return $
+       foreignDecl (prefix ++ "Const" ++ name) ("constant" ++ name) (map typeMapping fieldTypes) [t| Ptr Constant |]
+  )
+
+foreign import ccall unsafe "LLVMConstGEP" constantGetElementPtr' ::
+  Ptr Constant -> Ptr (Ptr Constant) -> CUInt -> IO (Ptr Constant)
+
+foreign import ccall unsafe "LLVMConstInBoundsGEP" constantInBoundsGetElementPtr' ::
+  Ptr Constant -> Ptr (Ptr Constant) -> CUInt -> IO (Ptr Constant)
+
+constantGetElementPtr :: LLVMBool -> Ptr Constant -> (CUInt, Ptr (Ptr Constant)) -> IO (Ptr Constant)
+constantGetElementPtr (LLVMBool ib) a (n, is) =
+  (case ib of { 0 -> constantGetElementPtr'; 1 -> constantInBoundsGetElementPtr' }) a is n
+
+foreign import ccall unsafe "LLVM_Hs_GetConstCPPOpcode" getConstantCPPOpcode ::
+  Ptr Constant -> IO CPPOpcode
+
+foreign import ccall unsafe "LLVM_Hs_GetConstPredicate" getConstantICmpPredicate ::
+  Ptr Constant -> IO ICmpPredicate
+
+foreign import ccall unsafe "LLVM_Hs_GetConstPredicate" getConstantFCmpPredicate ::
+  Ptr Constant -> IO FCmpPredicate
+
+foreign import ccall unsafe "LLVM_Hs_GetConstIndices" getConstantIndices ::
+  Ptr Constant -> Ptr CUInt -> IO (Ptr CUInt)
+
+foreign import ccall unsafe "LLVMGetUndef" constantUndef ::
+  Ptr Type -> IO (Ptr Constant)
+
+foreign import ccall unsafe "LLVMBlockAddress" blockAddress ::
+  Ptr Value -> Ptr BasicBlock -> IO (Ptr Constant)
+
+foreign import ccall unsafe "LLVM_Hs_GetBlockAddressFunction" getBlockAddressFunction ::
+  Ptr Constant -> IO (Ptr Value)
+
+foreign import ccall unsafe "LLVM_Hs_GetBlockAddressBlock" getBlockAddressBlock ::
+  Ptr Constant -> IO (Ptr BasicBlock)
+
+foreign import ccall unsafe "LLVM_Hs_GetConstTokenNone" getConstTokenNone ::
+  Ptr Context -> IO (Ptr Constant)
diff --git a/src/LLVM/Internal/FFI/ConstantC.cpp b/src/LLVM/Internal/FFI/ConstantC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/ConstantC.cpp
@@ -0,0 +1,114 @@
+#define __STDC_LIMIT_MACROS
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/Function.h"
+
+#include "llvm-c/Core.h"
+#include "LLVM/Internal/FFI/Value.h"
+#include "LLVM/Internal/FFI/Constant.h"
+#include "LLVM/Internal/FFI/BinaryOperator.h"
+
+using namespace llvm;
+
+namespace llvm {
+
+static const struct fltSemantics &unwrap(LLVMFloatSemantics s) {
+	switch(s) {
+#define ENUM_CASE(x) case LLVMFloatSemantics ## x: return APFloat::x();
+LLVM_HS_FOR_EACH_FLOAT_SEMANTICS(ENUM_CASE)
+#undef ENUM_CASE
+    default: return APFloat::Bogus();
+	}
+}
+
+}
+
+extern "C" {
+
+LLVMValueRef LLVM_Hs_GetConstantDataSequentialElementAsConstant(LLVMValueRef v, unsigned i) {
+	return wrap(unwrap<ConstantDataSequential>(v)->getElementAsConstant(i));
+}
+
+LLVMValueRef LLVM_Hs_GetBlockAddressFunction(LLVMValueRef v) {
+	return wrap(unwrap<BlockAddress>(v)->getFunction());
+}
+
+LLVMBasicBlockRef LLVM_Hs_GetBlockAddressBlock(LLVMValueRef v) {
+	return wrap(unwrap<BlockAddress>(v)->getBasicBlock());
+}
+
+double LLVM_Hs_ConstFloatDoubleValue(LLVMValueRef v) {
+	return unwrap<ConstantFP>(v)->getValueAPF().convertToDouble();
+}
+
+float LLVM_Hs_ConstFloatFloatValue(LLVMValueRef v) {
+	return unwrap<ConstantFP>(v)->getValueAPF().convertToFloat();
+}
+
+LLVMValueRef LLVM_Hs_ConstCast(unsigned opcode, LLVMValueRef v, LLVMTypeRef t) {
+	return wrap(ConstantExpr::getCast(opcode, unwrap<Constant>(v), unwrap(t)));
+}
+
+LLVMValueRef LLVM_Hs_ConstBinaryOperator(unsigned opcode, LLVMValueRef o0, LLVMValueRef o1) {
+	return wrap(ConstantExpr::get(opcode, unwrap<Constant>(o0), unwrap<Constant>(o1)));
+}
+
+#define CASE_CODE(op)																										\
+LLVMValueRef LLVM_Hs_Const ## op(unsigned nsw, unsigned nuw, LLVMValueRef o0, LLVMValueRef o1) { \
+	return wrap(ConstantExpr::get ## op(unwrap<Constant>(o0), unwrap<Constant>(o1), nuw != 0, nsw != 0)); \
+}
+LLVM_HS_FOR_EACH_OVERFLOWING_BINARY_OPERATOR(CASE_CODE)
+#undef CASE_CODE
+
+#define CASE_CODE(op)																										\
+LLVMValueRef LLVM_Hs_Const ## op(unsigned isExact, LLVMValueRef o0, LLVMValueRef o1) {	\
+	return wrap(ConstantExpr::get ## op(unwrap<Constant>(o0), unwrap<Constant>(o1), isExact != 0));	\
+}
+LLVM_HS_FOR_EACH_POSSIBLY_EXACT_BINARY_OPERATOR(CASE_CODE)
+#undef CASE_CODE
+
+unsigned LLVM_Hs_GetConstCPPOpcode(LLVMValueRef v) {
+	return unwrap<ConstantExpr>(v)->getOpcode();
+}
+
+unsigned LLVM_Hs_GetConstPredicate(LLVMValueRef v) {
+	return unwrap<ConstantExpr>(v)->getPredicate();
+}
+
+const unsigned *LLVM_Hs_GetConstIndices(LLVMValueRef v, unsigned *n) {
+	ArrayRef<unsigned> r = unwrap<ConstantExpr>(v)->getIndices();
+	*n = r.size();
+	return r.data();
+}
+
+const uint64_t *LLVM_Hs_GetConstantIntWords(LLVMValueRef v, unsigned *n) {
+	const APInt &i = unwrap<ConstantInt>(v)->getValue();
+	*n = i.getNumWords();
+	return i.getRawData();
+}
+
+LLVMValueRef LLVM_Hs_ConstFloatOfArbitraryPrecision(
+	LLVMContextRef c,
+	unsigned bits,
+	const uint64_t *words,
+	LLVMFloatSemantics semantics
+) {
+	return wrap(
+		ConstantFP::get(
+			*unwrap(c),
+			APFloat(unwrap(semantics), APInt(bits, ArrayRef<uint64_t>(words, (bits-1)/64 + 1)))
+		)
+	);
+}
+
+void LLVM_Hs_GetConstantFloatWords(LLVMValueRef v, uint64_t *bits) {
+	APInt a = unwrap<ConstantFP>(v)->getValueAPF().bitcastToAPInt();
+	for(unsigned i=0; i != a.getNumWords(); ++i) bits[i] = a.getRawData()[i];
+}
+
+LLVMValueRef LLVM_Hs_GetConstTokenNone(LLVMContextRef context) {
+	return wrap(ConstantTokenNone::get(*unwrap(context)));
+}
+
+
+}
diff --git a/src/LLVM/Internal/FFI/Context.hs b/src/LLVM/Internal/FFI/Context.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/Context.hs
@@ -0,0 +1,28 @@
+{-# LANGUAGE
+  ForeignFunctionInterface
+  #-}
+
+-- | Functions for handling the LLVMContext class. In all other LLVM interfaces,
+-- | prefer the newer explicitly thread-aware variants which use contexts
+-- | over corresponding older variants which implicitly reference a global context.
+-- | This choice allows multiple threads to do independent work with LLVM safely.
+module LLVM.Internal.FFI.Context where
+
+import LLVM.Prelude
+
+import Foreign.Ptr
+
+-- | a blind type to correspond to LLVMContext
+data Context
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreContext.html#gaac4f39a2d0b9735e64ac7681ab543b4c>
+foreign import ccall unsafe "LLVMContextCreate" contextCreate ::
+    IO (Ptr Context)
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreContext.html#ga0055cde9a9b2497b332d639d8844a810>
+foreign import ccall unsafe "LLVMGetGlobalContext" getGlobalContext ::
+    IO (Ptr Context)
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreContext.html#ga9cf8b0fb4a546d4cdb6f64b8055f5f57>
+foreign import ccall unsafe "LLVMContextDispose" contextDispose ::
+    Ptr Context -> IO ()
diff --git a/src/LLVM/Internal/FFI/DataLayout.hs b/src/LLVM/Internal/FFI/DataLayout.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/DataLayout.hs
@@ -0,0 +1,26 @@
+{-# LANGUAGE
+  ForeignFunctionInterface
+  #-}
+
+module LLVM.Internal.FFI.DataLayout where
+
+import LLVM.Prelude
+
+import Foreign.C.String
+import Foreign.Ptr
+
+import LLVM.Internal.FFI.LLVMCTypes
+
+data DataLayout
+
+-- Oooh those wacky LLVM C-API coders: C API called DataLayout TargetData.
+-- Great. Just great.
+
+foreign import ccall unsafe "LLVMCreateTargetData" createDataLayout :: 
+  CString -> IO (Ptr DataLayout)
+
+foreign import ccall unsafe "LLVMDisposeTargetData" disposeDataLayout :: 
+  Ptr DataLayout -> IO ()
+
+foreign import ccall unsafe "LLVMCopyStringRepOfTargetData" dataLayoutToString ::
+  Ptr DataLayout -> IO (OwnerTransfered CString)
diff --git a/src/LLVM/Internal/FFI/ExecutionEngine.hs b/src/LLVM/Internal/FFI/ExecutionEngine.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/ExecutionEngine.hs
@@ -0,0 +1,71 @@
+{-# LANGUAGE
+  ForeignFunctionInterface
+  #-}
+
+module LLVM.Internal.FFI.ExecutionEngine where
+
+import LLVM.Prelude
+
+import Foreign.Ptr
+import Foreign.C
+
+import LLVM.Internal.FFI.PtrHierarchy
+import LLVM.Internal.FFI.Module
+import LLVM.Internal.FFI.LLVMCTypes
+
+data ExecutionEngine
+
+foreign import ccall unsafe "LLVMCreateExecutionEngineForModule" createExecutionEngineForModule ::
+  Ptr (Ptr ExecutionEngine) -> Ptr Module -> Ptr (OwnerTransfered CString) -> IO CUInt
+
+foreign import ccall unsafe "LLVMCreateInterpreterForModule" createInterpreterForModule ::
+  Ptr (Ptr ExecutionEngine) -> Ptr Module -> Ptr (OwnerTransfered CString) -> IO CUInt
+
+foreign import ccall unsafe "LLVMCreateJITCompilerForModule" createJITCompilerForModule ::
+  Ptr (Ptr ExecutionEngine) -> Ptr Module -> CUInt -> Ptr (OwnerTransfered CString) -> IO CUInt
+
+foreign import ccall unsafe "LLVMCreateMCJITCompilerForModule" createMCJITCompilerForModule ::
+  Ptr (Ptr ExecutionEngine) -> Ptr Module -> Ptr MCJITCompilerOptions -> CSize -> Ptr (OwnerTransfered CString) -> IO CUInt
+
+foreign import ccall unsafe "LLVMDisposeExecutionEngine" disposeExecutionEngine ::
+  Ptr ExecutionEngine -> IO ()
+
+foreign import ccall unsafe "LLVMAddModule" addModule ::
+  Ptr ExecutionEngine -> Ptr Module -> IO ()
+
+foreign import ccall unsafe "LLVMRemoveModule" removeModule ::
+  Ptr ExecutionEngine -> Ptr Module -> Ptr (Ptr Module) -> Ptr CString -> IO CUInt
+
+foreign import ccall unsafe "LLVMFindFunction" findFunction ::
+  Ptr ExecutionEngine -> CString -> Ptr (Ptr Function) -> IO CUInt
+
+foreign import ccall unsafe "LLVMGetPointerToGlobal" getPointerToGlobal ::
+  Ptr ExecutionEngine -> Ptr GlobalValue -> IO (Ptr ())
+
+foreign import ccall unsafe "LLVMLinkInInterpreter" linkInInterpreter :: 
+  IO ()
+
+foreign import ccall unsafe "LLVMLinkInMCJIT" linkInMCJIT :: 
+  IO ()
+
+data MCJITCompilerOptions
+
+foreign import ccall unsafe "LLVM_Hs_GetMCJITCompilerOptionsSize" getMCJITCompilerOptionsSize ::
+  IO CSize
+
+foreign import ccall unsafe "LLVMInitializeMCJITCompilerOptions" initializeMCJITCompilerOptions ::
+  Ptr MCJITCompilerOptions -> CSize -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_SetMCJITCompilerOptionsOptLevel" setMCJITCompilerOptionsOptLevel ::
+  Ptr MCJITCompilerOptions -> CUInt -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_SetMCJITCompilerOptionsCodeModel" setMCJITCompilerOptionsCodeModel ::
+  Ptr MCJITCompilerOptions -> CodeModel -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_SetMCJITCompilerOptionsNoFramePointerElim" setMCJITCompilerOptionsNoFramePointerElim ::
+  Ptr MCJITCompilerOptions -> LLVMBool -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_SetMCJITCompilerOptionsEnableFastISel" setMCJITCompilerOptionsEnableFastISel ::
+  Ptr MCJITCompilerOptions -> LLVMBool -> IO ()
+
+
diff --git a/src/LLVM/Internal/FFI/ExecutionEngineC.cpp b/src/LLVM/Internal/FFI/ExecutionEngineC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/ExecutionEngineC.cpp
@@ -0,0 +1,30 @@
+#define __STDC_LIMIT_MACROS
+#include "llvm/IR/LLVMContext.h"
+#include "llvm-c/ExecutionEngine.h"
+
+using namespace llvm;
+
+extern "C" {
+
+size_t LLVM_Hs_GetMCJITCompilerOptionsSize() {
+  return sizeof(struct LLVMMCJITCompilerOptions);
+}
+
+void LLVM_Hs_SetMCJITCompilerOptionsOptLevel(struct LLVMMCJITCompilerOptions *o, unsigned x) {
+  o->OptLevel = x;
+}
+
+void LLVM_Hs_SetMCJITCompilerOptionsCodeModel(struct LLVMMCJITCompilerOptions *o, LLVMCodeModel x) {
+  o->CodeModel = x;
+}
+
+void LLVM_Hs_SetMCJITCompilerOptionsNoFramePointerElim(struct LLVMMCJITCompilerOptions *o, LLVMBool x) {
+  o->NoFramePointerElim = x;
+}
+
+void LLVM_Hs_SetMCJITCompilerOptionsEnableFastISel(struct LLVMMCJITCompilerOptions *o, LLVMBool x) {
+  o->EnableFastISel = x;
+}
+
+}
+
diff --git a/src/LLVM/Internal/FFI/Function.hs b/src/LLVM/Internal/FFI/Function.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/Function.hs
@@ -0,0 +1,72 @@
+{-# LANGUAGE
+  ForeignFunctionInterface,
+  MultiParamTypeClasses
+  #-}
+
+module LLVM.Internal.FFI.Function where
+
+import LLVM.Prelude
+
+import Foreign.Ptr
+import Foreign.C
+
+import LLVM.Internal.FFI.Attribute
+import LLVM.Internal.FFI.Context
+import LLVM.Internal.FFI.LLVMCTypes
+import LLVM.Internal.FFI.PtrHierarchy
+
+foreign import ccall unsafe "LLVMGetFunctionCallConv" getFunctionCallingConvention ::
+  Ptr Function -> IO CallingConvention
+
+foreign import ccall unsafe "LLVMSetFunctionCallConv" setFunctionCallingConvention ::
+  Ptr Function -> CallingConvention -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_GetFunctionMixedAttributeSet" getMixedAttributeSet ::
+  Ptr Function -> IO MixedAttributeSet
+
+foreign import ccall unsafe "LLVM_Hs_SetFunctionMixedAttributeSet" setMixedAttributeSet ::
+  Ptr Function -> MixedAttributeSet -> IO ()
+
+foreign import ccall unsafe "LLVMGetFirstBasicBlock" getFirstBasicBlock ::
+  Ptr Function -> IO (Ptr BasicBlock)
+
+foreign import ccall unsafe "LLVMGetLastBasicBlock" getLastBasicBlock ::
+  Ptr Function -> IO (Ptr BasicBlock)
+
+foreign import ccall unsafe "LLVMGetNextBasicBlock" getNextBasicBlock ::
+  Ptr BasicBlock -> IO (Ptr BasicBlock)
+
+foreign import ccall unsafe "LLVMAppendBasicBlockInContext" appendBasicBlockInContext ::
+  Ptr Context -> Ptr Function -> CString -> IO (Ptr BasicBlock)
+
+
+foreign import ccall unsafe "LLVMCountParams" countParams ::
+  Ptr Function -> IO CUInt
+
+foreign import ccall unsafe "LLVMGetParams" getParams ::
+  Ptr Function -> Ptr (Ptr Parameter) -> IO ()
+
+foreign import ccall unsafe "LLVMGetGC" getGC ::
+  Ptr Function -> IO CString
+
+foreign import ccall unsafe "LLVMSetGC" setGC ::
+  Ptr Function -> CString -> IO ()
+
+
+foreign import ccall unsafe "LLVM_Hs_HasFunctionPrefixData" hasPrefixData ::
+  Ptr Function -> IO LLVMBool
+
+foreign import ccall unsafe "LLVM_Hs_GetFunctionPrefixData" getPrefixData ::
+  Ptr Function -> IO (Ptr Constant)
+
+foreign import ccall unsafe "LLVM_Hs_SetFunctionPrefixData" setPrefixData ::
+  Ptr Function -> Ptr Constant -> IO ()
+
+foreign import ccall unsafe "LLVMHasPersonalityFn" hasPersonalityFn ::
+  Ptr Function -> IO LLVMBool
+
+foreign import ccall unsafe "LLVM_Hs_SetPersonalityFn" setPersonalityFn ::
+  Ptr Function -> Ptr Constant -> IO ()
+
+foreign import ccall unsafe "LLVMGetPersonalityFn" getPersonalityFn ::
+  Ptr Function -> IO (Ptr Constant)
diff --git a/src/LLVM/Internal/FFI/FunctionC.cpp b/src/LLVM/Internal/FFI/FunctionC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/FunctionC.cpp
@@ -0,0 +1,41 @@
+#define __STDC_LIMIT_MACROS
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Attributes.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/Value.h"
+
+#include "llvm-c/Core.h"
+#include "LLVM/Internal/FFI/AttributeC.hpp"
+
+using namespace llvm;
+
+extern "C" {
+
+const AttributeSetImpl *LLVM_Hs_GetFunctionMixedAttributeSet(LLVMValueRef f) {
+	return wrap(unwrap<Function>(f)->getAttributes());
+}
+
+void LLVM_Hs_SetFunctionMixedAttributeSet(LLVMValueRef f, AttributeSetImpl *asi) {
+	unwrap<Function>(f)->setAttributes(unwrap(asi));
+}
+
+LLVMBool LLVM_Hs_HasFunctionPrefixData(LLVMValueRef f) {
+  return unwrap<Function>(f)->hasPrefixData();
+}
+
+LLVMValueRef LLVM_Hs_GetFunctionPrefixData(LLVMValueRef f) {
+  return wrap(unwrap<Function>(f)->getPrefixData());
+}
+
+void LLVM_Hs_SetFunctionPrefixData(LLVMValueRef f, LLVMValueRef p) {
+  unwrap<Function>(f)->setPrefixData(unwrap<Constant>(p));
+}
+
+// This wrapper is necessary because LLVMSetPersonalityFn fails if
+// personalityFn is a nullptr even though the C++ API allows that.
+void LLVM_Hs_SetPersonalityFn(LLVMValueRef fn, LLVMValueRef personalityFn) {
+    unwrap<Function>(fn)->setPersonalityFn(personalityFn == nullptr ?
+                                           nullptr : unwrap<Constant>(personalityFn));
+}
+
+}
diff --git a/src/LLVM/Internal/FFI/GlobalAlias.hs b/src/LLVM/Internal/FFI/GlobalAlias.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/GlobalAlias.hs
@@ -0,0 +1,26 @@
+{-# LANGUAGE
+  ForeignFunctionInterface,
+  MultiParamTypeClasses,
+  UndecidableInstances
+  #-}
+-- | FFI functions for handling the LLVM GlobalAlias class
+module LLVM.Internal.FFI.GlobalAlias where
+
+import LLVM.Prelude
+
+import Foreign.Ptr
+
+import LLVM.Internal.FFI.PtrHierarchy
+
+-- | test if a 'Value' is a 'GlobalAlias'
+foreign import ccall unsafe "LLVMIsAGlobalAlias" isAGlobalAlias ::
+    Ptr Value -> IO (Ptr GlobalAlias)
+
+-- | get the constant aliased by this alias
+foreign import ccall unsafe "LLVM_Hs_GetAliasee" getAliasee ::
+    Ptr GlobalAlias -> IO (Ptr Constant)
+
+-- | set the constant aliased by this alias
+foreign import ccall unsafe "LLVM_Hs_SetAliasee" setAliasee ::
+    Ptr GlobalAlias -> Ptr Constant -> IO ()
+
diff --git a/src/LLVM/Internal/FFI/GlobalAliasC.cpp b/src/LLVM/Internal/FFI/GlobalAliasC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/GlobalAliasC.cpp
@@ -0,0 +1,22 @@
+#define __STDC_LIMIT_MACROS
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/GlobalAlias.h"
+#include "llvm/IR/GlobalObject.h"
+
+#include "llvm-c/Core.h"
+
+#include <iostream>
+
+using namespace llvm;
+
+extern "C" {
+
+LLVMValueRef LLVM_Hs_GetAliasee(LLVMValueRef g) {
+	return wrap(unwrap<GlobalAlias>(g)->getAliasee());
+}
+
+void LLVM_Hs_SetAliasee(LLVMValueRef g, LLVMValueRef c) {
+	unwrap<GlobalAlias>(g)->setAliasee(unwrap<Constant>(c));
+}
+
+}
diff --git a/src/LLVM/Internal/FFI/GlobalValue.h b/src/LLVM/Internal/FFI/GlobalValue.h
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/GlobalValue.h
@@ -0,0 +1,58 @@
+#ifndef __LLVM_INTERNAL_FFI__GLOBAL_VALUE__H__
+#define __LLVM_INTERNAL_FFI__GLOBAL_VALUE__H__
+
+#define LLVM_HS_FOR_EACH_LINKAGE(macro)		\
+	macro(External)																\
+	macro(AvailableExternally)										\
+	macro(LinkOnceAny)														\
+	macro(LinkOnceODR)														\
+	macro(WeakAny)																\
+	macro(WeakODR)																\
+	macro(Appending)															\
+	macro(Internal)																\
+	macro(Private)																\
+	macro(ExternalWeak)														\
+	macro(Common)
+
+#define LLVM_HS_FOR_EACH_VISIBILITY(macro)	\
+	macro(Default)																\
+	macro(Hidden)																	\
+	macro(Protected)															\
+
+#define LLVM_HS_FOR_EACH_COMDAT_SELECTION_KIND(macro)	\
+	macro(Any)                                                \
+	macro(ExactMatch)                                         \
+	macro(Largest)                                            \
+	macro(NoDuplicates)                                       \
+	macro(SameSize)
+
+typedef enum {
+#define ENUM_CASE(n) LLVM_Hs_COMDAT_Selection_Kind_ ## n,
+LLVM_HS_FOR_EACH_COMDAT_SELECTION_KIND(ENUM_CASE)
+#undef ENUM_CASE
+} LLVM_Hs_COMDAT_Selection_Kind;
+
+#define LLVM_HS_FOR_EACH_DLL_STORAGE_CLASS(macro)	\
+	macro(Default)                                        \
+	macro(DLLImport)                                      \
+	macro(DLLExport)
+
+#define LLVM_HS_FOR_EACH_THREAD_LOCAL_MODE(macro)  \
+	macro(NotThreadLocal)                                 \
+	macro(GeneralDynamicTLSModel)                         \
+	macro(LocalDynamicTLSModel)                           \
+	macro(InitialExecTLSModel)                            \
+	macro(LocalExecTLSModel)
+
+#define LLVM_HS_FOR_EACH_UNNAMED_ADDR(macro) \
+	macro(None)                                   \
+	macro(Local)                                  \
+	macro(Global)
+
+typedef enum {
+#define ENUM_CASE(x) LLVMUnnamedAddr ## x,
+LLVM_HS_FOR_EACH_UNNAMED_ADDR(ENUM_CASE)
+#undef ENUM_CASE
+} LLVMUnnamedAddr;
+
+#endif
diff --git a/src/LLVM/Internal/FFI/GlobalValue.hs b/src/LLVM/Internal/FFI/GlobalValue.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/GlobalValue.hs
@@ -0,0 +1,79 @@
+{-# LANGUAGE
+  ForeignFunctionInterface,
+  MultiParamTypeClasses,
+  UndecidableInstances
+  #-}
+-- | FFI functions for handling the LLVM GlobalValue class
+module LLVM.Internal.FFI.GlobalValue where
+
+import LLVM.Prelude
+
+import Foreign.Ptr
+import Foreign.C
+
+import LLVM.Internal.FFI.PtrHierarchy
+import LLVM.Internal.FFI.LLVMCTypes
+
+data COMDAT
+
+foreign import ccall unsafe "LLVMIsAGlobalValue" isAGlobalValue ::
+  Ptr Value -> IO (Ptr GlobalValue)
+
+foreign import ccall unsafe "LLVMGetLinkage" getLinkage ::
+  Ptr GlobalValue -> IO Linkage
+
+foreign import ccall unsafe "LLVMSetLinkage" setLinkage ::
+  Ptr GlobalValue -> Linkage -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_GetSection" getSection ::
+  Ptr GlobalValue -> Ptr CSize -> IO CString
+
+foreign import ccall unsafe "LLVMSetSection" setSection ::
+  Ptr GlobalValue -> CString -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_GetCOMDAT" getCOMDAT ::
+  Ptr GlobalValue -> IO (Ptr COMDAT)
+
+foreign import ccall unsafe "LLVM_Hs_SetCOMDAT" setCOMDAT ::
+  Ptr GlobalObject -> Ptr COMDAT -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_GetCOMDATName" getCOMDATName ::
+  Ptr COMDAT -> Ptr CSize -> IO (Ptr CChar)
+
+foreign import ccall unsafe "LLVM_Hs_GetCOMDATSelectionKind" getCOMDATSelectionKind ::
+  Ptr COMDAT -> IO COMDATSelectionKind
+
+foreign import ccall unsafe "LLVM_Hs_SetCOMDATSelectionKind" setCOMDATSelectionKind ::
+  Ptr COMDAT -> COMDATSelectionKind -> IO ()
+
+foreign import ccall unsafe "LLVMGetVisibility" getVisibility ::
+  Ptr GlobalValue -> IO Visibility
+
+foreign import ccall unsafe "LLVMSetVisibility" setVisibility ::
+  Ptr GlobalValue -> Visibility -> IO ()
+
+foreign import ccall unsafe "LLVMGetDLLStorageClass" getDLLStorageClass ::
+  Ptr GlobalValue -> IO DLLStorageClass
+
+foreign import ccall unsafe "LLVMSetDLLStorageClass" setDLLStorageClass ::
+  Ptr GlobalValue -> DLLStorageClass -> IO ()
+
+foreign import ccall unsafe "LLVMGetAlignment" getAlignment ::
+  Ptr GlobalValue -> IO CUInt
+
+foreign import ccall unsafe "LLVMSetAlignment" setAlignment ::
+  Ptr GlobalValue -> CUInt -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_GetUnnamedAddr" getUnnamedAddr ::
+  Ptr GlobalValue -> IO UnnamedAddr
+
+foreign import ccall unsafe "LLVM_Hs_SetUnnamedAddr" setUnnamedAddr ::
+  Ptr GlobalValue -> UnnamedAddr -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_GetThreadLocalMode" getThreadLocalMode ::
+  Ptr GlobalValue -> IO ThreadLocalMode
+
+foreign import ccall unsafe "LLVM_Hs_SetThreadLocalMode" setThreadLocalMode ::
+  Ptr GlobalValue -> ThreadLocalMode -> IO ()
+
+
diff --git a/src/LLVM/Internal/FFI/GlobalValueC.cpp b/src/LLVM/Internal/FFI/GlobalValueC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/GlobalValueC.cpp
@@ -0,0 +1,87 @@
+#define __STDC_LIMIT_MACROS
+#include "llvm/IR/Comdat.h"
+#include "llvm/IR/GlobalValue.h"
+#include "llvm/IR/GlobalObject.h"
+#include "llvm-c/Core.h"
+#include "LLVM/Internal/FFI/GlobalValue.h"
+
+using namespace llvm;
+
+extern "C" {
+
+const Comdat *LLVM_Hs_GetCOMDAT(LLVMValueRef globalVal) {
+  return unwrap<GlobalValue>(globalVal)->getComdat();
+}
+
+void LLVM_Hs_SetCOMDAT(LLVMValueRef globalObj, Comdat *comdat) {
+  return unwrap<GlobalObject>(globalObj)->setComdat(comdat);
+}
+
+const char *LLVM_Hs_GetCOMDATName(const Comdat &comdat, size_t &size) {
+  StringRef ref = comdat.getName();
+  size = ref.size();
+  return ref.data();
+}
+
+#define ENUM_CASE(n)                                                           \
+    static_assert(unsigned(Comdat::n) ==                                       \
+                      unsigned(LLVM_Hs_COMDAT_Selection_Kind_##n),        \
+                  "COMDAT SelectionKind Enum mismatch");
+LLVM_HS_FOR_EACH_COMDAT_SELECTION_KIND(ENUM_CASE)
+#undef ENUM_CASE
+
+const char* LLVM_Hs_GetSection(LLVMValueRef globalVal, size_t* strLength) {
+    const auto& section = unwrap<GlobalValue>(globalVal)->getSection();
+    *strLength = section.size();
+    return section.data();
+}
+
+unsigned LLVM_Hs_GetCOMDATSelectionKind(const Comdat &comdat) {
+  return unsigned(comdat.getSelectionKind());
+}
+
+void LLVM_Hs_SetCOMDATSelectionKind(Comdat &comdat, unsigned csk) {
+  comdat.setSelectionKind(Comdat::SelectionKind(csk));
+}
+
+static LLVMUnnamedAddr unwrap(GlobalValue::UnnamedAddr a) {
+    switch (a) {
+#define ENUM_CASE(x) case GlobalValue::UnnamedAddr::x: return LLVMUnnamedAddr ## x;
+LLVM_HS_FOR_EACH_UNNAMED_ADDR(ENUM_CASE)
+#undef ENUM_CASE
+    default: return LLVMUnnamedAddrNone;
+    }
+}
+
+static GlobalValue::UnnamedAddr wrap(LLVMUnnamedAddr a) {
+    switch (a) {
+#define ENUM_CASE(x) case LLVMUnnamedAddr ## x: return GlobalValue::UnnamedAddr::x;
+LLVM_HS_FOR_EACH_UNNAMED_ADDR(ENUM_CASE)
+#undef ENUM_CASE
+    default: return GlobalValue::UnnamedAddr::None;
+    }
+}
+
+LLVMUnnamedAddr LLVM_Hs_GetUnnamedAddr(LLVMValueRef globalVal) {
+    return unwrap(unwrap<GlobalValue>(globalVal)->getUnnamedAddr());
+}
+
+void LLVM_Hs_SetUnnamedAddr(LLVMValueRef globalVal, LLVMUnnamedAddr attr) {
+    unwrap<GlobalValue>(globalVal)->setUnnamedAddr(wrap(attr));
+}
+
+#define ENUM_CASE(n)                                                           \
+    static_assert(unsigned(GlobalValue::n) == unsigned(LLVM##n),               \
+                  "TLS Model Enum mismatch");
+LLVM_HS_FOR_EACH_THREAD_LOCAL_MODE(ENUM_CASE)
+#undef ENUM_CASE
+
+LLVMThreadLocalMode LLVM_Hs_GetThreadLocalMode(LLVMValueRef globalVal) {
+    return LLVMThreadLocalMode(unwrap<GlobalValue>(globalVal)->getThreadLocalMode());
+}
+
+void LLVM_Hs_SetThreadLocalMode(LLVMValueRef globalVal, LLVMThreadLocalMode mode) {
+    unwrap<GlobalValue>(globalVal)->setThreadLocalMode(GlobalValue::ThreadLocalMode(mode));
+}
+
+}
diff --git a/src/LLVM/Internal/FFI/GlobalVariable.hs b/src/LLVM/Internal/FFI/GlobalVariable.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/GlobalVariable.hs
@@ -0,0 +1,31 @@
+{-# LANGUAGE
+  ForeignFunctionInterface,
+  MultiParamTypeClasses,
+  UndecidableInstances
+  #-}
+-- | FFI functions for handling the LLVM GlobalVariable class
+module LLVM.Internal.FFI.GlobalVariable where
+
+import LLVM.Prelude
+
+import Foreign.Ptr
+import Foreign.C
+
+import LLVM.Internal.FFI.PtrHierarchy
+import LLVM.Internal.FFI.LLVMCTypes
+
+foreign import ccall unsafe "LLVMIsAGlobalVariable" isAGlobalVariable ::
+    Ptr Value -> IO (Ptr GlobalVariable)
+
+foreign import ccall unsafe "LLVMIsGlobalConstant" isGlobalConstant ::
+    Ptr GlobalVariable -> IO LLVMBool
+
+foreign import ccall unsafe "LLVMSetGlobalConstant" setGlobalConstant ::
+    Ptr GlobalVariable -> LLVMBool -> IO ()
+
+foreign import ccall unsafe "LLVMGetInitializer" getInitializer ::
+    Ptr GlobalVariable -> IO (Ptr Constant)
+
+foreign import ccall unsafe "LLVMSetInitializer" setInitializer ::
+    Ptr GlobalVariable -> Ptr Constant -> IO ()
+
diff --git a/src/LLVM/Internal/FFI/InlineAssembly.h b/src/LLVM/Internal/FFI/InlineAssembly.h
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/InlineAssembly.h
@@ -0,0 +1,14 @@
+#ifndef __LLVM_INTERNAL_FFI__INLINE_ASSEMBLY__H__
+#define __LLVM_INTERNAL_FFI__INLINE_ASSEMBLY__H__
+
+#define LLVM_HS_FOR_EACH_ASM_DIALECT(macro) \
+	macro(ATT) \
+	macro(Intel) \
+
+typedef enum {
+#define ENUM_CASE(d) LLVMAsmDialect_ ## d,
+	LLVM_HS_FOR_EACH_ASM_DIALECT(ENUM_CASE)
+#undef ENUM_CASE
+} LLVMAsmDialect;
+
+#endif
diff --git a/src/LLVM/Internal/FFI/InlineAssembly.hs b/src/LLVM/Internal/FFI/InlineAssembly.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/InlineAssembly.hs
@@ -0,0 +1,35 @@
+{-# LANGUAGE
+  ForeignFunctionInterface
+  #-}
+
+module LLVM.Internal.FFI.InlineAssembly where
+
+import LLVM.Prelude
+
+import Foreign.C
+import Foreign.Ptr
+
+import LLVM.Internal.FFI.LLVMCTypes
+import LLVM.Internal.FFI.PtrHierarchy
+
+foreign import ccall unsafe "LLVMIsAInlineAsm" isAInlineAsm ::
+  Ptr Value -> IO (Ptr InlineAsm)
+
+foreign import ccall unsafe "LLVM_Hs_CreateInlineAsm" createInlineAsm ::
+  Ptr Type -> CString -> CString -> LLVMBool -> LLVMBool -> AsmDialect -> IO (Ptr InlineAsm)
+
+foreign import ccall unsafe "LLVM_Hs_GetInlineAsmAsmString" getInlineAsmAssemblyString ::
+  Ptr InlineAsm -> IO CString
+
+foreign import ccall unsafe "LLVM_Hs_GetInlineAsmConstraintString" getInlineAsmConstraintString ::
+  Ptr InlineAsm -> IO CString
+
+foreign import ccall unsafe "LLVM_Hs_InlineAsmHasSideEffects" inlineAsmHasSideEffects ::
+  Ptr InlineAsm -> IO LLVMBool
+
+foreign import ccall unsafe "LLVM_Hs_InlineAsmIsAlignStack" inlineAsmIsAlignStack ::
+  Ptr InlineAsm -> IO LLVMBool
+
+foreign import ccall unsafe "LLVM_Hs_GetInlineAsmDialect" getInlineAsmDialect ::
+  Ptr InlineAsm -> IO AsmDialect
+
diff --git a/src/LLVM/Internal/FFI/InlineAssemblyC.cpp b/src/LLVM/Internal/FFI/InlineAssemblyC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/InlineAssemblyC.cpp
@@ -0,0 +1,73 @@
+#define __STDC_LIMIT_MACROS
+#include "llvm/IR/InlineAsm.h"
+#include "llvm/IR/Function.h"
+#include "llvm-c/Core.h"
+
+#include "LLVM/Internal/FFI/InlineAssembly.h"
+
+using namespace llvm;
+
+namespace llvm {
+static LLVMAsmDialect wrap(InlineAsm::AsmDialect d) {
+	switch(d) {
+#define ENUM_CASE(x) case InlineAsm::AD_ ## x: return LLVMAsmDialect_ ## x;
+LLVM_HS_FOR_EACH_ASM_DIALECT(ENUM_CASE)
+#undef ENUM_CASE
+	default: return LLVMAsmDialect(0);
+	}
+}
+
+static InlineAsm::AsmDialect unwrap(LLVMAsmDialect d) {
+	switch(d) {
+#define ENUM_CASE(x) case LLVMAsmDialect_ ## x: return InlineAsm::AD_ ## x;
+LLVM_HS_FOR_EACH_ASM_DIALECT(ENUM_CASE)
+#undef ENUM_CASE
+	default: return InlineAsm::AsmDialect(0);
+	}
+}
+}
+
+extern "C" {
+
+LLVMValueRef LLVM_Hs_CreateInlineAsm(
+	LLVMTypeRef t,
+	const char *asmStr,
+	const char *constraintsStr,
+	LLVMBool hasSideEffects,
+	LLVMBool isAlignStack,
+	LLVMAsmDialect dialect
+) {
+	return wrap(
+		InlineAsm::get(
+			unwrap<FunctionType>(t), 
+			asmStr,
+			constraintsStr,
+			hasSideEffects,
+			isAlignStack,
+			unwrap(dialect)
+		)
+	);
+}
+
+const char *LLVM_Hs_GetInlineAsmAsmString(LLVMValueRef v) {
+	return unwrap<InlineAsm>(v)->getAsmString().c_str();
+}
+
+const char *LLVM_Hs_GetInlineAsmConstraintString(LLVMValueRef v) {
+	return unwrap<InlineAsm>(v)->getConstraintString().c_str();
+}
+
+LLVMBool LLVM_Hs_InlineAsmHasSideEffects(LLVMValueRef v) {
+	return unwrap<InlineAsm>(v)->hasSideEffects();
+}
+
+LLVMBool LLVM_Hs_InlineAsmIsAlignStack(LLVMValueRef v) {
+	return unwrap<InlineAsm>(v)->isAlignStack();
+}
+
+LLVMAsmDialect LLVM_Hs_GetInlineAsmDialect(LLVMValueRef v) {
+	return wrap(unwrap<InlineAsm>(v)->getDialect());
+}
+
+}
+
diff --git a/src/LLVM/Internal/FFI/Instruction.h b/src/LLVM/Internal/FFI/Instruction.h
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/Instruction.h
@@ -0,0 +1,67 @@
+#ifndef __LLVM_INTERNAL_FFI__INSTRUCTION__H__
+#define __LLVM_INTERNAL_FFI__INSTRUCTION__H__
+
+#include "llvm/Config/llvm-config.h"
+
+#define LLVM_HS_FOR_EACH_ATOMIC_ORDERING(macro) \
+	macro(NotAtomic) \
+	macro(Unordered) \
+	macro(Monotonic) \
+	macro(Acquire) \
+	macro(Release) \
+	macro(AcquireRelease) \
+	macro(SequentiallyConsistent)
+
+#define LLVM_HS_FOR_EACH_RMW_OPERATION(macro) \
+	macro(Xchg) \
+	macro(Add) \
+	macro(Sub) \
+	macro(And) \
+	macro(Nand) \
+	macro(Or) \
+	macro(Xor) \
+	macro(Max) \
+	macro(Min) \
+	macro(UMax) \
+	macro(UMin)
+
+#define LLVM_HS_FOR_EACH_SYNCRONIZATION_SCOPE(macro) \
+	macro(SingleThread) \
+	macro(CrossThread)
+
+typedef enum {
+#define ENUM_CASE(x) LLVM ## x ## SynchronizationScope,
+LLVM_HS_FOR_EACH_SYNCRONIZATION_SCOPE(ENUM_CASE)
+#undef ENUM_CASE
+} LLVMSynchronizationScope;
+
+#define LLVM_HS_FOR_EACH_FAST_MATH_FLAG(macro) \
+	macro(UnsafeAlgebra, unsafeAlgebra)								\
+	macro(NoNaNs, noNaNs)															\
+	macro(NoInfs, noInfs)															\
+	macro(NoSignedZeros, noSignedZeros)								\
+	macro(AllowReciprocal, allowReciprocal)
+
+typedef enum {
+#define ENUM_CASE(x,l) LLVM ## x ## Bit,
+LLVM_HS_FOR_EACH_FAST_MATH_FLAG(ENUM_CASE)
+#undef ENUM_CASE
+} LLVMFastMathFlagBit;
+
+typedef enum {
+#define ENUM_CASE(x,l) LLVM ## x = (1 << LLVM ## x ## Bit),
+LLVM_HS_FOR_EACH_FAST_MATH_FLAG(ENUM_CASE)
+#undef ENUM_CASE
+} LLVMFastMathFlags;
+
+#define LLVM_HS_FOR_EACH_TAIL_CALL_KIND(macro) \
+	macro(None)                                       \
+	macro(Tail)                                       \
+	macro(MustTail)
+
+typedef enum {
+#define ENUM_CASE(x) LLVM_Hs_TailCallKind_ ## x,
+LLVM_HS_FOR_EACH_TAIL_CALL_KIND(ENUM_CASE)
+#undef ENUM_CASE
+} LLVM_Hs_TailCallKind;
+#endif
diff --git a/src/LLVM/Internal/FFI/Instruction.hs b/src/LLVM/Internal/FFI/Instruction.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/Instruction.hs
@@ -0,0 +1,181 @@
+{-# LANGUAGE
+  ForeignFunctionInterface,
+  MultiParamTypeClasses,
+  UndecidableInstances,
+  TemplateHaskell
+  #-}
+module LLVM.Internal.FFI.Instruction where
+
+import LLVM.Prelude
+
+import Foreign.Ptr
+import Foreign.C
+
+import LLVM.Internal.FFI.Attribute
+import LLVM.Internal.FFI.PtrHierarchy
+import LLVM.Internal.FFI.LLVMCTypes
+
+foreign import ccall unsafe "LLVMIsAInstruction" isAInstruction ::
+  Ptr Value -> IO (Ptr Instruction)
+
+newtype COpcode = COpcode CUInt
+
+-- get the C API opcode
+foreign import ccall unsafe "LLVMGetInstructionOpcode" getInstructionOpcode :: 
+  Ptr Instruction -> IO COpcode
+
+-- get the C++ API opcode (one less level of mapping than for that from the C API)
+foreign import ccall unsafe "LLVM_Hs_GetInstructionDefOpcode" getInstructionDefOpcode :: 
+  Ptr Instruction -> IO CPPOpcode
+
+foreign import ccall unsafe "LLVMGetICmpPredicate" getICmpPredicate ::
+  Ptr Instruction -> IO ICmpPredicate
+
+foreign import ccall unsafe "LLVM_Hs_GetFCmpPredicate" getFCmpPredicate ::
+  Ptr Instruction -> IO FCmpPredicate
+
+foreign import ccall unsafe "LLVM_Hs_GetCallSiteCallingConvention" getCallSiteCallingConvention ::
+  Ptr Instruction -> IO CallingConvention
+
+foreign import ccall unsafe "LLVM_Hs_SetCallSiteCallingConvention" setCallSiteCallingConvention ::
+  Ptr Instruction -> CallingConvention -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_GetTailCallKind" getTailCallKind ::
+  Ptr Instruction -> IO TailCallKind
+
+foreign import ccall unsafe "LLVM_Hs_SetTailCallKind" setTailCallKind ::
+  Ptr Instruction -> TailCallKind -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_GetCallSiteCalledValue" getCallSiteCalledValue ::
+  Ptr Instruction -> IO (Ptr Value)
+
+foreign import ccall unsafe "LLVM_Hs_GetCallSiteAttributeSet" getCallSiteAttributeSet ::
+  Ptr Instruction -> IO MixedAttributeSet
+
+foreign import ccall unsafe "LLVM_Hs_SetCallSiteAttributeSet" setCallSiteAttributeSet ::
+  Ptr Instruction -> MixedAttributeSet -> IO ()
+                     
+foreign import ccall unsafe "LLVMAddIncoming" addIncoming' ::
+  Ptr Instruction -> Ptr (Ptr Value) -> Ptr (Ptr BasicBlock) -> CUInt -> IO ()
+
+addIncoming :: Ptr Instruction -> (CUInt, Ptr (Ptr Value)) -> (CUInt, Ptr (Ptr BasicBlock)) -> IO ()
+addIncoming i (nvs, vs) (nbs, bs) | nbs == nvs = addIncoming' i vs bs nbs
+
+foreign import ccall unsafe "LLVMCountIncoming" countIncoming ::
+  Ptr Instruction -> IO CUInt
+
+foreign import ccall unsafe "LLVMGetIncomingValue" getIncomingValue ::
+  Ptr Instruction -> CUInt -> IO (Ptr Value)
+
+foreign import ccall unsafe "LLVMGetIncomingBlock" getIncomingBlock ::
+  Ptr Instruction -> CUInt -> IO (Ptr BasicBlock)
+
+
+foreign import ccall unsafe "LLVMAddCase" addCase ::
+  Ptr Instruction -> Ptr Constant -> Ptr BasicBlock -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_GetSwitchCases" getSwitchCases ::
+  Ptr Instruction -> Ptr (Ptr Constant) -> Ptr (Ptr BasicBlock) -> IO ()
+
+foreign import ccall unsafe "LLVMAddDestination" addDestination ::
+  Ptr Instruction -> Ptr BasicBlock -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_GetIndirectBrDests" getIndirectBrDests ::
+  Ptr Instruction -> Ptr (Ptr BasicBlock) -> IO ()
+
+
+foreign import ccall unsafe "LLVM_Hs_GetInstrAlignment" getInstrAlignment ::
+  Ptr Instruction -> IO CUInt
+
+foreign import ccall unsafe "LLVM_Hs_SetInstrAlignment" setInstrAlignment ::
+  Ptr Instruction -> CUInt -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_GetAllocaNumElements" getAllocaNumElements ::
+  Ptr Instruction -> IO (Ptr Value)
+
+foreign import ccall unsafe "LLVM_Hs_GetAllocatedType" getAllocatedType ::
+  Ptr Instruction -> IO (Ptr Type)
+
+foreign import ccall unsafe "LLVM_Hs_GetAtomicOrdering" getAtomicOrdering ::
+  Ptr Instruction -> IO MemoryOrdering
+
+foreign import ccall unsafe "LLVM_Hs_GetFailureAtomicOrdering" getFailureAtomicOrdering ::
+  Ptr Instruction -> IO MemoryOrdering
+
+foreign import ccall unsafe "LLVM_Hs_GetSynchronizationScope" getSynchronizationScope ::
+  Ptr Instruction -> IO SynchronizationScope
+
+getAtomicity :: Ptr Instruction -> IO (SynchronizationScope, MemoryOrdering)
+getAtomicity i = return (,) `ap` getSynchronizationScope i `ap` getAtomicOrdering i
+
+foreign import ccall unsafe "LLVM_Hs_GetVolatile" getVolatile ::
+  Ptr Instruction -> IO LLVMBool
+
+foreign import ccall unsafe "LLVM_Hs_GetInBounds" getInBounds ::
+  Ptr Value -> IO LLVMBool
+
+foreign import ccall unsafe "LLVM_Hs_GetAtomicRMWBinOp" getAtomicRMWBinOp ::
+  Ptr Instruction -> IO RMWOperation
+
+foreign import ccall unsafe "LLVM_Hs_CountInstStructureIndices" countInstStructureIndices ::
+  Ptr Instruction -> IO CUInt
+
+foreign import ccall unsafe "LLVM_Hs_GetInstStructureIndices" getInstStructureIndices ::
+  Ptr Instruction -> Ptr CUInt -> IO ()
+
+foreign import ccall unsafe "LLVMAddClause" addClause ::
+  Ptr Instruction -> Ptr Constant -> IO ()
+
+foreign import ccall unsafe "LLVMSetCleanup" setCleanup ::
+  Ptr Instruction -> LLVMBool -> IO ()
+
+foreign import ccall unsafe "LLVMIsCleanup" isCleanup ::
+  Ptr Instruction -> IO LLVMBool
+
+foreign import ccall unsafe "LLVMGetNumClauses" getNumClauses ::
+  Ptr Instruction -> IO CUInt
+
+foreign import ccall unsafe "LLVMGetClause" getClause ::
+  Ptr Instruction -> CUInt -> IO (Ptr Constant)
+
+foreign import ccall unsafe "LLVM_Hs_SetMetadata" setMetadata ::
+  Ptr Instruction -> MDKindID -> Ptr MDNode -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_GetMetadata" getMetadata ::
+  Ptr Instruction -> Ptr MDKindID -> Ptr (Ptr MDNode) -> CUInt -> IO CUInt
+
+foreign import ccall unsafe "LLVM_Hs_GetCleanupPad" getCleanupPad ::
+  Ptr Instruction -> IO (Ptr Instruction)
+
+foreign import ccall unsafe "LLVM_Hs_GetUnwindDest" getUnwindDest ::
+  Ptr Instruction -> IO (Ptr BasicBlock)
+
+foreign import ccall unsafe "LLVM_Hs_GetParentPad" getParentPad ::
+  Ptr Instruction -> IO (Ptr Value)
+
+foreign import ccall unsafe "LLVM_Hs_GetNumArgOperands" getNumArgOperands ::
+  Ptr Instruction -> IO CUInt
+
+foreign import ccall unsafe "LLVM_Hs_GetArgOperand" getArgOperand ::
+  Ptr Instruction -> CUInt -> IO (Ptr Value)
+
+foreign import ccall unsafe "LLVM_Hs_CatchSwitch_GetParentPad" catchSwitchGetParentPad ::
+  Ptr Instruction -> IO (Ptr Value)
+
+foreign import ccall unsafe "LLVM_Hs_CatchSwitch_GetUnwindDest" catchSwitchGetUnwindDest ::
+  Ptr Instruction -> IO (Ptr BasicBlock)
+
+foreign import ccall unsafe "LLVM_Hs_CatchSwitch_GetNumHandlers" catchSwitchGetNumHandlers ::
+  Ptr Instruction -> IO CUInt
+
+foreign import ccall unsafe "LLVM_Hs_CatchSwitch_GetHandler" catchSwitchGetHandler ::
+  Ptr Instruction -> CUInt -> IO (Ptr BasicBlock)
+
+foreign import ccall unsafe "LLVM_Hs_CatchSwitch_AddHandler" catchSwitchAddHandler ::
+  Ptr Instruction -> Ptr BasicBlock -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_CatchRet_GetCatchPad" catchRetGetCatchPad ::
+  Ptr Instruction -> IO (Ptr Value)
+
+foreign import ccall unsafe "LLVM_Hs_CatchRet_GetSuccessor" catchRetGetSuccessor ::
+  Ptr Instruction -> IO (Ptr BasicBlock)
diff --git a/src/LLVM/Internal/FFI/InstructionC.cpp b/src/LLVM/Internal/FFI/InstructionC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/InstructionC.cpp
@@ -0,0 +1,323 @@
+#define __STDC_LIMIT_MACROS
+#include "llvm/Config/llvm-config.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/InstrTypes.h"
+#include "llvm/IR/Attributes.h"
+#include "llvm/IR/Operator.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/Metadata.h"
+#include "llvm/IR/CallSite.h"
+
+#include "llvm-c/Core.h"
+
+#include "LLVM/Internal/FFI/Metadata.hpp"
+#include "LLVM/Internal/FFI/AttributeC.hpp"
+#include "LLVM/Internal/FFI/Instruction.h"
+
+using namespace llvm;
+
+namespace llvm {
+
+static LLVMAtomicOrdering wrap(AtomicOrdering l) {
+	switch(l) {
+#define ENUM_CASE(x) case AtomicOrdering::x: return LLVMAtomicOrdering ## x;
+LLVM_HS_FOR_EACH_ATOMIC_ORDERING(ENUM_CASE)
+#undef ENUM_CASE
+	default: return LLVMAtomicOrdering(0);
+	}
+}
+
+static LLVMSynchronizationScope wrap(SynchronizationScope l) {
+	switch(l) {
+#define ENUM_CASE(x) case x: return LLVM ## x ## SynchronizationScope;
+LLVM_HS_FOR_EACH_SYNCRONIZATION_SCOPE(ENUM_CASE)
+#undef ENUM_CASE
+	default: return LLVMSynchronizationScope(0);
+	}
+}
+
+static LLVMAtomicRMWBinOp wrap(AtomicRMWInst::BinOp l) {
+	switch(l) {
+#define ENUM_CASE(x) case AtomicRMWInst::x: return LLVMAtomicRMWBinOp ## x;
+LLVM_HS_FOR_EACH_RMW_OPERATION(ENUM_CASE)
+#undef ENUM_CASE
+	default: return LLVMAtomicRMWBinOp(0);
+	}
+}
+
+LLVMFastMathFlags wrap(FastMathFlags f) {
+	unsigned r = 0;
+#define ENUM_CASE(u,l) if (f.l()) r |= unsigned(LLVM ## u);
+LLVM_HS_FOR_EACH_FAST_MATH_FLAG(ENUM_CASE)
+#undef ENUM_CASE
+	return LLVMFastMathFlags(r);
+}
+
+}
+
+extern "C" {
+
+unsigned LLVM_Hs_GetInstructionDefOpcode(LLVMValueRef val) {
+	return unwrap<Instruction>(val)->getOpcode();
+}
+
+unsigned LLVM_Hs_HasNoSignedWrap(LLVMValueRef val) {
+	return unwrap<OverflowingBinaryOperator>(val)->hasNoSignedWrap();
+}
+
+unsigned LLVM_Hs_HasNoUnsignedWrap(LLVMValueRef val) {
+	return unwrap<OverflowingBinaryOperator>(val)->hasNoUnsignedWrap();
+}
+
+int LLVM_Hs_IsExact(LLVMValueRef val) {
+	return unwrap<PossiblyExactOperator>(val)->isExact();
+}
+
+LLVMFastMathFlags LLVM_Hs_GetFastMathFlags(LLVMValueRef val) {
+	return wrap(unwrap<Instruction>(val)->getFastMathFlags());
+}
+
+LLVMValueRef LLVM_Hs_GetCallSiteCalledValue(LLVMValueRef i) {
+	return wrap(CallSite(unwrap<Instruction>(i)).getCalledValue());
+}
+
+const AttributeSetImpl *LLVM_Hs_GetCallSiteAttributeSet(LLVMValueRef i) {
+	return wrap(CallSite(unwrap<Instruction>(i)).getAttributes());
+}
+
+void LLVM_Hs_SetCallSiteAttributeSet(LLVMValueRef i, const AttributeSetImpl *asi) {
+	CallSite(unwrap<Instruction>(i)).setAttributes(unwrap(asi));
+}
+
+unsigned LLVM_Hs_GetCallSiteCallingConvention(LLVMValueRef i) {
+  return unsigned(CallSite(unwrap<Instruction>(i)).getCallingConv());
+}
+
+void LLVM_Hs_SetCallSiteCallingConvention(LLVMValueRef i, unsigned cc) {
+  CallSite(unwrap<Instruction>(i)).setCallingConv(llvm::CallingConv::ID(cc));
+}
+
+#define CHECK(name)                                                            \
+    static_assert(unsigned(llvm::CallInst::TCK_##name) ==                      \
+                      unsigned(LLVM_Hs_TailCallKind_##name),              \
+                  "LLVM_Hs_TailCallKind enum out of sync w/ "             \
+                  "llvm::CallInst::TailCallKind for " #name);
+LLVM_HS_FOR_EACH_TAIL_CALL_KIND(CHECK)
+#undef CHECK
+
+unsigned LLVM_Hs_GetTailCallKind(LLVMValueRef i) {
+    return unwrap<CallInst>(i)->getTailCallKind();
+}
+
+void LLVM_Hs_SetTailCallKind(LLVMValueRef i, unsigned kind) {
+    return unwrap<CallInst>(i)->setTailCallKind(llvm::CallInst::TailCallKind(kind));
+}
+
+LLVMValueRef LLVM_Hs_GetAllocaNumElements(LLVMValueRef a) {
+	return wrap(unwrap<AllocaInst>(a)->getArraySize());
+}
+
+LLVMTypeRef LLVM_Hs_GetAllocatedType(LLVMValueRef a) {
+	return wrap(unwrap<AllocaInst>(a)->getAllocatedType());
+}
+
+// ------------------------------------------------------------
+
+#define LLVM_HS_FOR_EACH_ALIGNMENT_INST(macro) \
+	macro(Alloca) \
+	macro(Load) \
+	macro(Store)
+
+unsigned LLVM_Hs_GetInstrAlignment(LLVMValueRef l) {
+	switch(unwrap<Instruction>(l)->getOpcode()) {
+#define ENUM_CASE(n) case Instruction::n: return unwrap<n ## Inst>(l)->getAlignment();
+		LLVM_HS_FOR_EACH_ALIGNMENT_INST(ENUM_CASE)
+#undef ENUM_CASE
+	default: return 0;
+	}
+}
+
+void LLVM_Hs_SetInstrAlignment(LLVMValueRef l, unsigned a) {
+	switch(unwrap<Instruction>(l)->getOpcode()) {
+#define ENUM_CASE(n) case Instruction::n: unwrap<n ## Inst>(l)->setAlignment(a); break;
+		LLVM_HS_FOR_EACH_ALIGNMENT_INST(ENUM_CASE)
+#undef ENUM_CASE
+	}
+}
+
+// ------------------------------------------------------------
+
+#define LLVM_HS_FOR_EACH_ATOMIC_INST(macro)	\
+	macro(Load,)																		\
+	macro(Store,)																		\
+	macro(Fence,)																		\
+	macro(AtomicCmpXchg,Success)										\
+	macro(AtomicRMW,)
+
+LLVMAtomicOrdering LLVM_Hs_GetAtomicOrdering(LLVMValueRef i) {
+	switch(unwrap<Instruction>(i)->getOpcode()) {
+#define ENUM_CASE(n,s) case Instruction::n: return wrap(unwrap<n ## Inst>(i)->get ## s ## Ordering());
+		LLVM_HS_FOR_EACH_ATOMIC_INST(ENUM_CASE)
+#undef ENUM_CASE
+	default: return LLVMAtomicOrdering(0);
+	}
+}
+
+LLVMAtomicOrdering LLVM_Hs_GetFailureAtomicOrdering(LLVMValueRef i) {
+	switch(unwrap<Instruction>(i)->getOpcode()) {
+	case Instruction::AtomicCmpXchg: return wrap(unwrap<AtomicCmpXchgInst>(i)->getFailureOrdering());
+	default: return LLVMAtomicOrdering(0);
+	}
+}
+
+LLVMSynchronizationScope LLVM_Hs_GetSynchronizationScope(LLVMValueRef i) {
+	switch(unwrap<Instruction>(i)->getOpcode()) {
+#define ENUM_CASE(n,s) case Instruction::n: return wrap(unwrap<n ## Inst>(i)->getSynchScope());
+		LLVM_HS_FOR_EACH_ATOMIC_INST(ENUM_CASE)
+#undef ENUM_CASE
+	default: return LLVMSynchronizationScope(0);
+	}
+}
+
+#define LLVM_HS_FOR_EACH_VOLATILITY_INST(macro) \
+	macro(Load) \
+	macro(Store) \
+	macro(AtomicCmpXchg) \
+	macro(AtomicRMW)
+
+LLVMBool LLVM_Hs_GetVolatile(LLVMValueRef i) {
+	switch(unwrap<Instruction>(i)->getOpcode()) {
+#define ENUM_CASE(n) case Instruction::n: return unwrap<n ## Inst>(i)->isVolatile();
+		LLVM_HS_FOR_EACH_VOLATILITY_INST(ENUM_CASE)
+#undef ENUM_CASE
+	default: return LLVMBool(0);
+	}
+}
+
+// ------------------------------------------------------------
+
+LLVMBool LLVM_Hs_GetInBounds(LLVMValueRef i) {
+	return unwrap<GEPOperator>(i)->isInBounds();
+}
+
+LLVMAtomicRMWBinOp LLVM_Hs_GetAtomicRMWBinOp(LLVMValueRef i) {
+	return wrap(unwrap<AtomicRMWInst>(i)->getOperation());
+}
+
+LLVMRealPredicate LLVM_Hs_GetFCmpPredicate(LLVMValueRef Inst) {
+	if (FCmpInst *I = dyn_cast<FCmpInst>(unwrap(Inst)))
+		return (LLVMRealPredicate)I->getPredicate();
+	if (ConstantExpr *CE = dyn_cast<ConstantExpr>(unwrap(Inst)))
+		if (CE->getOpcode() == Instruction::FCmp)
+			return (LLVMRealPredicate)CE->getPredicate();
+	return (LLVMRealPredicate)0;
+}
+
+unsigned LLVM_Hs_CountInstStructureIndices(LLVMValueRef v) {
+	if (ExtractValueInst *i = dyn_cast<ExtractValueInst>(unwrap(v))) return i->getNumIndices();
+	if (InsertValueInst *i = dyn_cast<InsertValueInst>(unwrap(v))) return i->getNumIndices();
+	return 0;
+}
+
+void LLVM_Hs_GetInstStructureIndices(LLVMValueRef v, unsigned *is) {
+	ArrayRef<unsigned> a;
+	if (ExtractValueInst *i = dyn_cast<ExtractValueInst>(unwrap(v))) a = i->getIndices();
+	if (InsertValueInst *i = dyn_cast<InsertValueInst>(unwrap(v))) a = i->getIndices();
+	std::copy(a.begin(), a.end(), is);
+}
+
+void LLVM_Hs_GetSwitchCases(
+	LLVMValueRef v,
+	LLVMValueRef *values,
+	LLVMBasicBlockRef *dests
+) {
+	SwitchInst *s = unwrap<SwitchInst>(v);
+	for(SwitchInst::CaseIt i = s->case_begin(); i != s->case_end(); ++i, ++values, ++dests) {
+		*values = wrap(i.getCaseValue());
+		*dests = wrap(i.getCaseSuccessor());
+	}
+}
+
+void LLVM_Hs_GetIndirectBrDests(
+	LLVMValueRef v,
+	LLVMBasicBlockRef *dests
+) {
+	IndirectBrInst *ib = unwrap<IndirectBrInst>(v);
+	for(unsigned i=0; i != ib->getNumDestinations(); ++i, ++dests)
+		*dests = wrap(ib->getDestination(i));
+}
+
+inline Metadata **unwrap(LLVMMetadataRef *vals) {
+    return reinterpret_cast<Metadata**>(vals);
+}
+
+void LLVM_Hs_SetMetadata(LLVMValueRef inst, unsigned kindID, LLVMMetadataRef md) {
+    MDNode *N = md ? unwrap<MDNode>(md) : nullptr;
+    unwrap<Instruction>(inst)->setMetadata(kindID, N);
+}
+
+unsigned LLVM_Hs_GetMetadata(
+	LLVMValueRef i,
+	unsigned *kinds,
+	LLVMMetadataRef *nodes,
+	unsigned nKinds
+) {
+	SmallVector<std::pair<unsigned, MDNode *>, 4> mds;
+	unwrap<Instruction>(i)->getAllMetadata(mds);
+	if (mds.size() <= nKinds) {
+		for(unsigned i=0; i<mds.size(); ++i) {
+			kinds[i] = mds[i].first;
+			nodes[i] = wrap(mds[i].second);
+		}
+	}
+	return mds.size();
+}
+
+LLVMValueRef LLVM_Hs_GetCleanupPad(LLVMValueRef i) {
+    return wrap(unwrap<CleanupReturnInst>(i)->getCleanupPad());
+}
+
+LLVMBasicBlockRef LLVM_Hs_GetUnwindDest(LLVMValueRef i) {
+    return wrap(unwrap<CleanupReturnInst>(i)->getUnwindDest());
+}
+
+LLVMValueRef LLVM_Hs_GetParentPad(LLVMValueRef i) {
+    return wrap(unwrap<FuncletPadInst>(i)->getParentPad());
+}
+
+unsigned LLVM_Hs_GetNumArgOperands(LLVMValueRef i) {
+    return unwrap<FuncletPadInst>(i)->getNumArgOperands();
+}
+
+LLVMValueRef LLVM_Hs_GetArgOperand(LLVMValueRef i, unsigned op) {
+    return wrap(unwrap<FuncletPadInst>(i)->getArgOperand(op));
+}
+
+LLVMValueRef LLVM_Hs_CatchSwitch_GetParentPad(LLVMValueRef i) {
+    return wrap(unwrap<CatchSwitchInst>(i)->getParentPad());
+}
+
+LLVMBasicBlockRef LLVM_Hs_CatchSwitch_GetUnwindDest(LLVMValueRef i) {
+    return wrap(unwrap<CatchSwitchInst>(i)->getUnwindDest());
+}
+
+unsigned LLVM_Hs_CatchSwitch_GetNumHandlers(LLVMValueRef i) {
+    return unwrap<CatchSwitchInst>(i)->getNumHandlers();
+}
+
+LLVMBasicBlockRef LLVM_Hs_CatchSwitch_GetHandler(LLVMValueRef instr, unsigned i) {
+    return wrap(*(unwrap<CatchSwitchInst>(instr)->handler_begin() + i));
+}
+
+void LLVM_Hs_CatchSwitch_AddHandler(LLVMValueRef instr, LLVMBasicBlockRef bb) {
+    unwrap<CatchSwitchInst>(instr)->addHandler(unwrap(bb));
+}
+
+LLVMValueRef LLVM_Hs_CatchRet_GetCatchPad(LLVMValueRef i) {
+    return wrap(unwrap<CatchReturnInst>(i)->getCatchPad());
+}
+
+LLVMBasicBlockRef LLVM_Hs_CatchRet_GetSuccessor(LLVMValueRef i) {
+    return wrap(unwrap<CatchReturnInst>(i)->getSuccessor());
+}
+}
diff --git a/src/LLVM/Internal/FFI/InstructionDefs.hsc b/src/LLVM/Internal/FFI/InstructionDefs.hsc
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/InstructionDefs.hsc
@@ -0,0 +1,61 @@
+-- This module translates the instruction data in "llvm/Instruction.def" into a Haskell data structure,
+-- so it may be accessed conveniently with Template Haskell code
+module LLVM.Internal.FFI.InstructionDefs where
+
+import LLVM.Prelude
+
+import LLVM.Internal.FFI.LLVMCTypes
+
+#define FIRST_TERM_INST(num) struct inst { const char *kind; int opcode; const char *name; const char *clas; } insts[] = { { "Terminator", },
+
+#define FIRST_BINARY_INST(num) { "Binary" },
+#define FIRST_MEMORY_INST(num) { "Memory" },
+#define FIRST_CAST_INST(num) { "Cast" },
+#define FIRST_OTHER_INST(num) { "Other" },
+#define FIRST_FUNCLETPAD_INST(num) { "FuncletPad" },
+
+#define HANDLE_INST(num,opcode,class) { 0, num, #opcode, #class, },
+
+#define LAST_OTHER_INST(num) { 0, 0, 0, 0, } };
+
+#include "llvm/Config/llvm-config.h"
+
+#include "llvm/IR/Instruction.def"
+
+#{
+define hsc_inject() {                                       \
+  hsc_printf("[");                                       \
+  struct inst *i;                                           \
+  const char *kind;                                         \
+  int first = 1;                                            \
+  for(i = insts; i->kind || i->opcode; ++i) {               \
+    if (i->kind) { kind = i->kind; continue; }              \
+    if (!first) { hsc_printf("\n   ,"); } else { first = 0; }  \
+    hsc_printf(                                             \
+        " (CPPOpcode %d,\"%s\",\"%s\", %s)",               \
+        i->opcode, i->name, i->clas, kind                   \
+      );                                                    \
+  }                                                         \
+  hsc_printf(" ] ");                                       \
+}
+}
+
+data InstructionKind = Terminator | Binary | Memory | Cast | FuncletPad | Other
+  deriving (Eq, Ord, Show)
+
+data InstructionDef = InstructionDef {
+    cppOpcode :: CPPOpcode,
+    cAPIName :: String,
+    cAPIClassName :: String,
+    instructionKind :: InstructionKind
+  }
+  deriving (Eq, Ord, Show)
+
+instructionDefs :: [InstructionDef]
+instructionDefs = [ 
+ InstructionDef o an acn k
+ | (o, an, acn, k) <-
+   #{inject},
+ an /= "UserOp1" && an /= "UserOp2"
+ ]
+
diff --git a/src/LLVM/Internal/FFI/Iterate.hs b/src/LLVM/Internal/FFI/Iterate.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/Iterate.hs
@@ -0,0 +1,13 @@
+-- | Functions to help handle LLVM iteration patterns
+module LLVM.Internal.FFI.Iterate where
+
+import LLVM.Prelude
+
+import Foreign.Ptr
+
+-- | retrieve a sequence of objects which form a linked list, given an action to
+-- | retrieve the first member and an action to proceed through the list
+getXs :: IO (Ptr a) -> (Ptr a -> IO (Ptr a)) -> IO [Ptr a]
+getXs firstX nextX = walk =<< firstX
+    where walk x | x == nullPtr = return []
+          walk x = (x:) <$> (walk <=< nextX) x
diff --git a/src/LLVM/Internal/FFI/LLVMCTypes.hsc b/src/LLVM/Internal/FFI/LLVMCTypes.hsc
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/LLVMCTypes.hsc
@@ -0,0 +1,266 @@
+{-# LANGUAGE
+  GeneralizedNewtypeDeriving
+  #-}
+-- | Define types which correspond cleanly with some simple types on the C/C++ side.
+-- Encapsulate hsc macro weirdness here, supporting higher-level tricks elsewhere.
+module LLVM.Internal.FFI.LLVMCTypes where
+
+import LLVM.Prelude
+
+#define __STDC_LIMIT_MACROS
+#include "llvm-c/Core.h"
+#include "llvm-c/Linker.h"
+#include "llvm-c/OrcBindings.h"
+#include "llvm-c/Target.h"
+#include "llvm-c/TargetMachine.h"
+#include "LLVM/Internal/FFI/Attribute.h"
+#include "LLVM/Internal/FFI/Instruction.h"
+#include "LLVM/Internal/FFI/Value.h"
+#include "LLVM/Internal/FFI/SMDiagnostic.h"
+#include "LLVM/Internal/FFI/InlineAssembly.h"
+#include "LLVM/Internal/FFI/Target.h"
+#include "LLVM/Internal/FFI/CallingConvention.h"
+#include "LLVM/Internal/FFI/GlobalValue.h"
+#include "LLVM/Internal/FFI/Type.h"
+#include "LLVM/Internal/FFI/Constant.h"
+#include "LLVM/Internal/FFI/Analysis.h"
+#include "LLVM/Internal/FFI/LibFunc.h"
+#include "LLVM/Internal/FFI/OrcJIT.h"
+
+import Language.Haskell.TH.Quote
+
+import Data.Bits
+import Foreign.C
+import Foreign.Storable
+
+#{
+define hsc_inject(l, typ, cons, hprefix, recmac) { \
+  struct { const char *s; unsigned n; } *p, list[] = { LLVM_HS_FOR_EACH_ ## l(recmac) }; \
+  for(p = list; p < list + sizeof(list)/sizeof(list[0]); ++p) { \
+    hsc_printf(#hprefix "%s :: " #typ "\n", p->s); \
+    hsc_printf(#hprefix "%s = " #cons " %u\n", p->s, p->n); \
+  } \
+  hsc_printf(#hprefix "P :: QuasiQuoter\n" \
+             #hprefix "P = QuasiQuoter {\n" \
+             "  quoteExp = undefined,\n" \
+             "  quotePat = \\s -> dataToPatQ (const Nothing) $ case s of"); \
+  for(p = list; p < list + sizeof(list)/sizeof(list[0]); ++p) { \
+    hsc_printf("\n    \"%s\" -> " #hprefix "%s", p->s, p->s); \
+  } \
+  hsc_printf("\n    x -> error $ \"bad quasiquoted FFI constant for " #hprefix ": \" ++ x"); \
+  hsc_printf(",\n" \
+             "  quoteType = undefined,\n" \
+             "  quoteDec = undefined\n" \
+             " }\n"); \
+}
+}
+
+deriving instance Data CUInt
+
+newtype LLVMBool = LLVMBool CUInt
+
+newtype OwnerTransfered a = OwnerTransfered a
+  deriving (Storable)
+
+newtype NothingAsMinusOne h = NothingAsMinusOne CInt
+  deriving (Storable)
+
+newtype NothingAsEmptyString c = NothingAsEmptyString c
+  deriving (Storable)
+
+newtype CPPOpcode = CPPOpcode CUInt
+  deriving (Eq, Ord, Show, Typeable, Data)
+
+newtype ICmpPredicate = ICmpPredicate CUInt
+  deriving (Eq, Ord, Show, Typeable, Data)
+#{enum ICmpPredicate, ICmpPredicate,
+ iCmpPredEQ = LLVMIntEQ,
+ iCmpPredNE = LLVMIntNE,
+ iCmpPredUGT = LLVMIntUGT,
+ iCmpPredUGE = LLVMIntUGE,
+ iCmpPredULT = LLVMIntULT,
+ iCmpPredULE = LLVMIntULE,
+ iCmpPredSGT = LLVMIntSGT,
+ iCmpPredSGE = LLVMIntSGE,
+ iCmpPredSLT = LLVMIntSLT,
+ iCmpPredSLE = LLVMIntSLE
+}
+
+newtype FCmpPredicate = FCmpPredicate CUInt
+  deriving (Eq, Ord, Show, Typeable, Data)
+#{enum FCmpPredicate, FCmpPredicate,
+ fCmpPredFalse = LLVMRealPredicateFalse,
+ fCmpPredOEQ = LLVMRealOEQ,
+ fCmpPredOGT = LLVMRealOGT,
+ fCmpPredOGE = LLVMRealOGE,
+ fCmpPredOLT = LLVMRealOLT,
+ fCmpPredOLE = LLVMRealOLE,
+ fCmpPredONE = LLVMRealONE,
+ fCmpPredORD = LLVMRealORD,
+ fCmpPredUNO = LLVMRealUNO,
+ fCmpPredUEQ = LLVMRealUEQ,
+ fCmpPredUGT = LLVMRealUGT,
+ fCmpPredUGE = LLVMRealUGE,
+ fCmpPredULT = LLVMRealULT,
+ fCmpPredULE = LLVMRealULE,
+ fCmpPredUNE = LLVMRealUNE,
+ fcmpPredTrue = LLVMRealPredicateTrue
+}
+
+newtype MDKindID = MDKindID CUInt
+  deriving (Storable)
+
+newtype FastMathFlags = FastMathFlags CUInt
+  deriving (Eq, Ord, Show, Typeable, Data, Num, Bits)
+#define FMF_Rec(n,l) { #n, LLVM ## n, },
+#{inject FAST_MATH_FLAG, FastMathFlags, FastMathFlags, fastMathFlags, FMF_Rec}
+
+newtype MemoryOrdering = MemoryOrdering CUInt
+  deriving (Eq, Typeable, Data)
+#define MO_Rec(n) { #n, LLVMAtomicOrdering ## n },
+#{inject ATOMIC_ORDERING, MemoryOrdering, MemoryOrdering, memoryOrdering, MO_Rec}
+
+newtype UnnamedAddr = UnnamedAddr CUInt
+  deriving (Eq, Typeable, Data)
+#define UA_Rec(n) { #n, LLVMUnnamedAddr ## n },
+#{inject UNNAMED_ADDR, UnnamedAddr, UnnamedAddr, unnamedAddr, UA_Rec}
+
+newtype SynchronizationScope = SynchronizationScope CUInt
+  deriving (Eq, Typeable, Data)
+#define SS_Rec(n) { #n, LLVM ## n ## SynchronizationScope },
+#{inject SYNCRONIZATION_SCOPE, SynchronizationScope, SynchronizationScope, synchronizationScope, SS_Rec}
+
+newtype TailCallKind = TailCallKind CUInt
+  deriving (Eq, Typeable, Data)
+#define TCK_Rec(n) { #n, LLVM_Hs_TailCallKind_ ## n },
+#{inject TAIL_CALL_KIND, TailCallKind, TailCallKind, tailCallKind, TCK_Rec}
+
+newtype Linkage = Linkage CUInt
+  deriving (Eq, Read, Show, Typeable, Data)
+#define LK_Rec(n) { #n, LLVM ## n ## Linkage },
+#{inject LINKAGE, Linkage, Linkage, linkage, LK_Rec}
+
+newtype Visibility = Visibility CUInt
+  deriving (Eq, Read, Show, Typeable, Data)
+#define VIS_Rec(n) { #n, LLVM ## n ## Visibility },
+#{inject VISIBILITY, Visibility, Visibility, visibility, VIS_Rec}
+
+newtype COMDATSelectionKind = COMDATSelectionKind CUInt
+  deriving (Eq, Read, Show, Typeable, Data)
+#define CSK(n) { #n, LLVM_Hs_COMDAT_Selection_Kind_ ## n },
+#{inject COMDAT_SELECTION_KIND, COMDATSelectionKind, COMDATSelectionKind, comdatSelectionKind, CSK}
+
+newtype DLLStorageClass = DLLStorageClass CUInt
+  deriving (Eq, Read, Show, Typeable, Data)
+#define DLLSC_Rec(n) { #n, LLVM ## n ## StorageClass },
+#{inject DLL_STORAGE_CLASS, DLLStorageClass, DLLStorageClass, dllStorageClass, DLLSC_Rec}
+
+newtype CallingConvention = CallingConvention CUInt
+  deriving (Eq, Read, Show, Typeable, Data)
+#define CC_Rec(l, n) { #l, LLVM_Hs_CallingConvention_ ## l },
+#{inject CALLING_CONVENTION, CallingConvention, CallingConvention, callingConvention, CC_Rec}
+
+newtype ThreadLocalMode = ThreadLocalMode CUInt
+  deriving (Eq, Read, Show, Typeable, Data)
+#define TLS_Rec(n) { #n, LLVM ## n },
+#{inject THREAD_LOCAL_MODE, ThreadLocalMode, ThreadLocalMode, threadLocalMode, TLS_Rec}
+
+newtype ValueSubclassId = ValueSubclassId CUInt
+  deriving (Eq, Read, Show, Typeable, Data)
+#define VSID_Rec(n) { #n, LLVM ## n ## SubclassId },
+#{inject VALUE_SUBCLASS, ValueSubclassId, ValueSubclassId, valueSubclassId, VSID_Rec}
+
+newtype DiagnosticKind = DiagnosticKind CUInt
+  deriving (Eq, Read, Show, Typeable, Data)
+#define DK_Rec(n) { #n, LLVMDiagnosticKind ## n },
+#{inject DIAGNOSTIC_KIND, DiagnosticKind, DiagnosticKind, diagnosticKind, DK_Rec}
+
+newtype AsmDialect = AsmDialect CUInt
+  deriving (Eq, Read, Show, Typeable, Data)
+#define ASM_Rec(n) { #n, LLVMAsmDialect_ ## n },
+#{inject ASM_DIALECT, AsmDialect, AsmDialect, asmDialect, ASM_Rec}
+
+newtype RMWOperation = RMWOperation CUInt
+  deriving (Eq, Read, Show, Typeable, Data)
+#define RMWOp_Rec(n) { #n, LLVMAtomicRMWBinOp ## n },
+#{inject RMW_OPERATION, RMWOperation, RMWOperation, rmwOperation, RMWOp_Rec}
+
+newtype RelocModel = RelocModel CUInt
+  deriving (Eq, Read, Show, Typeable, Data)
+#define RM_Rec(n,m) { #n, LLVMReloc ## n },
+#{inject RELOC_MODEL, RelocModel, RelocModel, relocModel, RM_Rec}
+
+newtype CodeModel = CodeModel CUInt
+  deriving (Eq, Read, Show, Typeable, Data)
+#define CM_Rec(n) { #n, LLVMCodeModel ## n },
+#{inject CODE_MODEL, CodeModel, CodeModel, codeModel, CM_Rec}
+
+newtype CodeGenOptLevel = CodeGenOptLevel CUInt
+  deriving (Eq, Read, Show, Typeable, Data)
+#define CGOL_Rec(n) { #n, LLVMCodeGenLevel ## n },
+#{inject CODE_GEN_OPT_LEVEL, CodeGenOptLevel, CodeGenOptLevel, codeGenOptLevel, CGOL_Rec}
+
+newtype CodeGenFileType = CodeGenFileType CUInt
+  deriving (Eq, Read, Show, Typeable, Data)
+#define CGFT_Rec(n) { #n, LLVM ## n ## File },
+#{inject CODE_GEN_FILE_TYPE, CodeGenFileType, CodeGenFileType, codeGenFileType, CGFT_Rec}
+
+newtype FloatABIType = FloatABIType CUInt
+  deriving (Eq, Read, Show, Typeable, Data)
+#define FAT_Rec(n) { #n, LLVM_Hs_FloatABI_ ## n },
+#{inject FLOAT_ABI, FloatABIType, FloatABIType, floatABI, FAT_Rec}
+
+newtype FPOpFusionMode = FPOpFusionMode CUInt
+  deriving (Eq, Read, Show, Typeable, Data)
+#define FPOFM_Rec(n) { #n, LLVM_Hs_FPOpFusionMode_ ## n },
+#{inject FP_OP_FUSION_MODE, FPOpFusionMode, FPOpFusionMode, fpOpFusionMode, FPOFM_Rec}
+
+newtype TargetOptionFlag = TargetOptionFlag CUInt
+  deriving (Eq, Read, Show, Typeable, Data)
+#define TOF_Rec(n) { #n, LLVM_Hs_TargetOptionFlag_ ## n },
+#{inject TARGET_OPTION_FLAG, TargetOptionFlag, TargetOptionFlag, targetOptionFlag, TOF_Rec}
+
+newtype TypeKind = TypeKind CUInt
+  deriving (Eq, Read, Show, Typeable, Data)
+#define TK_Rec(n) { #n, LLVM ## n ## TypeKind },
+#{inject TYPE_KIND, TypeKind, TypeKind, typeKind, TK_Rec}
+
+#define COMMA ,
+#define IF_T(z) z
+#define IF_F(z)
+#define IF2(x) IF_ ## x
+#define IF(x) IF2(x)
+#define OR_TT T
+#define OR_TF T
+#define OR_FT T
+#define OR_FF F
+#define OR(x,y) OR_ ## x ## y
+newtype ParameterAttributeKind = ParameterAttributeKind CUInt
+  deriving (Eq, Read, Show, Typeable, Data)
+#define PAK_Rec(n,p,r,f) IF(OR(p,r))({ #n COMMA LLVM_Hs_AttributeKind_ ## n} COMMA)
+#{inject ATTRIBUTE_KIND, ParameterAttributeKind, ParameterAttributeKind, parameterAttributeKind, PAK_Rec}
+
+newtype FunctionAttributeKind = FunctionAttributeKind CUInt
+  deriving (Eq, Read, Show, Typeable, Data)
+#define FAK_Rec(n,p,r,f) IF(f)({ #n COMMA LLVM_Hs_AttributeKind_ ## n} COMMA)
+#{inject ATTRIBUTE_KIND, FunctionAttributeKind, FunctionAttributeKind, functionAttributeKind, FAK_Rec}
+
+newtype FloatSemantics = FloatSemantics CUInt
+  deriving (Eq, Read, Show, Typeable, Data)
+#define FS_Rec(n) { #n, LLVMFloatSemantics ## n },
+#{inject FLOAT_SEMANTICS, FloatSemantics, FloatSemantics, floatSemantics, FS_Rec}
+
+newtype VerifierFailureAction = VerifierFailureAction CUInt
+  deriving (Eq, Read, Show, Bits, Typeable, Data, Num)
+#define VFA_Rec(n) { #n, LLVM ## n ## Action },
+#{inject VERIFIER_FAILURE_ACTION, VerifierFailureAction, VerifierFailureAction, verifierFailureAction, VFA_Rec}
+
+newtype LibFunc = LibFunc CUInt
+  deriving (Eq, Read, Show, Bits, Typeable, Data, Num, Storable)
+#define LF_Rec(n) { #n, LLVMLibFunc__ ## n },
+#{inject LIB_FUNC, LibFunc, LibFunc, libFunc__, LF_Rec}
+
+newtype JITSymbolFlags = JITSymbolFlags CUInt
+  deriving (Eq, Read, Show, Bits, Typeable, Data, Num, Storable)
+#define SF_Rec(n) { #n, LLVMJITSymbolFlag ## n },
+#{inject JIT_SYMBOL_FLAG, JITSymbolFlags, JITSymbolFlags, jitSymbolFlags, SF_Rec}
diff --git a/src/LLVM/Internal/FFI/LibFunc.h b/src/LLVM/Internal/FFI/LibFunc.h
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/LibFunc.h
@@ -0,0 +1,332 @@
+#ifndef __LLVM_INTERNAL_FFI__LIB_FUNC__H__
+#define __LLVM_INTERNAL_FFI__LIB_FUNC__H__
+
+#define LLVM_HS_FOR_EACH_LIB_FUNC(macro)		\
+	macro(under_IO_getc)													\
+	macro(under_IO_putc)													\
+	macro(ZdaPv)																	\
+	macro(ZdaPvRKSt9nothrow_t)										\
+	macro(ZdlPv)																	\
+	macro(ZdlPvRKSt9nothrow_t)										\
+	macro(Znaj)																		\
+	macro(ZnajRKSt9nothrow_t)											\
+	macro(Znam)																		\
+	macro(ZnamRKSt9nothrow_t)											\
+	macro(Znwj)																		\
+	macro(ZnwjRKSt9nothrow_t)											\
+	macro(Znwm)																		\
+	macro(ZnwmRKSt9nothrow_t)											\
+	macro(cospi)																	\
+	macro(cospif)																	\
+	macro(cxa_atexit)															\
+	macro(cxa_guard_abort)												\
+	macro(cxa_guard_acquire)											\
+	macro(cxa_guard_release)											\
+	macro(dunder_isoc99_scanf)										\
+	macro(dunder_isoc99_sscanf)										\
+	macro(memcpy_chk)															\
+	macro(sincospi_stret)													\
+	macro(sincospif_stret)												\
+	macro(sinpi)																	\
+	macro(sinpif)																	\
+	macro(sqrt_finite)														\
+	macro(sqrtf_finite)														\
+	macro(sqrtl_finite)														\
+	macro(dunder_strdup)													\
+	macro(dunder_strndup)													\
+	macro(dunder_strtok_r)												\
+	macro(abs)																		\
+	macro(access)																	\
+	macro(acos)																		\
+	macro(acosf)																	\
+	macro(acosh)																	\
+	macro(acoshf)																	\
+	macro(acoshl)																	\
+	macro(acosl)																	\
+	macro(asin)																		\
+	macro(asinf)																	\
+	macro(asinh)																	\
+	macro(asinhf)																	\
+	macro(asinhl)																	\
+	macro(asinl)																	\
+	macro(atan)																		\
+	macro(atan2)																	\
+	macro(atan2f)																	\
+	macro(atan2l)																	\
+	macro(atanf)																	\
+	macro(atanh)																	\
+	macro(atanhf)																	\
+	macro(atanhl)																	\
+	macro(atanl)																	\
+	macro(atof)																		\
+	macro(atoi)																		\
+	macro(atol)																		\
+	macro(atoll)																	\
+	macro(bcmp)																		\
+	macro(bcopy)																	\
+	macro(bzero)																	\
+	macro(calloc)																	\
+	macro(cbrt)																		\
+	macro(cbrtf)																	\
+	macro(cbrtl)																	\
+	macro(ceil)																		\
+	macro(ceilf)																	\
+	macro(ceill)																	\
+	macro(chmod)																	\
+	macro(chown)																	\
+	macro(clearerr)																\
+	macro(closedir)																\
+	macro(copysign)																\
+	macro(copysignf)															\
+	macro(copysignl)															\
+	macro(cos)																		\
+	macro(cosf)																		\
+	macro(cosh)																		\
+	macro(coshf)																	\
+	macro(coshl)																	\
+	macro(cosl)																		\
+	macro(ctermid)																\
+	macro(exp)																		\
+	macro(exp10)																	\
+	macro(exp10f)																	\
+	macro(exp10l)																	\
+	macro(exp2)																		\
+	macro(exp2f)																	\
+	macro(exp2l)																	\
+	macro(expf)																		\
+	macro(expl)																		\
+	macro(expm1)																	\
+	macro(expm1f)																	\
+	macro(expm1l)																	\
+	macro(fabs)																		\
+	macro(fabsf)																	\
+	macro(fabsl)																	\
+	macro(fclose)																	\
+	macro(fdopen)																	\
+	macro(feof)																		\
+	macro(ferror)																	\
+	macro(fflush)																	\
+	macro(ffs)																		\
+	macro(ffsl)																		\
+	macro(ffsll)																	\
+	macro(fgetc)																	\
+	macro(fgetpos)																\
+	macro(fgets)																	\
+	macro(fileno)																	\
+	macro(fiprintf)																\
+	macro(flockfile)															\
+	macro(floor)																	\
+	macro(floorf)																	\
+	macro(floorl)																	\
+	macro(fmax)																		\
+	macro(fmaxf)																	\
+	macro(fmaxl)																	\
+	macro(fmin)																		\
+	macro(fminf)																	\
+	macro(fminl)																	\
+	macro(fmod)																		\
+	macro(fmodf)																	\
+	macro(fmodl)																	\
+	macro(fopen)																	\
+	macro(fopen64)																\
+	macro(fprintf)																\
+	macro(fputc)																	\
+	macro(fputs)																	\
+	macro(fread)																	\
+	macro(free)																		\
+	macro(frexp)																	\
+	macro(frexpf)																	\
+	macro(frexpl)																	\
+	macro(fscanf)																	\
+	macro(fseek)																	\
+	macro(fseeko)																	\
+	macro(fseeko64)																\
+	macro(fsetpos)																\
+	macro(fstat)																	\
+	macro(fstat64)																\
+	macro(fstatvfs)																\
+	macro(fstatvfs64)															\
+	macro(ftell)																	\
+	macro(ftello)																	\
+	macro(ftello64)																\
+	macro(ftrylockfile)														\
+	macro(funlockfile)														\
+	macro(fwrite)																	\
+	macro(getc)																		\
+	macro(getc_unlocked)													\
+	macro(getchar)																\
+	macro(getenv)																	\
+	macro(getitimer)															\
+	macro(getlogin_r)															\
+	macro(getpwnam)																\
+	macro(gets)																		\
+	macro(gettimeofday)														\
+	macro(htonl)																	\
+	macro(htons)																	\
+	macro(iprintf)																\
+	macro(isascii)																\
+	macro(isdigit)																\
+	macro(labs)																		\
+	macro(lchown)																	\
+	macro(ldexp)																	\
+	macro(ldexpf)																	\
+	macro(ldexpl)																	\
+	macro(llabs)																	\
+	macro(log)																		\
+	macro(log10)																	\
+	macro(log10f)																	\
+	macro(log10l)																	\
+	macro(log1p)																	\
+	macro(log1pf)																	\
+	macro(log1pl)																	\
+	macro(log2)																		\
+	macro(log2f)																	\
+	macro(log2l)																	\
+	macro(logb)																		\
+	macro(logbf)																	\
+	macro(logbl)																	\
+	macro(logf)																		\
+	macro(logl)																		\
+	macro(lstat)																	\
+	macro(lstat64)																\
+	macro(malloc)																	\
+	macro(memalign)																\
+	macro(memccpy)																\
+	macro(memchr)																	\
+	macro(memcmp)																	\
+	macro(memcpy)																	\
+	macro(memmove)																\
+	macro(memrchr)																\
+	macro(memset)																	\
+	macro(memset_pattern16)												\
+	macro(mkdir)																	\
+	macro(mktime)																	\
+	macro(modf)																		\
+	macro(modff)																	\
+	macro(modfl)																	\
+	macro(nearbyint)															\
+	macro(nearbyintf)															\
+	macro(nearbyintl)															\
+	macro(ntohl)																	\
+	macro(ntohs)																	\
+	macro(open)																		\
+	macro(open64)																	\
+	macro(opendir)																\
+	macro(pclose)																	\
+	macro(perror)																	\
+	macro(popen)																	\
+	macro(posix_memalign)													\
+	macro(pow)																		\
+	macro(powf)																		\
+	macro(powl)																		\
+	macro(pread)																	\
+	macro(printf)																	\
+	macro(putc)																		\
+	macro(putchar)																\
+	macro(puts)																		\
+	macro(pwrite)																	\
+	macro(qsort)																	\
+	macro(read)																		\
+	macro(readlink)																\
+	macro(realloc)																\
+	macro(reallocf)																\
+	macro(realpath)																\
+	macro(remove)																	\
+	macro(rename)																	\
+	macro(rewind)																	\
+	macro(rint)																		\
+	macro(rintf)																	\
+	macro(rintl)																	\
+	macro(rmdir)																	\
+	macro(round)																	\
+	macro(roundf)																	\
+	macro(roundl)																	\
+	macro(scanf)																	\
+	macro(setbuf)																	\
+	macro(setitimer)															\
+	macro(setvbuf)																\
+	macro(sin)																		\
+	macro(sinf)																		\
+	macro(sinh)																		\
+	macro(sinhf)																	\
+	macro(sinhl)																	\
+	macro(sinl)																		\
+	macro(siprintf)																\
+	macro(snprintf)																\
+	macro(sprintf)																\
+	macro(sqrt)																		\
+	macro(sqrtf)																	\
+	macro(sqrtl)																	\
+	macro(sscanf)																	\
+	macro(stat)																		\
+	macro(stat64)																	\
+	macro(statvfs)																\
+	macro(statvfs64)															\
+	macro(stpcpy)																	\
+	macro(stpncpy)																\
+	macro(strcasecmp)															\
+	macro(strcat)																	\
+	macro(strchr)																	\
+	macro(strcmp)																	\
+	macro(strcoll)																\
+	macro(strcpy)																	\
+	macro(strcspn)																\
+	macro(strdup)																	\
+	macro(strlen)																	\
+	macro(strncasecmp)														\
+	macro(strncat)																\
+	macro(strncmp)																\
+	macro(strncpy)																\
+	macro(strndup)																\
+	macro(strnlen)																\
+	macro(strpbrk)																\
+	macro(strrchr)																\
+	macro(strspn)																	\
+	macro(strstr)																	\
+	macro(strtod)																	\
+	macro(strtof)																	\
+	macro(strtok)																	\
+	macro(strtok_r)																\
+	macro(strtol)																	\
+	macro(strtold)																\
+	macro(strtoll)																\
+	macro(strtoul)																\
+	macro(strtoull)																\
+	macro(strxfrm)																\
+	macro(system)																	\
+	macro(tan)																		\
+	macro(tanf)																		\
+	macro(tanh)																		\
+	macro(tanhf)																	\
+	macro(tanhl)																	\
+	macro(tanl)																		\
+	macro(times)																	\
+	macro(tmpfile)																\
+	macro(tmpfile64)															\
+	macro(toascii)																\
+	macro(trunc)																	\
+	macro(truncf)																	\
+	macro(truncl)																	\
+	macro(uname)																	\
+	macro(ungetc)																	\
+	macro(unlink)																	\
+	macro(unsetenv)																\
+	macro(utime)																	\
+	macro(utimes)																	\
+	macro(valloc)																	\
+	macro(vfprintf)																\
+	macro(vfscanf)																\
+	macro(vprintf)																\
+	macro(vscanf)																	\
+	macro(vsnprintf)															\
+	macro(vsprintf)																\
+	macro(vsscanf)																\
+	macro(write)
+
+typedef enum {
+#define ENUM_CASE(x) LLVMLibFunc__ ## x,
+LLVM_HS_FOR_EACH_LIB_FUNC(ENUM_CASE)
+#undef ENUM_CASE
+} LLVMLibFunc;
+
+#endif
diff --git a/src/LLVM/Internal/FFI/MemoryBuffer.hs b/src/LLVM/Internal/FFI/MemoryBuffer.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/MemoryBuffer.hs
@@ -0,0 +1,28 @@
+{-# LANGUAGE
+  ForeignFunctionInterface
+  #-}
+module LLVM.Internal.FFI.MemoryBuffer where
+
+import LLVM.Prelude
+
+import Foreign.Ptr
+import Foreign.C
+
+import LLVM.Internal.FFI.LLVMCTypes
+
+data MemoryBuffer
+
+foreign import ccall unsafe "LLVMCreateMemoryBufferWithContentsOfFile" createMemoryBufferWithContentsOfFile ::
+  Ptr CChar -> Ptr (Ptr MemoryBuffer) -> Ptr (OwnerTransfered CString) -> IO LLVMBool
+
+foreign import ccall unsafe "LLVMCreateMemoryBufferWithMemoryRange" createMemoryBufferWithMemoryRange ::
+  Ptr CChar -> CSize -> CString -> LLVMBool -> IO (Ptr MemoryBuffer)
+
+foreign import ccall unsafe "LLVMGetBufferStart" getBufferStart ::
+  Ptr MemoryBuffer -> IO (Ptr CChar)
+
+foreign import ccall unsafe "LLVMGetBufferSize" getBufferSize ::
+  Ptr MemoryBuffer -> IO CSize
+
+foreign import ccall unsafe "LLVMDisposeMemoryBuffer" disposeMemoryBuffer ::
+  Ptr MemoryBuffer -> IO ()
diff --git a/src/LLVM/Internal/FFI/Metadata.hpp b/src/LLVM/Internal/FFI/Metadata.hpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/Metadata.hpp
@@ -0,0 +1,11 @@
+#include "llvm/IR/Metadata.h"
+#include "llvm-c/Core.h"
+
+namespace llvm {
+typedef struct LLVMOpaqueMetadata *LLVMMetadataRef;
+DEFINE_ISA_CONVERSION_FUNCTIONS(Metadata, LLVMMetadataRef)
+
+inline Metadata **unwrap(LLVMMetadataRef *Vals) {
+  return reinterpret_cast<Metadata**>(Vals);
+}
+}
diff --git a/src/LLVM/Internal/FFI/Metadata.hs b/src/LLVM/Internal/FFI/Metadata.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/Metadata.hs
@@ -0,0 +1,93 @@
+{-#
+  LANGUAGE
+  ForeignFunctionInterface
+  #-}
+
+module LLVM.Internal.FFI.Metadata where
+
+import LLVM.Prelude
+
+import Foreign.Ptr
+import Foreign.C
+
+import LLVM.Internal.FFI.Context
+import LLVM.Internal.FFI.PtrHierarchy
+import LLVM.Internal.FFI.LLVMCTypes
+
+foreign import ccall unsafe "LLVM_Hs_IsAMDString" isAMDString ::
+  Ptr Metadata -> IO (Ptr MDString)
+
+foreign import ccall unsafe "LLVM_Hs_IsAMDNode" isAMDNode ::
+  Ptr Metadata -> IO (Ptr MDNode)
+
+foreign import ccall unsafe "LLVM_Hs_IsAMDValue" isAMDValue ::
+  Ptr Metadata -> IO (Ptr MDValue)
+
+foreign import ccall unsafe "LLVM_Hs_IsAMetadataOperand" isAMetadataOperand ::
+  Ptr Value -> IO (Ptr MetadataAsVal)
+
+foreign import ccall unsafe "LLVM_Hs_GetMDValue" getMDValue ::
+  Ptr MDValue -> IO (Ptr Value)
+
+foreign import ccall unsafe "LLVM_Hs_GetMetadataOperand" getMetadataOperand ::
+  Ptr MetadataAsVal -> IO (Ptr Metadata)
+
+foreign import ccall unsafe "LLVMGetMDKindIDInContext" getMDKindIDInContext' ::
+  Ptr Context -> Ptr CChar -> CUInt -> IO MDKindID
+
+getMDKindIDInContext :: Ptr Context -> (Ptr CChar, CUInt) -> IO MDKindID
+getMDKindIDInContext ctx (c, n) = getMDKindIDInContext' ctx c n
+
+foreign import ccall unsafe "LLVM_Hs_GetMDKindNames" getMDKindNames ::
+  Ptr Context -> Ptr (Ptr CChar) -> Ptr CUInt -> CUInt -> IO CUInt
+
+foreign import ccall unsafe "LLVM_Hs_MDStringInContext" mdStringInContext' ::
+  Ptr Context -> CString -> CUInt -> IO (Ptr MDString)
+
+foreign import ccall unsafe "LLVM_Hs_MDValue" mdValue ::
+  Ptr Value -> IO (Ptr MDValue)
+
+foreign import ccall unsafe "LLVM_Hs_MetadataOperand" metadataOperand ::
+  Ptr Context -> Ptr Metadata -> IO (Ptr Value)
+
+mdStringInContext :: Ptr Context -> (CString, CUInt) -> IO (Ptr MDString)
+mdStringInContext ctx (p, n) = mdStringInContext' ctx p n
+
+foreign import ccall unsafe "LLVM_Hs_GetMDString" getMDString ::
+  Ptr MDString -> Ptr CUInt -> IO CString
+
+foreign import ccall unsafe "LLVM_Hs_MDNodeInContext" createMDNodeInContext' ::
+  Ptr Context -> Ptr (Ptr Metadata) -> CUInt -> IO (Ptr MDNode)
+
+createMDNodeInContext :: Ptr Context -> (CUInt, Ptr (Ptr Metadata)) -> IO (Ptr MDNode)
+createMDNodeInContext ctx (n, vs) = createMDNodeInContext' ctx vs n
+
+foreign import ccall unsafe "LLVM_Hs_CreateTemporaryMDNodeInContext" createTemporaryMDNodeInContext ::
+  Ptr Context -> IO (Ptr MDNode)
+
+foreign import ccall unsafe "LLVM_Hs_DestroyTemporaryMDNode" destroyTemporaryMDNode ::
+  Ptr MDNode -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_GetMDNodeNumOperands" getMDNodeNumOperands ::
+  Ptr MDNode -> IO CUInt
+
+foreign import ccall unsafe "LLVM_Hs_GetMDNodeOperands" getMDNodeOperands ::
+  Ptr MDNode -> Ptr (Ptr Metadata) -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_GetNamedMetadataName" getNamedMetadataName ::
+  Ptr NamedMetadata -> Ptr CUInt -> IO (Ptr CChar)
+
+foreign import ccall unsafe "LLVM_Hs_GetNamedMetadataNumOperands" getNamedMetadataNumOperands ::
+  Ptr NamedMetadata -> IO CUInt
+
+foreign import ccall unsafe "LLVM_Hs_GetNamedMetadataOperands" getNamedMetadataOperands ::
+  Ptr NamedMetadata -> Ptr (Ptr MDNode) -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_NamedMetadataAddOperands" namedMetadataAddOperands' ::
+  Ptr NamedMetadata -> Ptr (Ptr MDNode) -> CUInt -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_MetadataReplaceAllUsesWith" metadataReplaceAllUsesWith ::
+  Ptr MDNode -> Ptr Metadata -> IO ()
+
+namedMetadataAddOperands :: Ptr NamedMetadata -> (CUInt, Ptr (Ptr MDNode)) -> IO ()
+namedMetadataAddOperands nm (n, vs) = namedMetadataAddOperands' nm vs n
diff --git a/src/LLVM/Internal/FFI/MetadataC.cpp b/src/LLVM/Internal/FFI/MetadataC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/MetadataC.cpp
@@ -0,0 +1,151 @@
+#define __STDC_LIMIT_MACROS
+
+#include <iostream>
+#include "LLVM/Internal/FFI/Metadata.hpp"
+#include "llvm/Support/FormattedStream.h"
+
+#include "llvm/Config/llvm-config.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Metadata.h"
+#include "llvm-c/Core.h"
+
+using namespace llvm;
+
+extern "C" {
+
+LLVMMetadataRef LLVM_Hs_IsAMDString(LLVMMetadataRef md) {
+    if (isa<MDString>(unwrap(md))) {
+        return md;
+    }
+    return nullptr;
+}
+
+LLVMMetadataRef LLVM_Hs_MDStringInContext(LLVMContextRef c,
+                                               const char *str, unsigned slen) {
+    return wrap(MDString::get(*unwrap(c), StringRef(str, slen)));
+}
+
+const char *LLVM_Hs_GetMDString(LLVMMetadataRef md, unsigned* len) {
+    if (const MDString *S = dyn_cast<MDString>(unwrap(md))) {
+      *len = S->getString().size();
+      return S->getString().data();
+    }
+    *len = 0;
+    return nullptr;
+}
+
+LLVMMetadataRef LLVM_Hs_MDValue(LLVMValueRef v) {
+    return wrap(ValueAsMetadata::get(unwrap(v)));
+}
+
+LLVMValueRef LLVM_Hs_MetadataOperand(LLVMContextRef c, LLVMMetadataRef md) {
+    return wrap(MetadataAsValue::get(*unwrap(c), unwrap(md)));
+}
+
+LLVMMetadataRef LLVM_Hs_IsAMDNode(LLVMMetadataRef md) {
+    if (isa<MDNode>(unwrap(md))) {
+        return md;
+    }
+    return nullptr;
+}
+
+LLVMValueRef LLVM_Hs_GetMDValue(LLVMMetadataRef md) {
+    return wrap(unwrap<ValueAsMetadata>(md)->getValue());
+}
+
+LLVMMetadataRef LLVM_Hs_GetMetadataOperand(LLVMValueRef val) {
+    return wrap(unwrap<MetadataAsValue>(val)->getMetadata());
+}
+
+LLVMMetadataRef LLVM_Hs_MDNodeInContext(LLVMContextRef c,
+                                             LLVMMetadataRef *mds,
+                                             unsigned count) {
+    return wrap(MDNode::get(*unwrap(c), ArrayRef<Metadata *>(unwrap(mds), count)));
+}
+
+LLVMMetadataRef LLVM_Hs_IsAMDValue(LLVMMetadataRef md) {
+    if (isa<ValueAsMetadata>(unwrap(md))) {
+        return md;
+    }
+    return nullptr;
+}
+
+
+LLVMValueRef LLVM_Hs_IsAMetadataOperand(LLVMValueRef val) {
+    if (isa<MetadataAsValue>(unwrap(val))) {
+        return val;
+    }
+    return nullptr;
+}
+
+
+unsigned LLVM_Hs_GetMDKindNames(
+	LLVMContextRef c,
+	const char **s,
+	unsigned *l,
+	unsigned n
+) {
+	SmallVector<StringRef, 8> ns;
+	unwrap(c)->getMDKindNames(ns);
+	if (ns.size() <= n) {
+		for(unsigned i=0; i < ns.size(); ++i) {
+			s[i] = ns[i].data();
+			l[i] = ns[i].size();
+		}
+	}
+	return ns.size();
+}
+
+unsigned LLVM_Hs_GetMDNodeNumOperands(LLVMMetadataRef v) {
+	return unwrap<MDNode>(v)->getNumOperands();
+}
+
+void LLVM_Hs_NamedMetadataAddOperands(
+	NamedMDNode *n,
+	LLVMMetadataRef *ops,
+	unsigned nOps
+) {
+	for(unsigned i = 0; i != nOps; ++i) n->addOperand(unwrap<MDNode>(ops[i]));
+}
+
+const char *LLVM_Hs_GetNamedMetadataName(
+	NamedMDNode *n,
+	unsigned *len
+) {
+	StringRef s = n->getName();
+	*len = s.size();
+	return s.data();
+}
+
+unsigned LLVM_Hs_GetNamedMetadataNumOperands(NamedMDNode *n) {
+	return n->getNumOperands();
+}
+
+void LLVM_Hs_GetNamedMetadataOperands(NamedMDNode *n, LLVMMetadataRef *dest) {
+	for(unsigned i = 0; i != n->getNumOperands(); ++i)
+		dest[i] = wrap(n->getOperand(i));
+}
+
+LLVMMetadataRef LLVM_Hs_CreateTemporaryMDNodeInContext(LLVMContextRef c) {
+	return wrap(MDNode::getTemporary(*unwrap(c), ArrayRef<Metadata *>()).release());
+}
+
+void LLVM_Hs_DestroyTemporaryMDNode(LLVMMetadataRef v) {
+    MDNode::deleteTemporary(unwrap<MDNode>(v));
+}
+
+void LLVM_Hs_GetMDNodeOperands(LLVMMetadataRef md, LLVMMetadataRef *dest) {
+    const auto *N = cast<MDNode>(unwrap(md));
+    const unsigned numOperands = N->getNumOperands();
+    for (unsigned i = 0; i < numOperands; i++)
+        dest[i] = wrap(N->getOperand(i));
+}
+
+void LLVM_Hs_MetadataReplaceAllUsesWith(LLVMMetadataRef md, LLVMMetadataRef replacement) {
+    auto *Node = unwrap<MDNode>(md);
+    Node->replaceAllUsesWith(unwrap<Metadata>(replacement));
+    MDNode::deleteTemporary(Node);
+}
+
+}
+
diff --git a/src/LLVM/Internal/FFI/Module.hs b/src/LLVM/Internal/FFI/Module.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/Module.hs
@@ -0,0 +1,103 @@
+{-# LANGUAGE
+  ForeignFunctionInterface
+  #-}
+module LLVM.Internal.FFI.Module where
+
+import LLVM.Prelude
+
+import Foreign.Ptr
+import Foreign.C
+
+import LLVM.Internal.FFI.Context
+import LLVM.Internal.FFI.GlobalValue (COMDAT)
+import LLVM.Internal.FFI.LLVMCTypes
+import LLVM.Internal.FFI.PtrHierarchy
+import LLVM.Internal.FFI.Type
+
+data Module
+
+foreign import ccall unsafe "LLVMModuleCreateWithNameInContext" moduleCreateWithNameInContext ::
+  CString -> Ptr Context -> IO (Ptr Module)
+
+foreign import ccall unsafe "LLVMGetModuleContext" getModuleContext ::
+  Ptr Module -> IO (Ptr Context)
+
+foreign import ccall unsafe "LLVMDisposeModule" disposeModule ::
+  Ptr Module -> IO ()
+
+foreign import ccall unsafe "LLVMGetDataLayout" getDataLayout ::
+  Ptr Module -> IO CString
+
+foreign import ccall unsafe "LLVMSetDataLayout" setDataLayout ::
+  Ptr Module -> CString -> IO ()
+
+foreign import ccall unsafe "LLVMGetTarget" getTargetTriple ::
+  Ptr Module -> IO CString
+
+foreign import ccall unsafe "LLVMSetTarget" setTargetTriple ::
+  Ptr Module -> CString -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_GetModuleIdentifier" getModuleIdentifier ::
+  Ptr Module -> IO (OwnerTransfered CString)
+
+foreign import ccall unsafe "LLVM_Hs_GetSourceFileName" getSourceFileName ::
+  Ptr Module -> IO (OwnerTransfered CString)
+
+foreign import ccall unsafe "LLVM_Hs_SetSourceFileName" setSourceFileName ::
+  Ptr Module -> Ptr CChar -> IO ()
+
+foreign import ccall unsafe "LLVMGetFirstGlobal" getFirstGlobal ::
+  Ptr Module -> IO (Ptr GlobalVariable)
+
+foreign import ccall unsafe "LLVMGetNextGlobal" getNextGlobal ::
+  Ptr GlobalVariable -> IO (Ptr GlobalVariable)
+
+foreign import ccall unsafe "LLVM_Hs_GetFirstAlias" getFirstAlias ::
+  Ptr Module -> IO (Ptr GlobalAlias)
+
+foreign import ccall unsafe "LLVM_Hs_GetNextAlias" getNextAlias ::
+  Ptr GlobalAlias -> IO (Ptr GlobalAlias)
+
+foreign import ccall unsafe "LLVM_Hs_GetOrInsertCOMDAT" getOrInsertCOMDAT ::
+  Ptr Module -> CString -> IO (Ptr COMDAT)
+
+foreign import ccall unsafe "LLVMGetFirstFunction" getFirstFunction ::
+  Ptr Module -> IO (Ptr Function)
+
+foreign import ccall unsafe "LLVMGetNextFunction" getNextFunction ::
+  Ptr Function -> IO (Ptr Function)
+
+foreign import ccall unsafe "LLVM_Hs_GetFirstNamedMetadata" getFirstNamedMetadata ::
+  Ptr Module -> IO (Ptr NamedMetadata)
+
+foreign import ccall unsafe "LLVM_Hs_GetNextNamedMetadata" getNextNamedMetadata ::
+  Ptr NamedMetadata -> IO (Ptr NamedMetadata)
+
+foreign import ccall unsafe "LLVMAddGlobalInAddressSpace" addGlobalInAddressSpace ::
+  Ptr Module -> Ptr Type -> CString -> CUInt -> IO (Ptr GlobalVariable)
+
+foreign import ccall unsafe "LLVM_Hs_JustAddAlias" justAddAlias ::
+  Ptr Module -> Ptr Type -> AddrSpace -> CString -> IO (Ptr GlobalAlias)
+
+foreign import ccall unsafe "LLVMAddFunction" addFunction ::
+  Ptr Module -> CString -> Ptr Type -> IO (Ptr Function)
+
+foreign import ccall unsafe "LLVMGetNamedFunction" getNamedFunction ::
+  Ptr Module -> CString -> IO (Ptr Function)
+
+foreign import ccall unsafe "LLVM_Hs_GetOrAddNamedMetadata" getOrAddNamedMetadata ::
+  Ptr Module -> CString -> IO (Ptr NamedMetadata)
+
+foreign import ccall unsafe "LLVM_Hs_ModuleAppendInlineAsm" moduleAppendInlineAsm' ::
+  Ptr Module -> Ptr CChar -> CUInt -> IO ()
+
+newtype ModuleAsm a = ModuleAsm a
+
+moduleAppendInlineAsm :: Ptr Module -> ModuleAsm (Ptr CChar, CUInt) -> IO ()
+moduleAppendInlineAsm m (ModuleAsm (c, n)) = moduleAppendInlineAsm' m c n
+
+foreign import ccall unsafe "LLVM_Hs_ModuleGetInlineAsm" moduleGetInlineAsm ::
+  Ptr Module -> IO (ModuleAsm CString)
+
+foreign import ccall unsafe "LLVMLinkModules2" linkModules ::
+  Ptr Module -> Ptr Module -> IO LLVMBool
diff --git a/src/LLVM/Internal/FFI/ModuleC.cpp b/src/LLVM/Internal/FFI/ModuleC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/ModuleC.cpp
@@ -0,0 +1,70 @@
+#define __STDC_LIMIT_MACROS
+#include "llvm/IR/Module.h"
+#include "llvm-c/Core.h"
+
+using namespace llvm;
+
+extern "C" {
+
+char *LLVM_Hs_GetModuleIdentifier(LLVMModuleRef val) {
+	return strdup(unwrap(val)->getModuleIdentifier().c_str());
+}
+
+char *LLVM_Hs_GetSourceFileName(LLVMModuleRef val) {
+	return strdup(unwrap(val)->getSourceFileName().c_str());
+}
+
+void LLVM_Hs_SetSourceFileName(LLVMModuleRef val, const char* sourceFileName) {
+	return unwrap(val)->setSourceFileName(sourceFileName);
+}
+
+LLVMValueRef LLVM_Hs_GetFirstAlias(LLVMModuleRef m) {
+	Module *mod = unwrap(m);
+	Module::alias_iterator i = mod->alias_begin();
+    if (i == mod->alias_end()) {
+        return 0;
+    }
+    return wrap(&*i);
+}
+
+LLVMValueRef LLVM_Hs_GetNextAlias(LLVMValueRef a) {
+	GlobalAlias *alias = unwrap<GlobalAlias>(a);
+	Module::alias_iterator i(alias);
+	if (++i == alias->getParent()->alias_end()) return 0;
+	return wrap(&*i);
+}
+
+Comdat *LLVM_Hs_GetOrInsertCOMDAT(LLVMModuleRef m, const char *name) {
+  return unwrap(m)->getOrInsertComdat(name);
+}
+
+// TODO (cocreature): Figure out if we can just change the linkage here
+LLVMValueRef LLVM_Hs_JustAddAlias(LLVMModuleRef m, LLVMTypeRef ty, unsigned addrspace, const char *name) {
+	return wrap(GlobalAlias::create(unwrap(ty), addrspace, GlobalValue::ExternalLinkage, name, 0, unwrap(m)));
+}
+
+NamedMDNode *LLVM_Hs_GetOrAddNamedMetadata(LLVMModuleRef m, const char *name) {
+	return unwrap(m)->getOrInsertNamedMetadata(name);
+}
+
+NamedMDNode *LLVM_Hs_GetFirstNamedMetadata(LLVMModuleRef m) {
+	Module *mod = unwrap(m);
+	Module::named_metadata_iterator i = mod->named_metadata_begin();
+	return i == mod->named_metadata_end() ? 0 : &*i;
+}
+
+NamedMDNode *LLVM_Hs_GetNextNamedMetadata(NamedMDNode *a) {
+	Module::named_metadata_iterator i(a);
+	if (++i == a->getParent()->named_metadata_end()) return 0;
+	return &*i;
+}
+
+void LLVM_Hs_ModuleAppendInlineAsm(LLVMModuleRef m, const char *s, unsigned l) {
+	unwrap(m)->appendModuleInlineAsm(StringRef(s,l));
+}
+
+const char *LLVM_Hs_ModuleGetInlineAsm(LLVMModuleRef m) {
+	return unwrap(m)->getModuleInlineAsm().c_str();
+}
+
+}
diff --git a/src/LLVM/Internal/FFI/OrcJIT.h b/src/LLVM/Internal/FFI/OrcJIT.h
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/OrcJIT.h
@@ -0,0 +1,15 @@
+#ifndef __LLVM_INTERNAL_FFI__ORC_JIT__H__
+#define __LLVM_INTERNAL_FFI__ORC_JIT__H__
+
+#define LLVM_HS_FOR_EACH_JIT_SYMBOL_FLAG(macro) \
+	macro(None)                                      \
+	macro(Weak)                                      \
+	macro(Exported)                                  \
+
+typedef enum {
+#define ENUM_CASE(x) LLVMJITSymbolFlag ## x,
+LLVM_HS_FOR_EACH_JIT_SYMBOL_FLAG(ENUM_CASE)
+#undef ENUM_CASE
+} LLVMJITSymbolFlags;
+
+#endif
diff --git a/src/LLVM/Internal/FFI/OrcJIT.hs b/src/LLVM/Internal/FFI/OrcJIT.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/OrcJIT.hs
@@ -0,0 +1,51 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+
+module LLVM.Internal.FFI.OrcJIT where
+
+import LLVM.Prelude
+
+import Foreign.C
+import Foreign.Ptr
+
+import LLVM.Internal.FFI.DataLayout
+import LLVM.Internal.FFI.LLVMCTypes
+
+data JITSymbol
+data LambdaResolver
+data ObjectLinkingLayer
+
+newtype TargetAddress = TargetAddress Word64
+
+type SymbolResolverFn = CString -> Ptr JITSymbol -> IO ()
+
+foreign import ccall "wrapper" wrapSymbolResolverFn ::
+  SymbolResolverFn -> IO (FunPtr SymbolResolverFn)
+
+foreign import ccall safe "LLVM_Hs_disposeJITSymbol" disposeSymbol ::
+  Ptr JITSymbol -> IO ()
+
+foreign import ccall safe "LLVM_Hs_createLambdaResolver" createLambdaResolver ::
+  FunPtr SymbolResolverFn ->
+  FunPtr SymbolResolverFn ->
+  IO (Ptr LambdaResolver)
+
+foreign import ccall safe "LLVM_Hs_createObjectLinkingLayer" createObjectLinkingLayer ::
+  IO (Ptr ObjectLinkingLayer)
+
+foreign import ccall safe "LLVM_Hs_disposeObjectLinkingLayer" disposeObjectLinkingLayer ::
+  Ptr ObjectLinkingLayer -> IO ()
+
+foreign import ccall safe "LLVM_Hs_JITSymbol_getAddress" getAddress ::
+  Ptr JITSymbol -> IO TargetAddress
+
+foreign import ccall safe "LLVM_Hs_JITSymbol_getFlags" getFlags ::
+  Ptr JITSymbol -> IO JITSymbolFlags
+
+foreign import ccall safe "LLVM_Hs_setJITSymbol" setJITSymbol ::
+  Ptr JITSymbol -> TargetAddress -> JITSymbolFlags -> IO ()
+
+foreign import ccall safe "LLVM_Hs_getMangledSymbol" getMangledSymbol ::
+  Ptr CString -> CString -> Ptr DataLayout -> IO ()
+
+foreign import ccall safe "LLVM_Hs_disposeMangledSymbol" disposeMangledSymbol ::
+  CString -> IO ()
diff --git a/src/LLVM/Internal/FFI/OrcJIT/CompileOnDemandLayer.hs b/src/LLVM/Internal/FFI/OrcJIT/CompileOnDemandLayer.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/OrcJIT/CompileOnDemandLayer.hs
@@ -0,0 +1,74 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+module LLVM.Internal.FFI.OrcJIT.CompileOnDemandLayer where
+
+import LLVM.Prelude
+
+import Foreign.C
+import Foreign.Ptr
+
+import LLVM.Internal.FFI.DataLayout
+import LLVM.Internal.FFI.LLVMCTypes
+import LLVM.Internal.FFI.Module
+import LLVM.Internal.FFI.OrcJIT
+import LLVM.Internal.FFI.OrcJIT.IRCompileLayer (IRCompileLayer)
+import LLVM.Internal.FFI.PtrHierarchy
+
+data IndirectStubsManagerBuilder
+data JITCompileCallbackManager
+data CompileOnDemandLayer
+data Set a
+data ModuleSetHandle
+
+type PartitioningFn = Ptr Function -> Ptr (Set (Ptr Function)) -> IO ()
+
+foreign import ccall "wrapper" wrapPartitioningFn ::
+  PartitioningFn -> IO (FunPtr PartitioningFn)
+
+foreign import ccall "wrapper" wrapErrorHandler ::
+  IO () -> IO (FunPtr (IO ()))
+
+foreign import ccall safe "LLVM_Hs_createLocalCompileCallbackManager" createLocalCompileCallbackManager ::
+  CString -> TargetAddress -> IO (Ptr JITCompileCallbackManager)
+
+foreign import ccall safe "LLVM_Hs_disposeCallbackManager" disposeCallbackManager ::
+  Ptr JITCompileCallbackManager -> IO ()
+
+foreign import ccall safe "LLVM_Hs_createLocalIndirectStubsManagerBuilder" createLocalIndirectStubsManagerBuilder ::
+  CString -> IO (Ptr IndirectStubsManagerBuilder)
+
+foreign import ccall safe "LLVM_Hs_disposeIndirectStubsManagerBuilder" disposeIndirectStubsManagerBuilder ::
+  Ptr IndirectStubsManagerBuilder -> IO ()
+
+foreign import ccall safe "LLVM_Hs_insertFun" insertFun ::
+  Ptr (Set (Ptr Function)) -> Ptr Function -> IO ()
+
+foreign import ccall safe "LLVM_Hs_createCompileOnDemandLayer" createCompileOnDemandLayer ::
+  Ptr IRCompileLayer ->
+  FunPtr PartitioningFn ->
+  Ptr JITCompileCallbackManager ->
+  Ptr IndirectStubsManagerBuilder ->
+  LLVMBool ->
+  IO (Ptr CompileOnDemandLayer)
+
+foreign import ccall safe "LLVM_Hs_disposeCompileOnDemandLayer" disposeCompileOnDemandLayer ::
+  Ptr CompileOnDemandLayer ->
+  IO ()
+
+foreign import ccall safe "LLVM_Hs_CompileOnDemandLayer_addModuleSet" addModuleSet ::
+  Ptr CompileOnDemandLayer ->
+  Ptr DataLayout ->
+  Ptr (Ptr Module) ->
+  CUInt ->
+  Ptr LambdaResolver ->
+  IO (Ptr ModuleSetHandle)
+
+foreign import ccall safe "LLVM_Hs_CompileOnDemandLayer_removeModuleSet" removeModuleSet ::
+  Ptr CompileOnDemandLayer ->
+  Ptr ModuleSetHandle ->
+  IO ()
+
+foreign import ccall safe "LLVM_Hs_CompileOnDemandLayer_findSymbol" findSymbol ::
+  Ptr CompileOnDemandLayer ->
+  CString ->
+  LLVMBool ->
+  IO (Ptr JITSymbol)
diff --git a/src/LLVM/Internal/FFI/OrcJIT/IRCompileLayer.hs b/src/LLVM/Internal/FFI/OrcJIT/IRCompileLayer.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/OrcJIT/IRCompileLayer.hs
@@ -0,0 +1,36 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+module LLVM.Internal.FFI.OrcJIT.IRCompileLayer where
+
+import LLVM.Prelude
+
+import LLVM.Internal.FFI.DataLayout
+import LLVM.Internal.FFI.LLVMCTypes
+import LLVM.Internal.FFI.Module
+import LLVM.Internal.FFI.OrcJIT
+import LLVM.Internal.FFI.Target
+
+import Foreign.Ptr
+import Foreign.C
+
+data IRCompileLayer
+data ModuleSetHandle
+
+foreign import ccall safe "LLVM_Hs_createIRCompileLayer" createIRCompileLayer ::
+  Ptr ObjectLinkingLayer -> Ptr TargetMachine -> IO (Ptr IRCompileLayer)
+
+foreign import ccall safe "LLVM_Hs_disposeIRCompileLayer" disposeIRCompileLayer ::
+  Ptr IRCompileLayer -> IO ()
+
+foreign import ccall safe "LLVM_Hs_IRCompileLayer_addModuleSet" addModuleSet ::
+  Ptr IRCompileLayer ->
+  Ptr DataLayout ->
+  Ptr (Ptr Module) ->
+  CUInt ->
+  Ptr LambdaResolver ->
+  IO (Ptr ModuleSetHandle)
+
+foreign import ccall safe "LLVM_Hs_IRCompileLayer_removeModuleSet" removeModuleSet ::
+  Ptr IRCompileLayer -> Ptr ModuleSetHandle -> IO ()
+
+foreign import ccall safe "LLVM_Hs_IRCompileLayer_findSymbol" findSymbol ::
+  Ptr IRCompileLayer -> CString -> LLVMBool -> IO (Ptr JITSymbol)
diff --git a/src/LLVM/Internal/FFI/OrcJITC.cpp b/src/LLVM/Internal/FFI/OrcJITC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/OrcJITC.cpp
@@ -0,0 +1,256 @@
+#include "LLVM/Internal/FFI/OrcJIT.h"
+#include "LLVM/Internal/FFI/Target.hpp"
+#include "llvm/ExecutionEngine/Orc/CompileOnDemandLayer.h"
+#include "llvm/ExecutionEngine/Orc/CompileUtils.h"
+#include "llvm/ExecutionEngine/Orc/IRCompileLayer.h"
+#include "llvm/ExecutionEngine/Orc/IndirectionUtils.h"
+#include "llvm/ExecutionEngine/JITSymbol.h"
+#include "llvm/ExecutionEngine/Orc/LambdaResolver.h"
+#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
+#include "llvm/IR/Mangler.h"
+
+#include <type_traits>
+
+using namespace llvm;
+using namespace orc;
+
+typedef llvm::orc::ObjectLinkingLayer<> *LLVMObjectLinkingLayerRef;
+typedef llvm::orc::IRCompileLayer<llvm::orc::ObjectLinkingLayer<>>
+    LLVMIRCompileLayer;
+typedef LLVMIRCompileLayer *LLVMIRCompileLayerRef;
+typedef llvm::orc::CompileOnDemandLayer<LLVMIRCompileLayer>
+    LLVMCompileOnDemandLayer;
+typedef LLVMCompileOnDemandLayer *LLVMCompileOnDemandLayerRef;
+typedef llvm::orc::JITCompileCallbackManager *LLVMJITCompileCallbackManagerRef;
+typedef llvm::JITSymbol *LLVMJITSymbolRef;
+typedef LLVMIRCompileLayer::ModuleSetHandleT *LLVMModuleSetHandleRef;
+typedef LLVMCompileOnDemandLayer::ModuleSetHandleT *LLVMCODModuleSetHandleRef;
+typedef llvm::orc::LambdaResolver<
+    std::function<JITSymbol(const std::string &name)>,
+    std::function<JITSymbol(const std::string &name)>>
+    LLVMLambdaResolver;
+typedef LLVMLambdaResolver *LLVMLambdaResolverRef;
+typedef llvm::orc::IndirectStubsManager *LLVMIndirectStubsManagerRef;
+typedef std::function<std::unique_ptr<llvm::orc::IndirectStubsManager>()>
+    *LLVMIndirectStubsManagerBuilderRef;
+
+static std::string mangle(StringRef name, LLVMTargetDataRef dataLayout) {
+    std::string mangledName;
+    {
+        raw_string_ostream mangledNameStream(mangledName);
+        Mangler::getNameWithPrefix(mangledNameStream, name,
+                                   *unwrap(dataLayout));
+    }
+    return mangledName;
+}
+
+static std::vector<Module *> getModules(LLVMModuleRef *modules,
+                                        unsigned moduleCount,
+                                        LLVMTargetDataRef dataLayout) {
+    std::vector<Module *> moduleVec(moduleCount);
+    for (unsigned i = 0; i < moduleCount; ++i) {
+        moduleVec.at(i) = unwrap(modules[i]);
+        if (moduleVec.at(i)->getDataLayout().isDefault()) {
+            moduleVec.at(i)->setDataLayout(*unwrap(dataLayout));
+        }
+    }
+    return moduleVec;
+}
+
+extern "C" {
+LLVMIRCompileLayerRef
+LLVM_Hs_createIRCompileLayer(LLVMObjectLinkingLayerRef objectLayer,
+                                  LLVMTargetMachineRef tm) {
+    TargetMachine *tmm = unwrap(tm);
+    return new IRCompileLayer<ObjectLinkingLayer<>>(*objectLayer,
+                                                    SimpleCompiler(*tmm));
+}
+
+void LLVM_Hs_disposeIRCompileLayer(LLVMIRCompileLayerRef compileLayer) {
+    delete compileLayer;
+}
+
+LLVMJITSymbolRef
+LLVM_Hs_IRCompileLayer_findSymbol(LLVMIRCompileLayerRef compileLayer,
+                                       const char *name,
+                                       LLVMBool exportedSymbolsOnly) {
+    JITSymbol symbol = compileLayer->findSymbol(name, exportedSymbolsOnly);
+    return new JITSymbol(symbol);
+}
+
+void LLVM_Hs_disposeJITSymbol(LLVMJITSymbolRef symbol) { delete symbol; }
+
+LLVMLambdaResolverRef LLVM_Hs_createLambdaResolver(
+    void (*dylibResolver)(const char *, LLVMJITSymbolRef),
+    void (*externalResolver)(const char *, LLVMJITSymbolRef)) {
+    std::function<JITSymbol(const std::string &name)>
+        dylibResolverFun = [dylibResolver](
+            const std::string &name) -> JITSymbol {
+        JITSymbol symbol(nullptr);
+        dylibResolver(name.c_str(), &symbol);
+        return symbol;
+    };
+    std::function<JITSymbol(const std::string &name)>
+        externalResolverFun = [externalResolver](
+            const std::string &name) -> JITSymbol {
+        JITSymbol symbol(nullptr);
+        externalResolver(name.c_str(), &symbol);
+        return symbol;
+    };
+    auto lambdaResolver =
+        createLambdaResolver(dylibResolverFun, externalResolverFun);
+    return lambdaResolver.release();
+}
+
+LLVMModuleSetHandleRef LLVM_Hs_IRCompileLayer_addModuleSet(
+    LLVMIRCompileLayerRef compileLayer, LLVMTargetDataRef dataLayout,
+    LLVMModuleRef *modules, unsigned moduleCount,
+    LLVMLambdaResolverRef resolver) {
+    std::vector<Module *> moduleVec =
+        getModules(modules, moduleCount, dataLayout);
+    return new IRCompileLayer<ObjectLinkingLayer<>>::ModuleSetHandleT(
+        compileLayer->addModuleSet(
+            moduleVec, make_unique<SectionMemoryManager>(), resolver));
+}
+
+void LLVM_Hs_IRCompileLayer_removeModuleSet(
+    LLVMIRCompileLayerRef compileLayer,
+    LLVMModuleSetHandleRef moduleSetHandle) {
+    compileLayer->removeModuleSet(*moduleSetHandle);
+    delete moduleSetHandle;
+}
+
+LLVMObjectLinkingLayerRef LLVM_Hs_createObjectLinkingLayer() {
+    return new ObjectLinkingLayer<>();
+}
+
+void LLVM_Hs_disposeObjectLinkingLayer(
+    LLVMObjectLinkingLayerRef objectLayer) {
+    delete objectLayer;
+}
+
+static JITSymbolFlags unwrap(LLVMJITSymbolFlags f) {
+    JITSymbolFlags flags = JITSymbolFlags::None;
+#define ENUM_CASE(x)                                                           \
+    if (f & LLVMJITSymbolFlag##x)                                              \
+        flags |= JITSymbolFlags::x;
+    LLVM_HS_FOR_EACH_JIT_SYMBOL_FLAG(ENUM_CASE)
+#undef ENUM_CASE
+    return flags;
+}
+
+static LLVMJITSymbolFlags wrap(JITSymbolFlags f) {
+    unsigned r = 0;
+#define ENUM_CASE(x)                                                           \
+    if ((char)(f & JITSymbolFlags::x))                                         \
+        r |= (unsigned)LLVMJITSymbolFlag##x;
+    LLVM_HS_FOR_EACH_JIT_SYMBOL_FLAG(ENUM_CASE)
+#undef ENUM_CASE
+    return LLVMJITSymbolFlags(r);
+}
+
+JITTargetAddress
+LLVM_Hs_JITSymbol_getAddress(LLVMJITSymbolRef symbol) {
+    return symbol->getAddress();
+}
+
+LLVMJITSymbolFlags LLVM_Hs_JITSymbol_getFlags(LLVMJITSymbolRef symbol) {
+    return wrap(symbol->getFlags());
+}
+
+void LLVM_Hs_setJITSymbol(LLVMJITSymbolRef symbol,
+                               JITTargetAddress addr,
+                               LLVMJITSymbolFlags flags) {
+    *symbol = JITSymbol(addr, unwrap(flags));
+}
+
+void LLVM_Hs_getMangledSymbol(char **mangledSymbol, const char *symbol,
+                                   LLVMTargetDataRef dataLayout) {
+    std::string mangled = mangle(symbol, dataLayout);
+    *mangledSymbol = new char[mangled.size() + 1];
+    strcpy(*mangledSymbol, mangled.c_str());
+}
+
+void LLVM_Hs_disposeMangledSymbol(char *mangledSymbol) {
+    delete[] mangledSymbol;
+}
+
+LLVMJITCompileCallbackManagerRef LLVM_Hs_createLocalCompileCallbackManager(
+    const char *triple, JITTargetAddress errorHandler) {
+    return llvm::orc::createLocalCompileCallbackManager(Triple(triple),
+                                                        errorHandler)
+        .release();
+}
+
+void LLVM_Hs_disposeCallbackManager(
+    LLVMJITCompileCallbackManagerRef callbackManager) {
+    delete callbackManager;
+}
+
+LLVMIndirectStubsManagerBuilderRef
+LLVM_Hs_createLocalIndirectStubsManagerBuilder(const char *triple) {
+    return new std::function<std::unique_ptr<IndirectStubsManager>()>(
+        llvm::orc::createLocalIndirectStubsManagerBuilder(Triple(triple)));
+}
+
+void LLVM_Hs_disposeIndirectStubsManagerBuilder(
+    LLVMIndirectStubsManagerBuilderRef stubsManager) {
+    delete stubsManager;
+}
+
+void LLVM_Hs_insertFun(std::set<llvm::Function *> *set,
+                            llvm::Function *f) {
+    set->insert(f);
+}
+
+LLVMCompileOnDemandLayerRef LLVM_Hs_createCompileOnDemandLayer(
+    LLVMIRCompileLayerRef compileLayer,
+    void (*partitioningFtor)(llvm::Function *, std::set<llvm::Function *> *set),
+    LLVMJITCompileCallbackManagerRef callbackManager,
+    LLVMIndirectStubsManagerBuilderRef stubsManager,
+    LLVMBool cloneStubsIntoPartitions) {
+    return new LLVMCompileOnDemandLayer(
+        *compileLayer,
+        [partitioningFtor](llvm::Function &f) -> std::set<llvm::Function *> {
+            std::set<llvm::Function *> result;
+            partitioningFtor(&f, &result);
+            return result;
+        },
+        *callbackManager, *stubsManager, cloneStubsIntoPartitions);
+}
+
+void LLVM_Hs_disposeCompileOnDemandLayer(
+    LLVMCompileOnDemandLayerRef codLayer) {
+    delete codLayer;
+}
+
+LLVMCODModuleSetHandleRef LLVM_Hs_CompileOnDemandLayer_addModuleSet(
+    LLVMCompileOnDemandLayerRef compileLayer, LLVMTargetDataRef dataLayout,
+    LLVMModuleRef *modules, unsigned moduleCount,
+    LLVMLambdaResolverRef resolver) {
+    std::vector<Module *> moduleVec =
+        getModules(modules, moduleCount, dataLayout);
+    // We need to copy the resolver to make the use of unique_ptr (required by
+    // the LLVM API) safe
+    std::unique_ptr<LLVMLambdaResolver> uniqueResolver(
+        new LLVMLambdaResolver(*resolver));
+    return new LLVMCompileOnDemandLayer::ModuleSetHandleT(
+        compileLayer->addModuleSet(moduleVec,
+                                   make_unique<SectionMemoryManager>(),
+                                   std::move(uniqueResolver)));
+}
+
+void LLVM_Hs_CompileOnDemandLayer_removeModuleSet(
+    LLVMCompileOnDemandLayerRef compileLayer,
+    LLVMCODModuleSetHandleRef moduleSetHandle) {
+    compileLayer->removeModuleSet(*moduleSetHandle);
+    delete moduleSetHandle;
+}
+
+LLVMJITSymbolRef LLVM_Hs_CompileOnDemandLayer_findSymbol(
+    LLVMCompileOnDemandLayerRef compileLayer, const char *name,
+    LLVMBool exportedSymbolsOnly) {
+    JITSymbol symbol = compileLayer->findSymbol(name, exportedSymbolsOnly);
+    return new JITSymbol(symbol);
+}
+}
diff --git a/src/LLVM/Internal/FFI/PassManager.hs b/src/LLVM/Internal/FFI/PassManager.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/PassManager.hs
@@ -0,0 +1,134 @@
+{-# LANGUAGE
+  TemplateHaskell,
+  ForeignFunctionInterface,
+  CPP
+  #-}
+
+module LLVM.Internal.FFI.PassManager where
+
+import LLVM.Prelude
+
+import qualified Language.Haskell.TH as TH
+
+import Foreign.Ptr
+import Foreign.C
+
+import LLVM.Internal.FFI.LLVMCTypes
+import LLVM.Internal.FFI.PtrHierarchy
+import LLVM.Internal.FFI.Cleanup
+import LLVM.Internal.FFI.Module
+import LLVM.Internal.FFI.Target
+import LLVM.Internal.FFI.Transforms
+
+import qualified LLVM.Transforms as G
+
+data PassManager
+
+foreign import ccall unsafe "LLVMCreatePassManager" createPassManager ::
+  IO (Ptr PassManager)
+
+foreign import ccall unsafe "LLVMDisposePassManager" disposePassManager ::
+  Ptr PassManager -> IO ()
+
+foreign import ccall unsafe "LLVMRunPassManager" runPassManager ::
+  Ptr PassManager -> Ptr Module -> IO CUInt
+
+foreign import ccall unsafe "LLVMCreateFunctionPassManagerForModule" createFunctionPassManagerForModule ::
+  Ptr Module -> IO (Ptr PassManager)
+
+foreign import ccall unsafe "LLVMInitializeFunctionPassManager" initializeFunctionPassManager ::
+  Ptr PassManager -> IO CUInt
+
+foreign import ccall unsafe "LLVMRunFunctionPassManager" runFunctionPassManager ::
+  Ptr PassManager -> Ptr Value -> IO CUInt
+
+foreign import ccall unsafe "LLVMFinalizeFunctionPassManager" finalizeFunctionPassManager ::
+  Ptr PassManager -> IO CUInt
+
+foreign import ccall unsafe "LLVMAddAnalysisPasses" addAnalysisPasses ::
+  Ptr TargetMachine -> Ptr PassManager -> IO ()
+
+foreign import ccall unsafe "LLVMAddTargetLibraryInfo" addTargetLibraryInfoPass' ::
+  Ptr TargetLibraryInfo -> Ptr PassManager -> IO ()
+
+addTargetLibraryInfoPass :: Ptr PassManager -> Ptr TargetLibraryInfo -> IO ()
+addTargetLibraryInfoPass = flip addTargetLibraryInfoPass'
+
+$(do
+  let declareForeign :: TH.Name -> [TH.Type] -> TH.DecsQ
+      declareForeign hName extraParams = do
+        let n = TH.nameBase hName
+            passTypeMapping :: TH.Type -> TH.TypeQ
+            passTypeMapping t = case t of
+              TH.ConT h | h == ''Word -> [t| CUInt |]
+                        | h == ''G.GCOVVersion -> [t| CString |]
+              -- some of the LLVM methods for making passes use "-1" as a special value
+              -- handle those here
+              TH.AppT (TH.ConT mby) t' | mby == ''Maybe ->
+                case t' of
+                  TH.ConT h | h == ''Bool -> [t| NothingAsMinusOne Bool |]
+                            | h == ''Word -> [t| NothingAsMinusOne Word |]
+                            | h == ''FilePath -> [t| NothingAsEmptyString CString |]
+                  _ -> typeMapping t
+              _ -> typeMapping t
+        foreignDecl
+          (cName n)
+          ("add" ++ n ++ "Pass")
+          ([[t| Ptr PassManager |]]
+           ++ [[t| Ptr TargetMachine |] | needsTargetMachine n]
+           ++ map passTypeMapping extraParams)
+          (TH.tupleT 0)
+#if __GLASGOW_HASKELL__ < 800
+  TH.TyConI (TH.DataD _ _ _ cons _) <- TH.reify ''G.Pass
+#else
+  TH.TyConI (TH.DataD _ _ _ _ cons _) <- TH.reify ''G.Pass
+#endif
+  liftM concat $ forM cons $ \con -> case con of
+    TH.RecC n l -> declareForeign n [ t | (_,_,t) <- l ]
+    TH.NormalC n [] -> declareForeign n []
+    TH.NormalC n _ -> error "pass descriptor constructors with fields need to be records"
+ )
+
+data PassManagerBuilder
+
+foreign import ccall unsafe "LLVMPassManagerBuilderCreate" passManagerBuilderCreate ::
+    IO (Ptr PassManagerBuilder)
+
+foreign import ccall unsafe "LLVMPassManagerBuilderDispose" passManagerBuilderDispose ::
+    Ptr PassManagerBuilder -> IO ()
+
+foreign import ccall unsafe "LLVMPassManagerBuilderSetOptLevel" passManagerBuilderSetOptLevel ::
+    Ptr PassManagerBuilder -> CUInt -> IO ()
+
+foreign import ccall unsafe "LLVMPassManagerBuilderSetSizeLevel" passManagerBuilderSetSizeLevel ::
+    Ptr PassManagerBuilder -> CUInt -> IO ()
+
+foreign import ccall unsafe "LLVMPassManagerBuilderSetDisableUnitAtATime" passManagerBuilderSetDisableUnitAtATime ::
+    Ptr PassManagerBuilder -> LLVMBool -> IO ()
+
+foreign import ccall unsafe "LLVMPassManagerBuilderSetDisableUnrollLoops" passManagerBuilderSetDisableUnrollLoops ::
+    Ptr PassManagerBuilder -> CUInt -> IO ()
+
+foreign import ccall unsafe "LLVMPassManagerBuilderSetDisableSimplifyLibCalls" passManagerBuilderSetDisableSimplifyLibCalls ::
+    Ptr PassManagerBuilder -> LLVMBool -> IO ()
+
+foreign import ccall unsafe "LLVMPassManagerBuilderUseInlinerWithThreshold" passManagerBuilderUseInlinerWithThreshold ::
+    Ptr PassManagerBuilder -> CUInt -> IO ()
+
+foreign import ccall unsafe "LLVMPassManagerBuilderPopulateFunctionPassManager" passManagerBuilderPopulateFunctionPassManager ::
+    Ptr PassManagerBuilder -> Ptr PassManager -> IO ()
+
+foreign import ccall unsafe "LLVMPassManagerBuilderPopulateModulePassManager" passManagerBuilderPopulateModulePassManager ::
+    Ptr PassManagerBuilder -> Ptr PassManager -> IO ()
+
+foreign import ccall unsafe "LLVMPassManagerBuilderPopulateLTOPassManager" passManagerBuilderPopulateLTOPassManager ::
+    Ptr PassManagerBuilder -> Ptr PassManager -> CUChar -> CUChar -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_PassManagerBuilderSetLibraryInfo" passManagerBuilderSetLibraryInfo ::
+    Ptr PassManagerBuilder -> Ptr TargetLibraryInfo -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_PassManagerBuilderSetLoopVectorize" passManagerBuilderSetLoopVectorize ::
+    Ptr PassManagerBuilder -> LLVMBool -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_PassManagerBuilderSetSuperwordLevelParallelismVectorize" passManagerBuilderSetSuperwordLevelParallelismVectorize ::
+    Ptr PassManagerBuilder -> LLVMBool -> IO ()
diff --git a/src/LLVM/Internal/FFI/PassManagerC.cpp b/src/LLVM/Internal/FFI/PassManagerC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/PassManagerC.cpp
@@ -0,0 +1,248 @@
+#define __STDC_LIMIT_MACROS
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/Transforms/Scalar.h"
+#include "llvm/Transforms/Scalar/GVN.h"
+#include "llvm/Transforms/IPO.h"
+#include "llvm/Transforms/IPO/Internalize.h"
+#include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#include "llvm/Transforms/Vectorize.h"
+#include "llvm/Transforms/Instrumentation.h"
+#include "llvm/CodeGen/Passes.h"
+#include "llvm-c/Target.h"
+#include "llvm-c/Transforms/PassManagerBuilder.h"
+#include "llvm/Analysis/TargetLibraryInfo.h"
+#include "llvm/Target/TargetMachine.h"
+
+#include "llvm-c/Core.h"
+
+using namespace llvm;
+
+extern "C" {
+typedef struct LLVMOpaqueVectorizationConfig *LLVMVectorizationConfigRef;
+typedef struct LLVMOpaqueTargetLowering *LLVMTargetLoweringRef;
+typedef struct LLVMOpaqueTargetMachine *LLVMTargetMachineRef;
+}
+
+namespace llvm {
+inline TargetLowering *unwrap(LLVMTargetLoweringRef P) { 
+	return reinterpret_cast<TargetLowering *>(P);
+}
+
+inline LLVMTargetLoweringRef wrap(const TargetLowering *P) { 
+	return reinterpret_cast<LLVMTargetLoweringRef>(const_cast<TargetLowering *>(P));
+}
+
+inline TargetMachine *unwrap(LLVMTargetMachineRef P) {
+	return reinterpret_cast<TargetMachine*>(P);
+}
+
+inline LLVMTargetMachineRef wrap(const TargetMachine *P) {
+	return reinterpret_cast<LLVMTargetMachineRef>(const_cast<TargetMachine *>(P));
+}
+
+// Taken from llvm/lib/Transforms/IPO/PassManagerBuilder.cpp
+inline PassManagerBuilder *unwrap(LLVMPassManagerBuilderRef P) {
+    return reinterpret_cast<PassManagerBuilder*>(P);
+}
+
+inline TargetLibraryInfoImpl *unwrap(LLVMTargetLibraryInfoRef P) {
+  return reinterpret_cast<TargetLibraryInfoImpl*>(P);
+}
+
+}
+
+extern "C" {
+
+#define LLVM_HS_FOR_EACH_PASS_WITHOUT_LLVM_C_BINDING(macro) \
+	macro(BreakCriticalEdges) \
+	macro(DeadCodeElimination) \
+	macro(DeadInstElimination) \
+	macro(DemoteRegisterToMemory) \
+	macro(LCSSA) \
+	macro(LoopInstSimplify) \
+	macro(LowerAtomic) \
+	macro(LowerSwitch) \
+	macro(MergeFunctions) \
+	macro(PartialInlining) \
+	macro(Sinking) \
+	macro(StripDeadDebugInfo) \
+	macro(StripDebugDeclare) \
+	macro(StripNonDebugSymbols) \
+
+#define ENUM_CASE(p)								\
+void LLVM_Hs_Add ## p ## Pass(LLVMPassManagerRef PM) {	\
+	unwrap(PM)->add(create ## p ## Pass());			\
+}
+LLVM_HS_FOR_EACH_PASS_WITHOUT_LLVM_C_BINDING(ENUM_CASE)
+#undef ENUM_CASE
+
+void LLVM_Hs_AddCodeGenPreparePass(LLVMPassManagerRef PM, LLVMTargetMachineRef T) {
+	unwrap(PM)->add(createCodeGenPreparePass(unwrap(T)));
+}
+	
+void LLVM_Hs_AddGlobalValueNumberingPass(LLVMPassManagerRef PM, LLVMBool noLoads) {
+	unwrap(PM)->add(createGVNPass(noLoads));
+}
+
+void LLVM_Hs_AddInternalizePass(LLVMPassManagerRef PM, unsigned nExports, const char **exports) {
+    std::vector<std::string> exportList(nExports);
+    for (unsigned i = 0; i < nExports; ++i) {
+        exportList.at(i) = exports[i];
+    }
+    std::function<bool(const GlobalValue &)> mustPreserveGV = [exportList](const GlobalValue & gv) {
+        for (const auto& exp : exportList) {
+            if (gv.getName().equals(exp)) {
+                return true;
+            }
+        }
+        return false;
+    };
+    unwrap(PM)->add(createInternalizePass(std::move(mustPreserveGV)));
+}
+
+void LLVM_Hs_AddLoopStrengthReducePass(LLVMPassManagerRef PM) {
+	unwrap(PM)->add(createLoopStrengthReducePass());
+}
+
+void LLVM_Hs_AddLowerInvokePass(LLVMPassManagerRef PM) {
+	unwrap(PM)->add(createLowerInvokePass());
+}
+	
+void LLVM_Hs_AddSROAPass(LLVMPassManagerRef PM) {
+	unwrap(PM)->add(createSROAPass());
+}
+
+void LLVM_Hs_AddBasicBlockVectorizePass(
+	LLVMPassManagerRef PM,
+	unsigned vectorBits,
+	LLVMBool vectorizeBools,
+	LLVMBool vectorizeInts,
+	LLVMBool vectorizeFloats,
+	LLVMBool vectorizePointers,
+	LLVMBool vectorizeCasts,
+	LLVMBool vectorizeMath,
+	LLVMBool vectorizeFusedMultiplyAdd,
+	LLVMBool vectorizeSelect,
+	LLVMBool vectorizeCmp,
+	LLVMBool vectorizeGetElementPtr,
+	LLVMBool vectorizeMemoryOperations,
+	LLVMBool alignedOnly,
+	unsigned reqChainDepth,
+	unsigned searchLimit,
+	unsigned maxCandidatePairsForCycleCheck,
+	LLVMBool splatBreaksChain,
+	unsigned maxInstructions,
+	unsigned maxIterations,
+	LLVMBool powerOfTwoLengthsOnly,
+	LLVMBool noMemoryOperationBoost,
+	LLVMBool fastDependencyAnalysis
+) {
+	VectorizeConfig vectorizeConfig;
+	vectorizeConfig.VectorBits = vectorBits;
+	vectorizeConfig.VectorizeBools = vectorizeBools;
+	vectorizeConfig.VectorizeInts = vectorizeInts;
+	vectorizeConfig.VectorizeFloats = vectorizeFloats;
+	vectorizeConfig.VectorizePointers = vectorizePointers;
+	vectorizeConfig.VectorizeCasts = vectorizeCasts;
+	vectorizeConfig.VectorizeMath = vectorizeMath;
+	vectorizeConfig.VectorizeFMA = vectorizeFusedMultiplyAdd;
+	vectorizeConfig.VectorizeSelect = vectorizeSelect;
+	vectorizeConfig.VectorizeCmp = vectorizeCmp;
+	vectorizeConfig.VectorizeGEP = vectorizeGetElementPtr;
+	vectorizeConfig.VectorizeMemOps = vectorizeMemoryOperations;
+	vectorizeConfig.AlignedOnly = alignedOnly;
+	vectorizeConfig.ReqChainDepth = reqChainDepth;
+	vectorizeConfig.SearchLimit = searchLimit;
+	vectorizeConfig.MaxCandPairsForCycleCheck = maxCandidatePairsForCycleCheck;
+	vectorizeConfig.SplatBreaksChain = splatBreaksChain;
+	vectorizeConfig.MaxInsts = maxInstructions;
+	vectorizeConfig.MaxIter = maxIterations;
+	vectorizeConfig.Pow2LenOnly = powerOfTwoLengthsOnly;
+	vectorizeConfig.NoMemOpBoost = noMemoryOperationBoost;
+	vectorizeConfig.FastDep = fastDependencyAnalysis;	
+
+	unwrap(PM)->add(createBBVectorizePass(vectorizeConfig));
+}
+
+void LLVM_Hs_AddGCOVProfilerPass(
+	LLVMPassManagerRef PM,
+	LLVMBool emitNotes,
+	LLVMBool emitData,
+	const char *version,
+	LLVMBool useCfgChecksum,
+	LLVMBool noRedZone,
+	LLVMBool functionNamesInData
+) {
+	struct GCOVOptions options;
+	options.EmitNotes = emitNotes;
+	options.EmitData = emitData;
+	std::copy(version, version+4, options.Version);
+	options.UseCfgChecksum = useCfgChecksum;
+	options.NoRedZone = noRedZone;
+	options.FunctionNamesInData = functionNamesInData;
+	unwrap(PM)->add(createGCOVProfilerPass(options));
+}
+
+void LLVM_Hs_AddAddressSanitizerFunctionPass(
+	LLVMPassManagerRef PM
+) {
+	unwrap(PM)->add(createAddressSanitizerFunctionPass());
+}
+
+void LLVM_Hs_AddAddressSanitizerModulePass(
+	LLVMPassManagerRef PM
+) {
+	unwrap(PM)->add(createAddressSanitizerModulePass());
+}
+
+void LLVM_Hs_AddMemorySanitizerPass(
+	LLVMPassManagerRef PM,
+	LLVMBool trackOrigins
+) {
+	unwrap(PM)->add(createMemorySanitizerPass(trackOrigins));
+}
+
+void LLVM_Hs_AddThreadSanitizerPass(
+	LLVMPassManagerRef PM
+) {
+	unwrap(PM)->add(createThreadSanitizerPass());
+}
+
+void LLVM_Hs_AddBoundsCheckingPass(LLVMPassManagerRef PM) {
+	unwrap(PM)->add(createBoundsCheckingPass());
+}
+
+void LLVM_Hs_AddLoopVectorizePass(
+	LLVMPassManagerRef PM,
+	LLVMBool noUnrolling,
+	LLVMBool alwaysVectorize
+) {
+	unwrap(PM)->add(createLoopVectorizePass(noUnrolling, alwaysVectorize));
+}
+
+void LLVM_Hs_PassManagerBuilderSetLibraryInfo(
+	LLVMPassManagerBuilderRef PMB,
+	LLVMTargetLibraryInfoRef l
+) {
+	// The PassManager frees the TargetLibraryInfo when done,
+	// but we also free our ref, so give it a new copy.
+	unwrap(PMB)->LibraryInfo = new TargetLibraryInfoImpl(*unwrap(l));
+}
+
+void LLVM_Hs_PassManagerBuilderSetLoopVectorize(
+	LLVMPassManagerBuilderRef PMB,
+	LLVMBool runLoopVectorization
+) {
+	unwrap(PMB)->LoopVectorize = runLoopVectorization;
+}
+
+void LLVM_Hs_PassManagerBuilderSetSuperwordLevelParallelismVectorize(
+	LLVMPassManagerBuilderRef PMB,
+	LLVMBool runSLPVectorization
+) {
+	unwrap(PMB)->SLPVectorize = runSLPVectorization;
+}
+
+}
diff --git a/src/LLVM/Internal/FFI/PtrHierarchy.hs b/src/LLVM/Internal/FFI/PtrHierarchy.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/PtrHierarchy.hs
@@ -0,0 +1,134 @@
+{-# LANGUAGE
+  ForeignFunctionInterface,
+  MultiParamTypeClasses,
+  FunctionalDependencies,
+  UndecidableInstances,
+  CPP
+  #-}
+#if __GLASGOW_HASKELL__ < 710
+{-# LANGUAGE OverlappingInstances #-}
+#define CPP_OVERLAPPING
+#else
+#define CPP_OVERLAPPING {-# OVERLAPPING #-}
+#endif
+-- | This module defines typeclasses to represent the relationships of an object-oriented inheritance hierarchy
+module LLVM.Internal.FFI.PtrHierarchy where
+
+import LLVM.Prelude
+
+import Foreign.Ptr
+
+-- | a class to represent safe casting of pointers to objects of descendant-classes to ancestor-classes.
+class DescendentOf a b where
+    upCast :: Ptr b -> Ptr a
+    upCast = castPtr
+
+-- | trivial casts
+instance CPP_OVERLAPPING DescendentOf a a where
+    upCast = id
+
+-- | a class to represent direct parent-child relationships
+class ChildOf b c | c -> b
+
+-- | ancestor-descentant relationships are build out of parent-child relationships
+instance (DescendentOf a b, ChildOf b c) => DescendentOf a c
+
+-- | <http://llvm.org/doxygen/classllvm_1_1Value.html>
+data Value
+
+-- | <http://llvm.org/doxygen/classllvm_1_1Constant.html>
+data Constant
+
+instance ChildOf User Constant
+
+-- | <http://llvm.org/doxygen/classllvm_1_1GlobalValue.html>
+data GlobalValue
+
+instance ChildOf Constant GlobalValue
+
+-- | <http://llvm.org/doxygen/classllvm_1_1GlobalObject.html>
+data GlobalObject
+
+instance ChildOf GlobalValue GlobalObject
+
+-- | <http://llvm.org/doxygen/classllvm_1_1GlobalVariable.html>
+data GlobalVariable
+
+instance ChildOf GlobalObject GlobalVariable
+
+-- | <http://llvm.org/doxygen/classllvm_1_1GlobalAlias.html>
+data GlobalAlias
+
+instance ChildOf GlobalValue GlobalAlias
+
+-- | <http://llvm.org/doxygen/classllvm_1_1Function.html>
+data Function
+
+instance ChildOf GlobalObject Function
+
+-- | <http://llvm.org/doxygen/classllvm_1_1BasicBlock.html>
+data BasicBlock
+
+instance ChildOf Value BasicBlock
+
+-- | <http://llvm.org/doxygen/classllvm_1_1Argument.html>
+data Parameter
+
+instance ChildOf Value Parameter
+
+-- | <http://llvm.org/doxygen/classllvm_1_1Instruction.html>
+data Instruction
+
+instance ChildOf User Instruction
+
+-- | <http://llvm.org/doxygen/classllvm_1_1BinaryOperator.html>
+data BinaryOperator
+
+instance ChildOf Instruction BinaryOperator
+
+-- | <http://llvm.org/doxygen/classllvm_1_1User.html>
+data User
+
+instance ChildOf Value User
+
+-- | <http://llvm.org/doxygen/classllvm_1_1MDNode.html>
+data MDNode
+
+instance ChildOf Metadata MDNode
+
+-- | <http://llvm.org/doxygen/classllvm_1_1MDString.html>
+data MDString
+
+instance ChildOf Metadata MDString
+
+-- | http://llvm.org/doxygen/classllvm_1_1ValueAsMetadata.html
+data MDValue
+
+instance ChildOf Metadata MDValue
+
+-- | <http://llvm.org/doxygen/classllvm_1_1NamedMDNode.html>
+data NamedMetadata
+
+-- | <http://llvm.org/doxygen/classllvm_1_1InlineAsm.html>
+data InlineAsm
+
+instance ChildOf Value InlineAsm
+
+-- | <http://llvm.org/doxygen/classllvm_1_1Type.html>
+data Type
+
+-- | <http://llvm.org/doxygen/classllvm_1_1Metadata.html>
+data Metadata
+
+-- | <http://www.llvm.org/docs/doxygen/html/classllvm_1_1MetadataAsValue.html>
+data MetadataAsVal
+
+instance ChildOf Value MetadataAsVal
+
+-- | <http://llvm.org/docs/doxygen/html/classllvm_1_1raw__ostream.html>
+data RawOStream
+
+-- | <http://llvm.org/docs/doxygen/html/classllvm_1_1raw__pwrite__stream.html>
+data RawPWriteStream
+
+instance ChildOf RawOStream RawPWriteStream
diff --git a/src/LLVM/Internal/FFI/RawOStream.hs b/src/LLVM/Internal/FFI/RawOStream.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/RawOStream.hs
@@ -0,0 +1,35 @@
+{-# LANGUAGE
+  ForeignFunctionInterface
+  #-}
+module LLVM.Internal.FFI.RawOStream where
+
+import LLVM.Prelude
+
+import Foreign.Ptr
+import Foreign.C
+import Control.Exception (bracket)
+
+import LLVM.Internal.FFI.ByteRangeCallback
+import LLVM.Internal.FFI.LLVMCTypes
+import LLVM.Internal.FFI.PtrHierarchy
+
+type RawPWriteStreamCallback = Ptr RawPWriteStream -> IO ()
+
+foreign import ccall "wrapper" wrapRawPWriteStreamCallback ::
+  RawPWriteStreamCallback -> IO (FunPtr RawPWriteStreamCallback)
+
+foreign import ccall safe "LLVM_Hs_WithFileRawPWriteStream" withFileRawPWriteStream' ::
+  CString -> LLVMBool -> LLVMBool -> Ptr (OwnerTransfered CString) -> FunPtr RawPWriteStreamCallback -> IO LLVMBool
+
+withFileRawPWriteStream :: CString -> LLVMBool -> LLVMBool -> Ptr (OwnerTransfered CString) -> RawPWriteStreamCallback -> IO LLVMBool
+withFileRawPWriteStream p ex bin err c =
+  bracket (wrapRawPWriteStreamCallback c) freeHaskellFunPtr (withFileRawPWriteStream' p ex bin err)
+
+foreign import ccall safe "LLVM_Hs_WithBufferRawPWriteStream" withBufferRawPWriteStream' ::
+  FunPtr ByteRangeCallback -> FunPtr RawPWriteStreamCallback -> IO ()
+
+withBufferRawPWriteStream :: ByteRangeCallback -> RawPWriteStreamCallback -> IO ()
+withBufferRawPWriteStream oc c =
+  bracket (wrapRawPWriteStreamCallback c) freeHaskellFunPtr $ \c ->
+  bracket (wrapByteRangeCallback oc) freeHaskellFunPtr $ \oc ->
+    withBufferRawPWriteStream' oc c
diff --git a/src/LLVM/Internal/FFI/RawOStreamC.cpp b/src/LLVM/Internal/FFI/RawOStreamC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/RawOStreamC.cpp
@@ -0,0 +1,36 @@
+#define __STDC_LIMIT_MACROS
+#include "llvm-c/Core.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using sys::fs::F_None;
+using sys::fs::F_Excl;
+using sys::fs::F_Text;
+
+extern "C" {
+
+LLVMBool
+LLVM_Hs_WithFileRawPWriteStream(const char *filename, LLVMBool excl,
+                                LLVMBool text, char **error,
+                                void (&callback)(raw_pwrite_stream &ostream)) {
+    std::error_code e;
+    raw_fd_ostream os(filename, e,
+                      (excl ? F_Excl : F_None) | (text ? F_Text : F_None));
+    if (e) {
+        *error = strdup(e.message().c_str());
+        return false;
+    }
+    callback(os);
+    return true;
+}
+
+void LLVM_Hs_WithBufferRawPWriteStream(
+    void (&outputCallback)(const char *start, size_t length),
+    void (&streamCallback)(raw_pwrite_stream &ostream)) {
+    SmallString<0> s;
+    raw_svector_ostream os(s);
+    streamCallback(os);
+    outputCallback(os.str().data(), s.size());
+}
+}
diff --git a/src/LLVM/Internal/FFI/SMDiagnostic.h b/src/LLVM/Internal/FFI/SMDiagnostic.h
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/SMDiagnostic.h
@@ -0,0 +1,15 @@
+#ifndef __LLVM_INTERNAL_FFI__SMDIAGNOSTIC__
+#define __LLVM_INTERNAL_FFI__SMDIAGNOSTIC__
+
+#define LLVM_HS_FOR_EACH_DIAGNOSTIC_KIND(macro) \
+	macro(Error) \
+	macro(Warning) \
+	macro(Note)
+
+typedef enum {
+#define ENUM_CASE(k) LLVMDiagnosticKind ## k,
+LLVM_HS_FOR_EACH_DIAGNOSTIC_KIND(ENUM_CASE)
+#undef ENUM_CASE
+} LLVMDiagnosticKind;
+
+#endif
diff --git a/src/LLVM/Internal/FFI/SMDiagnostic.hs b/src/LLVM/Internal/FFI/SMDiagnostic.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/SMDiagnostic.hs
@@ -0,0 +1,40 @@
+{-# LANGUAGE
+  ForeignFunctionInterface
+  #-}
+-- | FFI functions for handling the LLVM SMDiagnostic class
+module LLVM.Internal.FFI.SMDiagnostic where
+
+import LLVM.Prelude
+
+import Foreign.Ptr
+import Foreign.C
+
+import LLVM.Internal.FFI.LLVMCTypes
+
+data SMDiagnostic
+
+-- | allocate an SMDiagnostic object
+foreign import ccall unsafe "LLVM_Hs_CreateSMDiagnostic" createSMDiagnostic ::
+  IO (Ptr SMDiagnostic)
+
+foreign import ccall unsafe "LLVM_Hs_DisposeSMDiagnostic" disposeSMDiagnostic ::
+  Ptr SMDiagnostic -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_GetSMDiagnosticKind" getSMDiagnosticKind ::
+  Ptr SMDiagnostic -> IO DiagnosticKind
+
+foreign import ccall unsafe "LLVM_Hs_GetSMDiagnosticLineNo" getSMDiagnosticLineNo ::
+  Ptr SMDiagnostic -> IO CInt 
+
+foreign import ccall unsafe "LLVM_Hs_GetSMDiagnosticColumnNo" getSMDiagnosticColumnNo ::
+  Ptr SMDiagnostic -> IO CInt 
+
+foreign import ccall unsafe "LLVM_Hs_GetSMDiagnosticFilename" getSMDiagnosticFilename ::
+  Ptr SMDiagnostic -> Ptr CUInt -> IO CString 
+
+foreign import ccall unsafe "LLVM_Hs_GetSMDiagnosticMessage" getSMDiagnosticMessage ::
+  Ptr SMDiagnostic -> Ptr CUInt -> IO CString 
+
+foreign import ccall unsafe "LLVM_Hs_GetSMDiagnosticLineContents" getSMDiagnosticLineContents ::
+  Ptr SMDiagnostic -> Ptr CUInt -> IO CString 
+
diff --git a/src/LLVM/Internal/FFI/SMDiagnosticC.cpp b/src/LLVM/Internal/FFI/SMDiagnosticC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/SMDiagnosticC.cpp
@@ -0,0 +1,42 @@
+#define __STDC_LIMIT_MACROS
+#include "llvm/Config/llvm-config.h"
+#include "llvm/IR/LLVMContext.h"
+
+#include "llvm/Support/SourceMgr.h"
+#include "llvm-c/Core.h"
+
+#include "LLVM/Internal/FFI/SMDiagnostic.h"
+
+using namespace llvm;
+
+extern "C" {
+
+SMDiagnostic *LLVM_Hs_CreateSMDiagnostic() { return new SMDiagnostic(); }
+void LLVM_Hs_DisposeSMDiagnostic(SMDiagnostic *p) { delete p; }
+
+LLVMDiagnosticKind LLVM_Hs_GetSMDiagnosticKind(SMDiagnostic *p) {
+	switch(p->getKind()) {
+#define ENUM_CASE(k) case SourceMgr::DK_ ## k: return LLVMDiagnosticKind ## k;
+		LLVM_HS_FOR_EACH_DIAGNOSTIC_KIND(ENUM_CASE)
+#undef ENUM_CASE
+	default: return LLVMDiagnosticKind(0);
+	}
+}
+
+int LLVM_Hs_GetSMDiagnosticLineNo(SMDiagnostic *p) { return p->getLineNo(); }
+int LLVM_Hs_GetSMDiagnosticColumnNo(SMDiagnostic *p) { return p->getColumnNo(); }
+
+const char *LLVM_Hs_GetSMDiagnosticFilename(SMDiagnostic *p, unsigned *len) { 
+	*len = p->getFilename().size();
+	return p->getFilename().data();
+}
+const char *LLVM_Hs_GetSMDiagnosticMessage(SMDiagnostic *p, unsigned *len) {
+	*len = p->getMessage().size();
+	return p->getMessage().data();
+}
+const char *LLVM_Hs_GetSMDiagnosticLineContents(SMDiagnostic *p, unsigned *len) {
+	*len = p->getLineContents().size();
+	return p->getLineContents().data();
+}
+
+}
diff --git a/src/LLVM/Internal/FFI/Target.h b/src/LLVM/Internal/FFI/Target.h
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/Target.h
@@ -0,0 +1,71 @@
+#ifndef __LLVM_INTERNAL_FFI__TARGET__H__
+#define __LLVM_INTERNAL_FFI__TARGET__H__
+
+#define LLVM_HS_FOR_EACH_RELOC_MODEL(macro)	\
+	macro(Default, Default)													\
+	macro(Static, Static)														\
+	macro(PIC, PIC_)																\
+	macro(DynamicNoPic, DynamicNoPIC)
+
+#define LLVM_HS_FOR_EACH_CODE_MODEL(macro) \
+	macro(Default)																\
+	macro(JITDefault)															\
+	macro(Small)																	\
+	macro(Kernel)																	\
+	macro(Medium)																	\
+	macro(Large)
+
+#define LLVM_HS_FOR_EACH_CODE_GEN_OPT_LEVEL(macro) \
+	macro(None)																						\
+	macro(Less)																						\
+	macro(Default)																				\
+	macro(Aggressive)
+
+#define LLVM_HS_FOR_EACH_CODE_GEN_FILE_TYPE(macro)	\
+	macro(Assembly)                                       \
+	macro(Object)
+
+#define LLVM_HS_FOR_EACH_TARGET_OPTION_FLAG(macro)	\
+	macro(PrintMachineCode)																\
+	macro(LessPreciseFPMADOption)													\
+	macro(UnsafeFPMath)																		\
+	macro(NoInfsFPMath)																		\
+	macro(NoNaNsFPMath)																		\
+	macro(HonorSignDependentRoundingFPMathOption)					\
+	macro(NoZerosInBSS)																		\
+	macro(GuaranteedTailCallOpt)													\
+	macro(EnableFastISel)																	\
+	macro(UseInitArray)																		\
+	macro(DisableIntegratedAS)														\
+	macro(CompressDebugSections)													\
+	macro(TrapUnreachable)
+
+typedef enum {
+#define ENUM_CASE(n) LLVM_Hs_TargetOptionFlag_ ## n,
+	LLVM_HS_FOR_EACH_TARGET_OPTION_FLAG(ENUM_CASE)
+#undef ENUM_CASE
+} LLVM_Hs_TargetOptionFlag;
+
+#define LLVM_HS_FOR_EACH_FLOAT_ABI(macro)	\
+	macro(Default)																\
+	macro(Soft)																		\
+	macro(Hard)
+
+typedef enum {
+#define ENUM_CASE(n) LLVM_Hs_FloatABI_ ## n,
+	LLVM_HS_FOR_EACH_FLOAT_ABI(ENUM_CASE)
+#undef ENUM_CASE
+} LLVM_Hs_FloatABI;
+
+#define LLVM_HS_FOR_EACH_FP_OP_FUSION_MODE(macro)	\
+	macro(Fast)																						\
+	macro(Standard)																				\
+	macro(Strict)
+
+typedef enum {
+#define ENUM_CASE(n) LLVM_Hs_FPOpFusionMode_ ## n,
+	LLVM_HS_FOR_EACH_FP_OP_FUSION_MODE(ENUM_CASE)
+#undef ENUM_CASE
+} LLVM_Hs_FPOpFusionMode;
+
+#endif
diff --git a/src/LLVM/Internal/FFI/Target.hpp b/src/LLVM/Internal/FFI/Target.hpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/Target.hpp
@@ -0,0 +1,11 @@
+#ifndef __LLVM_INTERNAL_FFI__TARGET__HPP__
+#define __LLVM_INTERNAL_FFI__TARGET__HPP__
+
+#include "llvm-c/TargetMachine.h"
+#include "llvm/Target/TargetMachine.h"
+
+namespace llvm {
+DEFINE_SIMPLE_CONVERSION_FUNCTIONS(TargetMachine, LLVMTargetMachineRef)
+}
+
+#endif
diff --git a/src/LLVM/Internal/FFI/Target.hs b/src/LLVM/Internal/FFI/Target.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/Target.hs
@@ -0,0 +1,141 @@
+{-# LANGUAGE
+  ForeignFunctionInterface,
+  GeneralizedNewtypeDeriving
+  #-}
+module LLVM.Internal.FFI.Target where
+
+import LLVM.Prelude
+
+import Foreign.Ptr
+import Foreign.C
+
+import LLVM.Internal.FFI.DataLayout
+import LLVM.Internal.FFI.LLVMCTypes
+import LLVM.Internal.FFI.MemoryBuffer
+import LLVM.Internal.FFI.Module
+import LLVM.Internal.FFI.PtrHierarchy
+
+data Target
+
+foreign import ccall unsafe "LLVM_Hs_InitializeNativeTarget" initializeNativeTarget ::
+    IO LLVMBool
+
+foreign import ccall unsafe "LLVM_Hs_LookupTarget" lookupTarget ::
+    CString -> CString -> Ptr (OwnerTransfered CString) -> Ptr (OwnerTransfered CString) -> IO (Ptr Target)
+
+data TargetOptions
+
+foreign import ccall unsafe "LLVM_Hs_CreateTargetOptions" createTargetOptions ::
+  IO (Ptr TargetOptions)
+
+foreign import ccall unsafe "LLVM_Hs_SetTargetOptionFlag" setTargetOptionFlag ::
+  Ptr TargetOptions -> TargetOptionFlag -> LLVMBool -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_GetTargetOptionFlag" getTargetOptionsFlag ::
+  Ptr TargetOptions -> TargetOptionFlag -> IO LLVMBool
+
+foreign import ccall unsafe "LLVM_Hs_SetStackAlignmentOverride" setStackAlignmentOverride ::
+  Ptr TargetOptions -> CUInt -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_GetStackAlignmentOverride" getStackAlignmentOverride ::
+  Ptr TargetOptions -> IO CUInt
+
+foreign import ccall unsafe "LLVM_Hs_SetFloatABIType" setFloatABIType ::
+  Ptr TargetOptions -> FloatABIType -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_GetFloatABIType" getFloatABIType ::
+  Ptr TargetOptions -> IO FloatABIType
+
+foreign import ccall unsafe "LLVM_Hs_SetAllowFPOpFusion" setAllowFPOpFusion ::
+  Ptr TargetOptions -> FPOpFusionMode -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_GetAllowFPOpFusion" getAllowFPOpFusion ::
+  Ptr TargetOptions -> IO FPOpFusionMode
+
+foreign import ccall unsafe "LLVM_Hs_DisposeTargetOptions" disposeTargetOptions ::
+  Ptr TargetOptions -> IO ()
+
+data TargetMachine
+
+foreign import ccall unsafe "LLVMCreateTargetMachine" createTargetMachine ::
+  Ptr Target
+  -> CString
+  -> CString
+  -> CString
+  -> Ptr TargetOptions
+  -> RelocModel
+  -> CodeModel
+  -> CodeGenOptLevel
+  -> IO (Ptr TargetMachine)
+
+foreign import ccall unsafe "LLVMDisposeTargetMachine" disposeTargetMachine ::
+  Ptr TargetMachine -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_TargetMachineEmit" targetMachineEmit ::
+  Ptr TargetMachine
+  -> Ptr Module
+  -> Ptr RawPWriteStream
+  -> CodeGenFileType
+  -> Ptr (OwnerTransfered CString)
+  -> IO LLVMBool
+
+foreign import ccall unsafe "LLVMTargetMachineEmitToFile" targetMachineEmitToFile ::
+  Ptr TargetMachine
+  -> Ptr Module
+  -> CString
+  -> CodeGenFileType
+  -> Ptr (OwnerTransfered CString)
+  -> IO LLVMBool
+
+foreign import ccall unsafe "LLVMTargetMachineEmitToMemoryBuffer" targetMachineEmitToMemoryBuffer ::
+  Ptr TargetMachine
+  -> Ptr Module
+  -> CodeGenFileType
+  -> Ptr (OwnerTransfered CString)
+  -> Ptr (Ptr MemoryBuffer)
+  -> IO LLVMBool
+
+data TargetLowering
+
+-- foreign import ccall unsafe "LLVM_Hs_GetTargetLowering" getTargetLowering ::
+--   Ptr TargetMachine -> IO (Ptr TargetLowering)
+
+foreign import ccall unsafe "LLVMGetTargetMachineTriple" getTargetMachineTriple ::
+  Ptr TargetMachine -> IO (OwnerTransfered CString)
+
+foreign import ccall unsafe "LLVM_Hs_GetDefaultTargetTriple" getDefaultTargetTriple :: 
+  IO (OwnerTransfered CString)
+
+foreign import ccall unsafe "LLVM_Hs_GetProcessTargetTriple" getProcessTargetTriple :: 
+  IO (OwnerTransfered CString)
+
+foreign import ccall unsafe "LLVM_Hs_GetHostCPUName" getHostCPUName :: 
+  Ptr CSize -> IO CString
+
+foreign import ccall unsafe "LLVM_Hs_GetHostCPUFeatures" getHostCPUFeatures ::
+  IO (OwnerTransfered CString)
+
+foreign import ccall unsafe "LLVM_Hs_GetTargetMachineDataLayout" getTargetMachineDataLayout ::
+  Ptr TargetMachine -> IO (OwnerTransfered CString)
+
+data TargetLibraryInfo
+
+foreign import ccall unsafe "LLVM_Hs_CreateTargetLibraryInfo" createTargetLibraryInfo ::
+  CString -> IO (Ptr TargetLibraryInfo)
+
+foreign import ccall unsafe "LLVM_Hs_GetLibFunc" getLibFunc ::
+  Ptr TargetLibraryInfo -> CString -> Ptr LibFunc -> IO LLVMBool
+
+foreign import ccall unsafe "LLVM_Hs_LibFuncGetName" libFuncGetName ::
+  Ptr TargetLibraryInfo -> LibFunc -> Ptr CSize -> IO CString
+
+foreign import ccall unsafe "LLVM_Hs_LibFuncSetAvailableWithName" libFuncSetAvailableWithName ::
+  Ptr TargetLibraryInfo -> LibFunc -> CString -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_DisposeTargetLibraryInfo" disposeTargetLibraryInfo ::
+  Ptr TargetLibraryInfo -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_InitializeAllTargets" initializeAllTargets :: IO ()
+
+foreign import ccall unsafe "LLVMCreateTargetDataLayout" createTargetDataLayout ::
+  Ptr TargetMachine -> IO (Ptr DataLayout)
diff --git a/src/LLVM/Internal/FFI/TargetC.cpp b/src/LLVM/Internal/FFI/TargetC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/TargetC.cpp
@@ -0,0 +1,319 @@
+#define __STDC_LIMIT_MACROS
+#include "llvm-c/Target.h"
+#include "LLVM/Internal/FFI/LibFunc.h"
+#include "LLVM/Internal/FFI/Target.h"
+#include "LLVM/Internal/FFI/Target.hpp"
+#include "llvm-c/Core.h"
+#include "llvm-c/TargetMachine.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Analysis/TargetLibraryInfo.h"
+#include "llvm/ExecutionEngine/Interpreter.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Support/FormattedStream.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Target/TargetMachine.h"
+
+using namespace llvm;
+
+namespace llvm {
+// Taken from llvm/lib/Target/TargetMachineC.cpp
+// These functions need to be marked as static to avoid undefined behavior
+// due to multiple definitions
+static LLVMTargetRef wrap(const Target *P) {
+  return reinterpret_cast<LLVMTargetRef>(const_cast<Target *>(P));
+}
+
+static inline TargetLibraryInfoImpl *unwrap(LLVMTargetLibraryInfoRef P) {
+  return reinterpret_cast<TargetLibraryInfoImpl*>(P);
+}
+
+static inline LLVMTargetLibraryInfoRef wrap(const TargetLibraryInfoImpl *P) {
+  TargetLibraryInfoImpl *X = const_cast<TargetLibraryInfoImpl*>(P);
+  return reinterpret_cast<LLVMTargetLibraryInfoRef>(X);
+}
+
+static FloatABI::ABIType unwrap(LLVM_Hs_FloatABI x) {
+  switch (x) {
+#define ENUM_CASE(x)                                                           \
+  case LLVM_Hs_FloatABI_##x:                                              \
+    return FloatABI::x;
+    LLVM_HS_FOR_EACH_FLOAT_ABI(ENUM_CASE)
+#undef ENUM_CASE
+  default:
+    return FloatABI::ABIType(0);
+  }
+}
+
+static LibFunc::Func unwrap(LLVMLibFunc x) {
+  switch (x) {
+#define ENUM_CASE(x)                                                           \
+  case LLVMLibFunc__##x:                                                       \
+    return LibFunc::x;
+    LLVM_HS_FOR_EACH_LIB_FUNC(ENUM_CASE)
+#undef ENUM_CASE
+  default:
+    return LibFunc::Func(0);
+  }
+}
+
+static LLVMLibFunc wrap(LibFunc::Func x) {
+  switch (x) {
+#define ENUM_CASE(x)                                                           \
+  case LibFunc::x:                                                             \
+    return LLVMLibFunc__##x;
+    LLVM_HS_FOR_EACH_LIB_FUNC(ENUM_CASE)
+#undef ENUM_CASE
+  default:
+    return LLVMLibFunc(0);
+  }
+}
+
+static LLVM_Hs_FloatABI wrap(FloatABI::ABIType x) {
+  switch (x) {
+#define ENUM_CASE(x)                                                           \
+  case FloatABI::x:                                                            \
+    return LLVM_Hs_FloatABI_##x;
+    LLVM_HS_FOR_EACH_FLOAT_ABI(ENUM_CASE)
+#undef ENUM_CASE
+  default:
+    return LLVM_Hs_FloatABI(0);
+  }
+}
+
+static FPOpFusion::FPOpFusionMode unwrap(LLVM_Hs_FPOpFusionMode x) {
+  switch (x) {
+#define ENUM_CASE(x)                                                           \
+  case LLVM_Hs_FPOpFusionMode_##x:                                        \
+    return FPOpFusion::x;
+    LLVM_HS_FOR_EACH_FP_OP_FUSION_MODE(ENUM_CASE)
+#undef ENUM_CASE
+  default:
+    return FPOpFusion::FPOpFusionMode(0);
+  }
+}
+
+static LLVM_Hs_FPOpFusionMode wrap(FPOpFusion::FPOpFusionMode x) {
+  switch (x) {
+#define ENUM_CASE(x)                                                           \
+  case FPOpFusion::x:                                                          \
+    return LLVM_Hs_FPOpFusionMode_##x;
+    LLVM_HS_FOR_EACH_FP_OP_FUSION_MODE(ENUM_CASE)
+#undef ENUM_CASE
+  default:
+    return LLVM_Hs_FPOpFusionMode(0);
+  }
+}
+}
+
+extern "C" {
+
+LLVMBool LLVM_Hs_InitializeNativeTarget() {
+  return LLVMInitializeNativeTarget() || InitializeNativeTargetAsmPrinter() ||
+         InitializeNativeTargetAsmParser();
+}
+
+LLVMTargetRef LLVM_Hs_LookupTarget(const char *arch, const char *ctriple,
+                                        const char **tripleOut,
+                                        const char **cerror) {
+  std::string error;
+  Triple triple(ctriple);
+  if (const Target *result =
+          TargetRegistry::lookupTarget(arch, triple, error)) {
+    *tripleOut = strdup(triple.getTriple().c_str());
+    return wrap(result);
+  }
+  *cerror = strdup(error.c_str());
+  return 0;
+}
+
+TargetOptions *LLVM_Hs_CreateTargetOptions() {
+  TargetOptions *to = new TargetOptions();
+  return to;
+}
+
+void LLVM_Hs_SetTargetOptionFlag(TargetOptions *to,
+                                      LLVM_Hs_TargetOptionFlag f,
+                                      unsigned v) {
+  switch (f) {
+#define ENUM_CASE(op)                                                          \
+  case LLVM_Hs_TargetOptionFlag_##op:                                     \
+    to->op = v ? 1 : 0;                                                        \
+    break;
+    LLVM_HS_FOR_EACH_TARGET_OPTION_FLAG(ENUM_CASE)
+#undef ENUM_CASE
+  }
+}
+
+unsigned LLVM_Hs_GetTargetOptionFlag(TargetOptions *to,
+                                          LLVM_Hs_TargetOptionFlag f) {
+  switch (f) {
+#define ENUM_CASE(op)                                                          \
+  case LLVM_Hs_TargetOptionFlag_##op:                                     \
+    return to->op;
+    LLVM_HS_FOR_EACH_TARGET_OPTION_FLAG(ENUM_CASE)
+#undef ENUM_CASE
+  default:
+    assert(false && "Unknown target option flag");
+    return 0;
+  }
+}
+
+void LLVM_Hs_SetStackAlignmentOverride(TargetOptions *to, unsigned v) {
+  to->StackAlignmentOverride = v;
+}
+
+unsigned LLVM_Hs_GetStackAlignmentOverride(TargetOptions *to) {
+  return to->StackAlignmentOverride;
+}
+
+void LLVM_Hs_SetFloatABIType(TargetOptions *to, LLVM_Hs_FloatABI v) {
+  to->FloatABIType = unwrap(v);
+}
+
+LLVM_Hs_FloatABI LLVM_Hs_GetFloatABIType(TargetOptions *to) {
+  return wrap(to->FloatABIType);
+}
+
+void LLVM_Hs_SetAllowFPOpFusion(TargetOptions *to,
+                                     LLVM_Hs_FPOpFusionMode v) {
+  to->AllowFPOpFusion = unwrap(v);
+}
+
+LLVM_Hs_FPOpFusionMode LLVM_Hs_GetAllowFPOpFusion(TargetOptions *to) {
+  return wrap(to->AllowFPOpFusion);
+}
+
+void LLVM_Hs_DisposeTargetOptions(TargetOptions *t) { delete t; }
+
+// const TargetLowering *LLVM_Hs_GetTargetLowering(LLVMTargetMachineRef t)
+// {
+// 	return unwrap(t)->getTargetLowering();
+// }
+
+char *LLVM_Hs_GetDefaultTargetTriple() {
+  return strdup(sys::getDefaultTargetTriple().c_str());
+}
+
+char *LLVM_Hs_GetProcessTargetTriple() {
+  return strdup(sys::getProcessTriple().c_str());
+}
+
+const char *LLVM_Hs_GetHostCPUName(size_t &len) {
+  StringRef r = sys::getHostCPUName();
+  len = r.size();
+  return r.data();
+}
+
+char *LLVM_Hs_GetHostCPUFeatures() {
+  StringMap<bool> featureMap;
+  std::string features;
+  if (sys::getHostCPUFeatures(featureMap)) {
+    bool first = true;
+    for (llvm::StringMap<bool>::const_iterator it = featureMap.begin();
+         it != featureMap.end(); ++it) {
+      if (!first) {
+        features += ",";
+      }
+      first = false;
+      features += (it->second ? "+" : "-") + it->first().str();
+    }
+  }
+  return strdup(features.c_str());
+}
+
+char *LLVM_Hs_GetTargetMachineDataLayout(LLVMTargetMachineRef t) {
+  return strdup(
+      unwrap(t)->createDataLayout().getStringRepresentation().c_str());
+}
+
+LLVMTargetLibraryInfoRef
+LLVM_Hs_CreateTargetLibraryInfo(const char *triple) {
+    const TargetLibraryInfoImpl* p = new TargetLibraryInfoImpl(Triple(triple));
+    return wrap(p);
+}
+
+LLVMBool LLVM_Hs_GetLibFunc(
+	LLVMTargetLibraryInfoRef l,
+	const char *funcName,
+	LLVMLibFunc *f
+) {
+	LibFunc::Func func;
+	LLVMBool result = unwrap(l)->getLibFunc(funcName, func);
+	*f = wrap(func);
+	return result;
+}
+
+const char *LLVM_Hs_LibFuncGetName(
+	LLVMTargetLibraryInfoRef l,
+	LLVMLibFunc f,
+	size_t *nameSize
+) {
+	TargetLibraryInfo impl(*unwrap(l));
+    StringRef s = impl.getName(unwrap(f));
+	*nameSize = s.size();
+	return s.data();
+}
+
+void LLVM_Hs_LibFuncSetAvailableWithName(
+	LLVMTargetLibraryInfoRef l,
+	LLVMLibFunc f,
+	const char *name
+) {
+	unwrap(l)->setAvailableWithName(unwrap(f), name);
+}
+
+void LLVM_Hs_DisposeTargetLibraryInfo(LLVMTargetLibraryInfoRef l) {
+	delete unwrap(l);
+}
+
+void LLVM_Hs_InitializeAllTargets() {
+  InitializeAllTargetInfos();
+  InitializeAllTargets();
+  InitializeAllTargetMCs();
+  InitializeAllAsmPrinters();
+  // None of the other components are bound yet
+}
+
+// This is identical to LLVMTargetMachineEmit but LLVM doesn’t expose this function so we copy it here.
+LLVMBool LLVM_Hs_TargetMachineEmit(
+    LLVMTargetMachineRef T,
+    LLVMModuleRef M,
+    raw_pwrite_stream *OS,
+    LLVMCodeGenFileType codegen,
+    char **ErrorMessage
+) {
+  TargetMachine* TM = unwrap(T);
+  Module* Mod = unwrap(M);
+
+  legacy::PassManager pass;
+
+  std::string error;
+
+  Mod->setDataLayout(TM->createDataLayout());
+
+  TargetMachine::CodeGenFileType ft;
+  switch (codegen) {
+    case LLVMAssemblyFile:
+      ft = TargetMachine::CGFT_AssemblyFile;
+      break;
+    default:
+      ft = TargetMachine::CGFT_ObjectFile;
+      break;
+  }
+  if (TM->addPassesToEmitFile(pass, *OS, ft)) {
+    error = "TargetMachine can't emit a file of this type";
+    *ErrorMessage = strdup(error.c_str());
+    return true;
+  }
+
+  pass.run(*Mod);
+
+  OS->flush();
+  return false;
+}
+
+}
diff --git a/src/LLVM/Internal/FFI/Threading.hs b/src/LLVM/Internal/FFI/Threading.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/Threading.hs
@@ -0,0 +1,13 @@
+{-# LANGUAGE
+  ForeignFunctionInterface
+  #-}
+module LLVM.Internal.FFI.Threading where
+
+import LLVM.Prelude
+
+import Foreign.C
+
+import LLVM.Internal.FFI.LLVMCTypes
+
+foreign import ccall unsafe "LLVMIsMultithreaded" isMultithreaded ::
+  IO LLVMBool
diff --git a/src/LLVM/Internal/FFI/Transforms.hs b/src/LLVM/Internal/FFI/Transforms.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/Transforms.hs
@@ -0,0 +1,76 @@
+-- | Code used with Template Haskell to build the FFI for transform passes.
+module LLVM.Internal.FFI.Transforms where
+
+import LLVM.Prelude
+
+-- | does the constructor for this pass require a TargetMachine object
+needsTargetMachine :: String -> Bool
+needsTargetMachine "CodeGenPrepare" = True
+needsTargetMachine _ = False
+
+-- | Translate a Haskell name (used in the public Haskell interface, typically not abbreviated)
+-- | for a pass into the (sometimes obscure, sometimes abbreviated) name used in the LLVM C interface.
+-- | This translation includes, by choice of prefix, whether the C interface implementation is found in
+-- | the LLVM distribution ("LLVM" prefix) or either not available or broken there and so implemented
+-- | as part of this Haskell package ("LLVM_Hs_" prefix).
+cName :: String -> String
+cName n =
+    let core = case n of
+            "AddressSanitizer" -> "AddressSanitizerFunction"
+            "AggressiveDeadCodeElimination" -> "AggressiveDCE"
+            "AlwaysInline" -> "AlwaysInliner"
+            "DeadInstructionElimination" -> "DeadInstElimination"
+            "EarlyCommonSubexpressionElimination" -> "EarlyCSE"
+            "FunctionAttributes" -> "FunctionAttrs"
+            "GlobalDeadCodeElimination" -> "GlobalDCE"
+            "InductionVariableSimplify" -> "IndVarSimplify"
+            "InternalizeFunctions" -> "Internalize"
+            "InterproceduralConstantPropagation" -> "IPConstantPropagation"
+            "InterproceduralSparseConditionalConstantPropagation" -> "IPSCCP"
+            "LoopClosedSingleStaticAssignment" -> "LCSSA"
+            "LoopInvariantCodeMotion" -> "LICM"
+            "LoopInstructionSimplify" -> "LoopInstSimplify"
+            "MemcpyOptimization" -> "MemCpyOpt"
+            "PruneExceptionHandling" -> "PruneEH"
+            "ScalarReplacementOfAggregates" -> "SROA"
+            "OldScalarReplacementOfAggregates" -> "ScalarReplAggregates"
+            "SimplifyControlFlowGraph" -> "CFGSimplification"
+            "SparseConditionalConstantPropagation" -> "SCCP"
+            "SuperwordLevelParallelismVectorize" -> "SLPVectorize"
+            h -> h
+        patchImpls = [
+         "AddressSanitizer",
+         "AddressSanitizerModule",
+         "BoundsChecking",
+         "CodeGenPrepare",
+         "GlobalValueNumbering",
+         "InternalizeFunctions",
+         "BasicBlockVectorize",
+         "BlockPlacement",
+         "BreakCriticalEdges",
+         "DeadCodeElimination",
+         "DeadInstructionElimination",
+         "DemoteRegisterToMemory",
+         "EdgeProfiler",
+         "GCOVProfiler",
+         "LoopClosedSingleStaticAssignment",
+         "LoopInstructionSimplify",
+         "LoopStrengthReduce",
+         "LoopVectorize",
+         "LowerAtomic",
+         "LowerInvoke",
+         "LowerSwitch",
+         "MemorySanitizer",
+         "MergeFunctions",
+         "OptimalEdgeProfiler",
+         "PathProfiler",
+         "PartialInlining",
+         "ScalarReplacementOfAggregates",
+         "Sinking",
+         "StripDeadDebugInfo",
+         "StripDebugDeclare",
+         "StripNonDebugSymbols",
+         "ThreadSanitizer"
+         ]
+    in
+      (if (n `elem` patchImpls) then "LLVM_Hs_" else "LLVM") ++ "Add" ++ core ++ "Pass"
diff --git a/src/LLVM/Internal/FFI/Type.h b/src/LLVM/Internal/FFI/Type.h
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/Type.h
@@ -0,0 +1,23 @@
+#ifndef __LLVM_INTERNAL_FFI__TYPE__H__
+#define __LLVM_INTERNAL_FFI__TYPE__H__
+
+#define LLVM_HS_FOR_EACH_TYPE_KIND(macro) \
+	macro(Void) \
+	macro(Half) \
+	macro(Float) \
+	macro(Double) \
+	macro(X86_FP80) \
+	macro(FP128) \
+	macro(PPC_FP128) \
+	macro(Label) \
+	macro(Integer) \
+	macro(Function) \
+	macro(Struct) \
+	macro(Array) \
+	macro(Pointer) \
+	macro(Vector) \
+	macro(Metadata) \
+	macro(X86_MMX) \
+	macro(Token)
+
+#endif
diff --git a/src/LLVM/Internal/FFI/Type.hs b/src/LLVM/Internal/FFI/Type.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/Type.hs
@@ -0,0 +1,152 @@
+{-# LANGUAGE
+  ForeignFunctionInterface
+  #-}
+-- | Functions for handling the LLVM types
+module LLVM.Internal.FFI.Type where
+
+import LLVM.Prelude
+
+import Foreign.Ptr
+import Foreign.C
+
+import LLVM.Internal.FFI.LLVMCTypes
+import LLVM.Internal.FFI.Context
+import LLVM.Internal.FFI.PtrHierarchy
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreType.html#ga112756467f0988613faa6043d674d843>
+foreign import ccall unsafe "LLVMGetTypeKind" getTypeKind ::
+  Ptr Type -> IO TypeKind
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreTypeInt.html#gadfb8ba2f605f0860a4bf2e3c480ab6a2>
+foreign import ccall unsafe "LLVMGetIntTypeWidth" getIntTypeWidth ::
+  Ptr Type -> IO (CUInt)
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreTypeFunction.html#ga2970f0f4d9ee8a0f811f762fb2fa7f82>
+foreign import ccall unsafe "LLVMIsFunctionVarArg" isFunctionVarArg ::
+  Ptr Type -> IO LLVMBool
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreTypeFunction.html#gacfa4594cbff421733add602a413cae9f>
+foreign import ccall unsafe "LLVMGetReturnType" getReturnType ::
+  Ptr Type -> IO (Ptr Type)
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreTypeFunction.html#ga44fa41d22ed1f589b8202272f54aad77>
+foreign import ccall unsafe "LLVMCountParamTypes" countParamTypes ::
+  Ptr Type -> IO CUInt
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreTypeFunction.html#ga83dd3a49a0f3f017f4233fc0d667bda2>
+foreign import ccall unsafe "LLVMGetParamTypes" getParamTypes ::
+  Ptr Type -> Ptr (Ptr Type) -> IO ()
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreTypeSequential.html#ga0b03e26a2d254530a9b5c279cdf52257>
+foreign import ccall unsafe "LLVMGetElementType" getElementType ::
+  Ptr Type -> IO (Ptr Type)
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreTypeInt.html#ga2e5db8cbc30daa156083f2c42989138d>
+foreign import ccall unsafe "LLVMIntTypeInContext" intTypeInContext ::
+  Ptr Context -> CUInt -> IO (Ptr Type)
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreTypeFunction.html#ga8b0c32e7322e5c6c1bf7eb95b0961707>
+foreign import ccall unsafe "LLVMFunctionType" functionType' ::
+  Ptr Type -> Ptr (Ptr Type) -> CUInt -> LLVMBool -> IO (Ptr Type)
+
+functionType :: Ptr Type -> (CUInt, Ptr (Ptr Type)) -> LLVMBool -> IO (Ptr Type)
+functionType rt (n, ats) va = functionType' rt ats n va
+
+newtype AddrSpace = AddrSpace CUInt
+-- | <http://llvm.org/doxygen/group__LLVMCCoreTypeSequential.html#ga299fe6147083678d0494b1b875f542fae>
+foreign import ccall unsafe "LLVMPointerType" pointerType ::
+  Ptr Type -> AddrSpace -> IO (Ptr Type)
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreTypeSequential.html#ga124b162b69b5def41dde2fda3668cbd9>
+foreign import ccall unsafe "LLVMGetPointerAddressSpace" getPointerAddressSpace ::
+  Ptr Type -> IO AddrSpace
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreTypeSequential.html#ga5ec731adf74fb40bc3b401956d0c6ff2>
+foreign import ccall unsafe "LLVMVectorType" vectorType ::
+  Ptr Type -> CUInt -> IO (Ptr Type)
+
+-- | what <http://llvm.org/doxygen/group__LLVMCCoreTypeSequential.html#gabd1666e080f693e1af0b4018005cd927>
+-- | would be if it supported 64-bit array sizes, as the C++ type does.
+foreign import ccall unsafe "LLVM_Hs_ArrayType" arrayType ::
+  Ptr Type -> Word64 -> IO (Ptr Type)
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreTypeStruct.html#gaff2af74740a22f7d18701f0d8c3e5a6f>
+foreign import ccall unsafe "LLVMStructTypeInContext" structTypeInContext' ::
+  Ptr Context -> Ptr (Ptr Type) -> CUInt -> LLVMBool -> IO (Ptr Type)
+
+structTypeInContext :: Ptr Context -> (CUInt, Ptr (Ptr Type)) -> LLVMBool -> IO (Ptr Type)
+structTypeInContext ctx (n, ts) p = structTypeInContext' ctx ts n p
+
+foreign import ccall unsafe "LLVM_Hs_StructCreateNamed" structCreateNamed ::
+  Ptr Context -> CString -> IO (Ptr Type)
+
+foreign import ccall unsafe "LLVMGetStructName" getStructName ::
+  Ptr Type -> IO CString
+
+foreign import ccall unsafe "LLVM_Hs_StructIsLiteral" structIsLiteral ::
+  Ptr Type -> IO LLVMBool
+
+foreign import ccall unsafe "LLVM_Hs_StructIsOpaque" structIsOpaque ::
+  Ptr Type -> IO LLVMBool
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreTypeStruct.html#ga3e940e660375ae0cbdde81c0d8ec91e3>
+foreign import ccall unsafe "LLVMIsPackedStruct" isPackedStruct ::
+  Ptr Type -> IO LLVMBool
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreTypeStruct.html#gaf32e6d6bcec38b786efbef689b0dddf7>
+foreign import ccall unsafe "LLVMCountStructElementTypes" countStructElementTypes ::
+  Ptr Type -> IO CUInt
+
+foreign import ccall unsafe "LLVMGetStructElementTypes" getStructElementTypes ::
+  Ptr Type -> Ptr (Ptr Type) -> IO ()
+
+foreign import ccall unsafe "LLVMStructSetBody" structSetBody' ::
+  Ptr Type -> Ptr (Ptr Type) -> CUInt -> LLVMBool -> IO ()
+
+structSetBody :: Ptr Type -> (CUInt, Ptr (Ptr Type)) -> LLVMBool -> IO ()
+structSetBody s (n,ts) p = structSetBody' s ts n p
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreTypeSequential.html#gafb88a5ebd2a8062e105854910dc7ca17>
+foreign import ccall unsafe "LLVMGetVectorSize" getVectorSize ::
+  Ptr Type -> IO CUInt
+
+-- | what <http://llvm.org/doxygen/group__LLVMCCoreTypeSequential.html#ga02dc08041a12265cb700ee469497df63>
+-- | would be if it supported 64 bit lengths
+foreign import ccall unsafe "LLVM_Hs_GetArrayLength" getArrayLength ::
+  Ptr Type -> IO Word64
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreTypeOther.html#ga1c78ca6d7bf279330b9195fa52f23828>
+foreign import ccall unsafe "LLVMVoidTypeInContext" voidTypeInContext ::
+  Ptr Context -> IO (Ptr Type)
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreTypeFloat.html#ga3a5332a1d075602bccad7576d1a8e36f>
+foreign import ccall unsafe "LLVMHalfTypeInContext" halfTypeInContext ::
+  Ptr Context -> IO (Ptr Type)
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreTypeFloat.html#ga529c83a8a5461e5beac19eb867216e3c>
+foreign import ccall unsafe "LLVMFloatTypeInContext" floatTypeInContext ::
+  Ptr Context -> IO (Ptr Type)
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreTypeFloat.html#ga200527010747eab31b73d3e3f6d94935>
+foreign import ccall unsafe "LLVMDoubleTypeInContext" doubleTypeInContext ::
+  Ptr Context -> IO (Ptr Type)
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreTypeFloat.html#ga24f77b84b625ed3dd516b52480606093>
+foreign import ccall unsafe "LLVMX86FP80TypeInContext" x86FP80TypeInContext ::
+  Ptr Context -> IO (Ptr Type)
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreTypeFloat.html#ga1c02fb08f9ae12a719ed42099d42ccd8>
+foreign import ccall unsafe "LLVMFP128TypeInContext" fP128TypeInContext ::
+  Ptr Context -> IO (Ptr Type)
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreTypeFloat.html#gac2491184fc3d8631c7b264c067f2f761>
+foreign import ccall unsafe "LLVMPPCFP128TypeInContext" ppcFP128TypeInContext ::
+  Ptr Context -> IO (Ptr Type)
+
+-- | <http://llvm.org/doxygen/classllvm_1_1Type.html#a28fdf240b8220065bc60d6d1b1a2f174>
+foreign import ccall unsafe "LLVM_Hs_MetadataTypeInContext" metadataTypeInContext ::
+  Ptr Context -> IO (Ptr Type)
+
+-- | <http://llvm.org/docs/doxygen/html/Core_8cpp.html#a5d3702e198e2373db7e31bb18879efc3>
+foreign import ccall unsafe "LLVM_Hs_TokenTypeInContext" tokenTypeInContext ::
+  Ptr Context -> IO (Ptr Type)
diff --git a/src/LLVM/Internal/FFI/TypeC.cpp b/src/LLVM/Internal/FFI/TypeC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/TypeC.cpp
@@ -0,0 +1,42 @@
+#define __STDC_LIMIT_MACROS
+#include "llvm-c/Core.h"
+#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Type.h"
+
+using namespace llvm;
+
+extern "C" {
+
+LLVMTypeRef LLVM_Hs_StructCreateNamed(LLVMContextRef C, const char *Name) {
+    if (Name) {
+        return wrap(StructType::create(*unwrap(C), Name));
+    } else {
+        return wrap(StructType::create(*unwrap(C)));
+    }
+}
+
+LLVMBool LLVM_Hs_StructIsLiteral(LLVMTypeRef t) {
+    return unwrap<StructType>(t)->isLiteral();
+}
+
+LLVMBool LLVM_Hs_StructIsOpaque(LLVMTypeRef t) {
+    return unwrap<StructType>(t)->isOpaque();
+}
+
+LLVMTypeRef LLVM_Hs_ArrayType(LLVMTypeRef ElementType, uint64_t ElementCount) {
+    return wrap(ArrayType::get(unwrap(ElementType), ElementCount));
+}
+
+uint64_t LLVM_Hs_GetArrayLength(LLVMTypeRef ArrayTy) {
+    return unwrap<ArrayType>(ArrayTy)->getNumElements();
+}
+
+LLVMTypeRef LLVM_Hs_MetadataTypeInContext(LLVMContextRef C) {
+    return wrap(Type::getMetadataTy(*unwrap(C)));
+}
+
+LLVMTypeRef LLVM_Hs_TokenTypeInContext(LLVMContextRef C) {
+    return wrap(Type::getTokenTy(*unwrap(C)));
+}
+}
diff --git a/src/LLVM/Internal/FFI/User.hs b/src/LLVM/Internal/FFI/User.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/User.hs
@@ -0,0 +1,38 @@
+{-# LANGUAGE
+  ForeignFunctionInterface,
+  MultiParamTypeClasses,
+  UndecidableInstances
+  #-}
+-- | FFI functions for handling the LLVM User class
+module LLVM.Internal.FFI.User where
+
+import LLVM.Prelude
+
+import Foreign.Ptr
+import Foreign.C
+
+import LLVM.Internal.FFI.PtrHierarchy
+
+-- | a blind type to correspond to llvm::Use
+data Use
+
+-- | test if a 'Value' is a 'User'
+foreign import ccall unsafe "LLVMIsAUser" isAUser ::
+    Ptr Value -> IO (Ptr User)
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreValueUses.html#ga66a226d3d06ffada5c929656f4d97d35>
+foreign import ccall unsafe "LLVMGetFirstUse" getFirstUse ::
+    Ptr User -> IO (Ptr Use)
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreValueUses.html#ga6ea72661bcca2b77bea57173317ec942>
+foreign import ccall unsafe "LLVMGetNextUse" getNextUse ::
+    Ptr Use -> IO (Ptr Use)
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreValueUser.html#ga2ad633a6afc7906f1afe329f244240f6>
+foreign import ccall unsafe "LLVMGetNumOperands" getNumOperands ::
+    Ptr User -> IO CUInt
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreValueUser.html#ga799d58a361054323cb457945071cbfdb>
+foreign import ccall unsafe "LLVMGetOperand" getOperand ::
+    Ptr User -> CUInt -> IO (Ptr Value)
+
diff --git a/src/LLVM/Internal/FFI/Value.h b/src/LLVM/Internal/FFI/Value.h
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/Value.h
@@ -0,0 +1,32 @@
+#ifndef __LLVM_INTERNAL_FFI__VALUE__H__
+#define __LLVM_INTERNAL_FFI__VALUE__H__
+
+#define LLVM_HS_FOR_EACH_VALUE_SUBCLASS(macro) \
+	macro(Argument) \
+	macro(BasicBlock) \
+	macro(Function) \
+	macro(GlobalAlias) \
+	macro(GlobalVariable) \
+	macro(UndefValue) \
+	macro(BlockAddress) \
+	macro(ConstantExpr) \
+	macro(ConstantAggregateZero) \
+	macro(ConstantDataArray) \
+	macro(ConstantDataVector) \
+	macro(ConstantInt) \
+	macro(ConstantFP) \
+	macro(ConstantArray) \
+	macro(ConstantStruct) \
+	macro(ConstantTokenNone) \
+	macro(ConstantVector) \
+	macro(ConstantPointerNull) \
+	macro(InlineAsm) \
+	macro(Instruction)
+
+typedef enum {
+#define ENUM_CASE(class) LLVM ## class ## SubclassId,
+LLVM_HS_FOR_EACH_VALUE_SUBCLASS(ENUM_CASE)
+#undef ENUM_CASE
+} LLVMValueSubclassId;
+
+#endif
diff --git a/src/LLVM/Internal/FFI/Value.hs b/src/LLVM/Internal/FFI/Value.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/Value.hs
@@ -0,0 +1,41 @@
+{-# LANGUAGE
+  ForeignFunctionInterface,
+  MultiParamTypeClasses,
+  UndecidableInstances
+  #-}
+-- | FFI functions for handling the LLVM Value class
+module LLVM.Internal.FFI.Value where
+
+import LLVM.Prelude
+
+import Foreign.Ptr
+import Foreign.C
+
+import LLVM.Internal.FFI.LLVMCTypes
+import LLVM.Internal.FFI.PtrHierarchy
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreValueGeneral.html#ga12179f46b79de8436852a4189d4451e0>
+foreign import ccall unsafe "LLVMTypeOf" typeOf ::
+  Ptr Value -> IO (Ptr Type)
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreValueGeneral.html#ga70948786725c43968d15225dd584e5a9>
+foreign import ccall unsafe "LLVMGetValueName" getValueName ::
+  Ptr Value -> IO CString
+
+-- | <http://llvm.org/doxygen/group__LLVMCCoreValueGeneral.html#gac1f61f74d83d218d4943c018e8fd8d13>
+foreign import ccall unsafe "LLVMSetValueName" setValueName ::
+  Ptr Value -> CString -> IO ()
+
+-- | This function exposes the ID returned by llvm::Value::getValueID()
+-- | <http://llvm.org/doxygen/classllvm_1_1Value.html#a2983b7b4998ef5b9f51b18c01588af3c>. 
+foreign import ccall unsafe "LLVM_Hs_GetValueSubclassId" getValueSubclassId ::
+  Ptr Value -> IO ValueSubclassId
+
+foreign import ccall unsafe "LLVMReplaceAllUsesWith" replaceAllUsesWith ::
+  Ptr Value -> Ptr Value -> IO ()
+
+foreign import ccall unsafe "LLVM_Hs_CreateArgument" createArgument ::
+  Ptr Type -> CString -> IO (Ptr Value)
+
+foreign import ccall unsafe "LLVMDumpValue" dumpValue ::
+  Ptr Value -> IO ()
diff --git a/src/LLVM/Internal/FFI/ValueC.cpp b/src/LLVM/Internal/FFI/ValueC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FFI/ValueC.cpp
@@ -0,0 +1,30 @@
+#define __STDC_LIMIT_MACROS
+#include "llvm-c/Core.h"
+#include "llvm/IR/Type.h"
+#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/Value.h"
+#include "llvm/IR/Argument.h"
+#include "LLVM/Internal/FFI/Value.h"
+
+using namespace llvm;
+
+extern "C" {
+
+LLVMValueSubclassId LLVM_Hs_GetValueSubclassId(LLVMValueRef v) {
+	switch(unwrap(v)->getValueID()) {
+#define VALUE_SUBCLASS_ID_CASE(class) case Value::class ## Val: return LLVM ## class ## SubclassId;
+LLVM_HS_FOR_EACH_VALUE_SUBCLASS(VALUE_SUBCLASS_ID_CASE)
+#undef VALUE_SUBCLASS_ID_CASE
+	default: break;
+	}
+	return LLVMValueSubclassId(0);
+}
+
+LLVMValueRef LLVM_Hs_CreateArgument(
+	LLVMTypeRef t,
+	const char *name
+) {
+	return wrap(new Argument(unwrap(t), name));
+}
+
+}
diff --git a/src/LLVM/Internal/FastMathFlags.hs b/src/LLVM/Internal/FastMathFlags.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FastMathFlags.hs
@@ -0,0 +1,51 @@
+{-# LANGUAGE 
+  MultiParamTypeClasses
+  #-}
+module LLVM.Internal.FastMathFlags where
+
+import LLVM.Prelude
+
+import Control.Monad.Trans
+import Control.Monad.AnyCont
+import Control.Monad.State
+import Control.Exception
+
+import Data.Bits
+
+import qualified LLVM.Internal.FFI.Builder as FFI
+import qualified LLVM.Internal.FFI.LLVMCTypes as FFI
+
+import LLVM.Internal.Coding
+import LLVM.Internal.EncodeAST
+
+import qualified LLVM.AST as A
+
+instance EncodeM IO A.FastMathFlags FFI.FastMathFlags where
+  encodeM A.NoFastMathFlags = return 0
+  encodeM A.UnsafeAlgebra = return FFI.fastMathFlagsUnsafeAlgebra
+  encodeM f = return $ foldr1 (.|.) [ 
+               if a f then b else 0
+               | (a,b) <- [
+                (A.noNaNs, FFI.fastMathFlagsNoNaNs),
+                (A.noInfs, FFI.fastMathFlagsNoInfs),
+                (A.noSignedZeros, FFI.fastMathFlagsNoSignedZeros),
+                (A.allowReciprocal, FFI.fastMathFlagsAllowReciprocal)
+               ] 
+              ]
+
+instance EncodeM EncodeAST A.FastMathFlags () where
+  encodeM f = do 
+    f <- liftIO $ encodeM f
+    builder <- gets encodeStateBuilder
+    anyContToM $ bracket (FFI.setFastMathFlags builder f) (\() -> FFI.setFastMathFlags builder 0)
+
+instance Monad m => DecodeM m A.FastMathFlags FFI.FastMathFlags where
+  decodeM 0 = return A.NoFastMathFlags
+  decodeM f | FFI.fastMathFlagsUnsafeAlgebra .&. f /= 0 = return A.UnsafeAlgebra
+  decodeM f = return A.FastMathFlags {
+                A.noNaNs = FFI.fastMathFlagsNoNaNs .&. f /= 0,
+                A.noInfs = FFI.fastMathFlagsNoInfs .&. f /= 0,
+                A.noSignedZeros = FFI.fastMathFlagsNoSignedZeros .&. f /= 0,
+                A.allowReciprocal = FFI.fastMathFlagsAllowReciprocal .&. f /= 0
+              }
+
diff --git a/src/LLVM/Internal/FloatingPointPredicate.hs b/src/LLVM/Internal/FloatingPointPredicate.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/FloatingPointPredicate.hs
@@ -0,0 +1,29 @@
+{-# LANGUAGE
+  MultiParamTypeClasses,
+  TemplateHaskell
+  #-}
+module LLVM.Internal.FloatingPointPredicate where
+
+import LLVM.Internal.Coding
+
+import qualified LLVM.Internal.FFI.LLVMCTypes as FFI
+import qualified LLVM.AST.FloatingPointPredicate as A.FPPred
+
+genCodingInstance [t| A.FPPred.FloatingPointPredicate |] ''FFI.FCmpPredicate [
+  (FFI.fCmpPredFalse, A.FPPred.False),
+  (FFI.fCmpPredOEQ, A.FPPred.OEQ),
+  (FFI.fCmpPredOGT, A.FPPred.OGT),
+  (FFI.fCmpPredOGE, A.FPPred.OGE),
+  (FFI.fCmpPredOLT, A.FPPred.OLT),
+  (FFI.fCmpPredOLE, A.FPPred.OLE),
+  (FFI.fCmpPredONE, A.FPPred.ONE),
+  (FFI.fCmpPredORD, A.FPPred.ORD),
+  (FFI.fCmpPredUNO, A.FPPred.UNO),
+  (FFI.fCmpPredUEQ, A.FPPred.UEQ),
+  (FFI.fCmpPredUGT, A.FPPred.UGT),
+  (FFI.fCmpPredUGE, A.FPPred.UGE),
+  (FFI.fCmpPredULT, A.FPPred.ULT),
+  (FFI.fCmpPredULE, A.FPPred.ULE),
+  (FFI.fCmpPredUNE, A.FPPred.UNE),
+  (FFI.fcmpPredTrue, A.FPPred.True)
+ ]
diff --git a/src/LLVM/Internal/Function.hs b/src/LLVM/Internal/Function.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/Function.hs
@@ -0,0 +1,70 @@
+module LLVM.Internal.Function where
+
+import LLVM.Prelude
+
+import Control.Monad.Trans
+import Control.Monad.AnyCont
+
+import Foreign.C (CUInt)
+import Foreign.Ptr  
+
+import Data.Map (Map)
+import qualified Data.Map as Map
+
+import qualified LLVM.Internal.FFI.Function as FFI
+import qualified LLVM.Internal.FFI.PtrHierarchy as FFI
+
+import LLVM.Internal.DecodeAST
+import LLVM.Internal.EncodeAST
+import LLVM.Internal.Value
+import LLVM.Internal.Coding
+import LLVM.Internal.Constant ()
+import LLVM.Internal.Attribute
+
+import qualified LLVM.AST as A
+import qualified LLVM.AST.Constant as A
+import qualified LLVM.AST.ParameterAttribute as A.PA  
+
+getMixedAttributeSet :: Ptr FFI.Function -> DecodeAST MixedAttributeSet
+getMixedAttributeSet = decodeM <=< liftIO . FFI.getMixedAttributeSet
+
+setFunctionAttributes :: Ptr FFI.Function -> MixedAttributeSet -> EncodeAST ()
+setFunctionAttributes f = (liftIO . FFI.setMixedAttributeSet f) <=< encodeM
+
+getParameters :: Ptr FFI.Function -> Map CUInt [A.PA.ParameterAttribute] -> DecodeAST [A.Parameter]
+getParameters f attrs = scopeAnyCont $ do
+  n <- liftIO (FFI.countParams f)
+  ps <- allocaArray n
+  liftIO $ FFI.getParams f ps
+  params <- peekArray n ps
+  forM (zip params [0..]) $ \(param, i) -> 
+    return A.Parameter 
+       `ap` typeOf param
+       `ap` getLocalName param
+       `ap` (return $ Map.findWithDefault [] i attrs)
+  
+getGC :: Ptr FFI.Function -> DecodeAST (Maybe String)
+getGC f = scopeAnyCont $ decodeM =<< liftIO (FFI.getGC f)
+
+setGC :: Ptr FFI.Function -> Maybe String -> EncodeAST ()
+setGC f gc = scopeAnyCont $ liftIO . FFI.setGC f =<< encodeM gc 
+
+getPrefixData :: Ptr FFI.Function -> DecodeAST (Maybe A.Constant)
+getPrefixData f = do
+  has <- decodeM =<< (liftIO $ FFI.hasPrefixData f)
+  if has
+   then decodeM =<< (liftIO $ FFI.getPrefixData f)
+   else return Nothing
+
+setPrefixData :: Ptr FFI.Function -> Maybe A.Constant -> EncodeAST ()
+setPrefixData f = maybe (return ()) (liftIO . FFI.setPrefixData f <=< encodeM)
+
+getPersonalityFn :: Ptr FFI.Function -> DecodeAST (Maybe A.Constant)
+getPersonalityFn f = do
+  has <- decodeM =<< liftIO (FFI.hasPersonalityFn f)
+  if has
+     then decodeM =<< liftIO (FFI.getPersonalityFn f)
+     else pure Nothing
+
+setPersonalityFn :: Ptr FFI.Function -> Maybe A.Constant -> EncodeAST ()
+setPersonalityFn f personality = (liftIO . FFI.setPersonalityFn f =<< encodeM personality)
diff --git a/src/LLVM/Internal/Global.hs b/src/LLVM/Internal/Global.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/Global.hs
@@ -0,0 +1,146 @@
+{-# LANGUAGE
+  TemplateHaskell,
+  MultiParamTypeClasses
+  #-}
+module LLVM.Internal.Global where
+
+import LLVM.Prelude
+
+import Control.Monad.State
+import Control.Monad.AnyCont
+import Foreign.Ptr
+import qualified Data.Map as Map
+
+import qualified LLVM.Internal.FFI.PtrHierarchy as FFI
+import qualified LLVM.Internal.FFI.LLVMCTypes as FFI
+import qualified LLVM.Internal.FFI.GlobalValue as FFI
+
+import LLVM.Internal.Coding
+import LLVM.Internal.DecodeAST
+import LLVM.Internal.EncodeAST
+
+import qualified LLVM.AST.Linkage as A.L
+import qualified LLVM.AST.Visibility as A.V
+import qualified LLVM.AST.COMDAT as A.COMDAT
+import qualified LLVM.AST.DLL as A.DLL
+import qualified LLVM.AST.ThreadLocalStorage as A.TLS
+import qualified LLVM.AST.Global as A.G
+
+genCodingInstance [t| A.L.Linkage |] ''FFI.Linkage [
+  (FFI.linkageExternal, A.L.External),
+  (FFI.linkageAvailableExternally, A.L.AvailableExternally),
+  (FFI.linkageLinkOnceAny, A.L.LinkOnce),
+  (FFI.linkageLinkOnceODR, A.L.LinkOnceODR),
+  (FFI.linkageWeakAny, A.L.Weak),
+  (FFI.linkageWeakODR, A.L.WeakODR),
+  (FFI.linkageAppending, A.L.Appending),
+  (FFI.linkageInternal, A.L.Internal),
+  (FFI.linkagePrivate, A.L.Private),
+  (FFI.linkageExternalWeak, A.L.ExternWeak),
+  (FFI.linkageCommon, A.L.Common)
+ ]
+
+getLinkage :: FFI.DescendentOf FFI.GlobalValue v => Ptr v -> DecodeAST A.L.Linkage
+getLinkage g = liftIO $ decodeM =<< FFI.getLinkage (FFI.upCast g)
+
+setLinkage :: FFI.DescendentOf FFI.GlobalValue v => Ptr v -> A.L.Linkage -> EncodeAST ()
+setLinkage g l = liftIO . FFI.setLinkage (FFI.upCast g) =<< encodeM l
+                                                                       
+genCodingInstance [t| A.V.Visibility |] ''FFI.Visibility [
+  (FFI.visibilityDefault, A.V.Default),
+  (FFI.visibilityHidden, A.V.Hidden),
+  (FFI.visibilityProtected, A.V.Protected)
+ ]
+
+getVisibility :: FFI.DescendentOf FFI.GlobalValue v => Ptr v -> DecodeAST A.V.Visibility
+getVisibility g = liftIO $ decodeM =<< FFI.getVisibility (FFI.upCast g)
+
+setVisibility :: FFI.DescendentOf FFI.GlobalValue v => Ptr v -> A.V.Visibility -> EncodeAST ()
+setVisibility g v = liftIO . FFI.setVisibility (FFI.upCast g) =<< encodeM v
+
+genCodingInstance [t| Maybe A.DLL.StorageClass |] ''FFI.DLLStorageClass [
+  (FFI.dllStorageClassDefault, Nothing),
+  (FFI.dllStorageClassDLLImport, Just A.DLL.Import),
+  (FFI.dllStorageClassDLLExport, Just A.DLL.Export)
+ ]
+
+getDLLStorageClass :: FFI.DescendentOf FFI.GlobalValue v => Ptr v -> DecodeAST (Maybe A.DLL.StorageClass)
+getDLLStorageClass g = liftIO $ decodeM =<< FFI.getDLLStorageClass (FFI.upCast g)
+
+setDLLStorageClass :: FFI.DescendentOf FFI.GlobalValue v => Ptr v -> Maybe A.DLL.StorageClass -> EncodeAST ()
+setDLLStorageClass g sc = liftIO . FFI.setDLLStorageClass (FFI.upCast g) =<< encodeM sc
+
+getSection :: FFI.DescendentOf FFI.GlobalValue v => Ptr v -> DecodeAST (Maybe String)
+getSection g = do
+  sectionLengthPtr <- alloca
+  sectionNamePtr <- liftIO $ FFI.getSection (FFI.upCast g) sectionLengthPtr
+  if sectionNamePtr == nullPtr then
+    return Nothing
+    else
+      do sectionLength <- peek sectionLengthPtr
+         sectionName <- decodeM (sectionNamePtr, sectionLength)
+         return (Just sectionName)
+
+setSection :: FFI.DescendentOf FFI.GlobalValue v => Ptr v -> Maybe String -> EncodeAST ()
+setSection g s = scopeAnyCont $ do
+  s <- encodeM (maybe "" id s)
+  liftIO $ FFI.setSection (FFI.upCast g) s
+
+genCodingInstance [t| A.COMDAT.SelectionKind |] ''FFI.COMDATSelectionKind [
+  (FFI.comdatSelectionKindAny, A.COMDAT.Any),
+  (FFI.comdatSelectionKindExactMatch, A.COMDAT.ExactMatch),
+  (FFI.comdatSelectionKindLargest, A.COMDAT.Largest),
+  (FFI.comdatSelectionKindNoDuplicates, A.COMDAT.NoDuplicates),
+  (FFI.comdatSelectionKindSameSize, A.COMDAT.SameSize)
+ ]
+
+instance DecodeM DecodeAST (String, A.COMDAT.SelectionKind) (Ptr FFI.COMDAT) where
+  decodeM c = return (,)
+              `ap` (decodeM $ FFI.getCOMDATName c)
+              `ap` (decodeM =<< liftIO (FFI.getCOMDATSelectionKind c))
+
+getCOMDATName :: FFI.DescendentOf FFI.GlobalValue v => Ptr v -> DecodeAST (Maybe String)
+getCOMDATName g = do
+  c <- liftIO $ FFI.getCOMDAT (FFI.upCast g)
+  if c == nullPtr
+   then return Nothing
+   else do
+     cds <- gets comdats
+     liftM Just $ case Map.lookup c cds of
+       Just (name, _) -> return name
+       Nothing -> do
+          cd@(name, _) <- decodeM c
+          modify $ \s -> s { comdats = Map.insert c cd cds }
+          return name
+
+setCOMDAT :: FFI.DescendentOf FFI.GlobalObject v => Ptr v -> Maybe String -> EncodeAST ()
+setCOMDAT _ Nothing = return ()
+setCOMDAT g (Just name) = do
+  cd <- referCOMDAT name
+  liftIO $ FFI.setCOMDAT (FFI.upCast g) cd
+
+setAlignment :: FFI.DescendentOf FFI.GlobalValue v => Ptr v -> Word32 -> EncodeAST ()
+setAlignment g i = liftIO $ FFI.setAlignment (FFI.upCast g) (fromIntegral i)
+
+getAlignment :: FFI.DescendentOf FFI.GlobalValue v => Ptr v -> DecodeAST Word32
+getAlignment g = liftIO $ fromIntegral <$> FFI.getAlignment (FFI.upCast g)
+
+genCodingInstance [t| Maybe A.TLS.Model |] ''FFI.ThreadLocalMode [
+  (FFI.threadLocalModeNotThreadLocal, Nothing),
+  (FFI.threadLocalModeGeneralDynamicTLSModel, Just A.TLS.GeneralDynamic),
+  (FFI.threadLocalModeLocalDynamicTLSModel, Just A.TLS.LocalDynamic),
+  (FFI.threadLocalModeInitialExecTLSModel, Just A.TLS.InitialExec),
+  (FFI.threadLocalModeLocalExecTLSModel, Just A.TLS.LocalExec)
+ ]
+
+getThreadLocalMode :: FFI.DescendentOf FFI.GlobalValue v => Ptr v -> DecodeAST (Maybe A.TLS.Model)
+getThreadLocalMode g = liftIO $ decodeM =<< FFI.getThreadLocalMode (FFI.upCast g)
+
+setThreadLocalMode :: FFI.DescendentOf FFI.GlobalValue v => Ptr v -> Maybe A.TLS.Model -> EncodeAST ()
+setThreadLocalMode g m = liftIO . FFI.setThreadLocalMode (FFI.upCast g) =<< encodeM m
+
+genCodingInstance [t| Maybe A.G.UnnamedAddr |] ''FFI.UnnamedAddr [
+  (FFI.unnamedAddrNone, Nothing),
+  (FFI.unnamedAddrLocal, Just A.G.LocalAddr),
+  (FFI.unnamedAddrGlobal, Just A.G.GlobalAddr)
+ ]
diff --git a/src/LLVM/Internal/Inject.hs b/src/LLVM/Internal/Inject.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/Inject.hs
@@ -0,0 +1,11 @@
+{-# LANGUAGE MultiParamTypeClasses #-}
+module LLVM.Internal.Inject where
+
+import LLVM.Prelude
+
+class Inject a b where
+  inject :: a -> b
+
+instance Inject a a where
+  inject = id
+
diff --git a/src/LLVM/Internal/InlineAssembly.hs b/src/LLVM/Internal/InlineAssembly.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/InlineAssembly.hs
@@ -0,0 +1,68 @@
+{-# LANGUAGE
+  TemplateHaskell,
+  MultiParamTypeClasses
+  #-}
+module LLVM.Internal.InlineAssembly where
+ 
+import LLVM.Prelude
+
+import Control.Monad.IO.Class
+
+import Foreign.C
+import Foreign.Ptr
+
+import qualified LLVM.Internal.FFI.InlineAssembly as FFI
+import qualified LLVM.Internal.FFI.LLVMCTypes as FFI
+import qualified LLVM.Internal.FFI.Module as FFI
+import qualified LLVM.Internal.FFI.PtrHierarchy as FFI
+
+import qualified LLVM.AST as A (Definition(..))
+import qualified LLVM.AST.InlineAssembly as A
+import qualified LLVM.AST.Type as A
+
+import LLVM.Internal.Coding 
+import LLVM.Internal.EncodeAST
+import LLVM.Internal.DecodeAST
+import LLVM.Internal.Value
+
+genCodingInstance [t| A.Dialect |] ''FFI.AsmDialect [
+   (FFI.asmDialectATT, A.ATTDialect),
+   (FFI.asmDialectIntel, A.IntelDialect)
+  ]
+
+instance EncodeM EncodeAST A.InlineAssembly (Ptr FFI.InlineAsm) where
+  encodeM (A.InlineAssembly {
+             A.type' = t,
+             A.assembly = assembly,
+             A.constraints = constraints,
+             A.hasSideEffects = hasSideEffects,
+             A.alignStack = alignStack,
+             A.dialect = dialect
+           }) = do
+    t <- encodeM t
+    assembly <- encodeM assembly
+    constraints <- encodeM constraints
+    hasSideEffects <- encodeM hasSideEffects
+    alignStack <- encodeM alignStack
+    dialect <- encodeM dialect
+    liftIO $ FFI.createInlineAsm t assembly constraints hasSideEffects alignStack dialect
+
+instance DecodeM DecodeAST A.InlineAssembly (Ptr FFI.InlineAsm) where
+  decodeM p = do
+    return A.InlineAssembly
+      `ap` (liftM (\(A.PointerType f _) -> f) (typeOf p))
+      `ap` (decodeM =<< liftIO (FFI.getInlineAsmAssemblyString p))
+      `ap` (decodeM =<< liftIO (FFI.getInlineAsmConstraintString p))
+      `ap` (decodeM =<< liftIO (FFI.inlineAsmHasSideEffects p))
+      `ap` (decodeM =<< liftIO (FFI.inlineAsmIsAlignStack p))
+      `ap` (decodeM =<< liftIO (FFI.getInlineAsmDialect p))
+
+instance DecodeM DecodeAST [A.Definition] (FFI.ModuleAsm CString) where
+  decodeM (FFI.ModuleAsm s) = do
+    s <- decodeM s
+    let takeModIA "" = []
+        takeModIA s =
+          let (a,r) = break (== '\n') s
+          in A.ModuleInlineAssembly a : takeModIA (dropWhile (== '\n') r)
+    return $ takeModIA s
+    
diff --git a/src/LLVM/Internal/Instruction.hs b/src/LLVM/Internal/Instruction.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/Instruction.hs
@@ -0,0 +1,607 @@
+{-# LANGUAGE
+  TemplateHaskell,
+  QuasiQuotes,
+  MultiParamTypeClasses,
+  UndecidableInstances,
+  ViewPatterns
+  #-}
+module LLVM.Internal.Instruction where
+
+import LLVM.Prelude
+
+import qualified Language.Haskell.TH as TH
+import qualified Language.Haskell.TH.Quote as TH
+import qualified LLVM.Internal.InstructionDefs as ID
+import LLVM.Internal.InstructionDefs (instrP)
+
+import Control.Monad.AnyCont
+import Control.Monad.Error.Class
+import Control.Monad.IO.Class
+import Control.Monad.State (gets)
+
+import Foreign.Ptr
+
+import Control.Exception (assert)
+import qualified Data.Map as Map
+import qualified Data.List as List
+import Data.List.NonEmpty (NonEmpty((:|)))
+import qualified Data.List.NonEmpty as NonEmpty
+
+import qualified LLVM.Internal.FFI.PtrHierarchy as FFI
+import qualified LLVM.Internal.FFI.BinaryOperator as FFI
+import qualified LLVM.Internal.FFI.Instruction as FFI
+import qualified LLVM.Internal.FFI.Value as FFI
+import qualified LLVM.Internal.FFI.User as FFI
+import qualified LLVM.Internal.FFI.Builder as FFI
+import qualified LLVM.Internal.FFI.Constant as FFI
+import qualified LLVM.Internal.FFI.BasicBlock as FFI
+
+import LLVM.Internal.Atomicity ()
+import LLVM.Internal.Attribute
+import LLVM.Internal.CallingConvention ()
+import LLVM.Internal.Coding
+import LLVM.Internal.DecodeAST
+import LLVM.Internal.EncodeAST
+import LLVM.Internal.FastMathFlags ()
+import LLVM.Internal.Metadata ()
+import LLVM.Internal.Operand ()
+import LLVM.Internal.RMWOperation ()
+import LLVM.Internal.TailCallKind ()
+import LLVM.Internal.Type
+import LLVM.Internal.Value
+
+import qualified LLVM.AST as A
+import qualified LLVM.AST.Constant as A.C
+
+callInstAttributeSet :: Ptr FFI.Instruction -> DecodeAST MixedAttributeSet
+callInstAttributeSet = decodeM <=< liftIO . FFI.getCallSiteAttributeSet
+
+meta :: Ptr FFI.Instruction -> DecodeAST A.InstructionMetadata
+meta i = do
+  let getMetadata n = scopeAnyCont $ do
+         ks <- allocaArray n
+         ps <- allocaArray n
+         n' <- liftIO $ FFI.getMetadata i ks ps n
+         if (n' > n) 
+          then getMetadata n'
+          else return zip `ap` decodeM (n', ks) `ap` decodeM (n', ps)
+  getMetadata 4
+
+setMD :: Ptr FFI.Instruction -> A.InstructionMetadata -> EncodeAST ()
+setMD i md = forM_ md $ \(kindName, anode) -> do
+               kindID <- encodeM kindName
+               node <- encodeM anode
+               liftIO $ FFI.setMetadata i kindID node
+
+instance DecodeM DecodeAST A.Terminator (Ptr FFI.Instruction) where
+  decodeM i = scopeAnyCont $ do
+    n <- liftIO $ FFI.getInstructionDefOpcode i
+    nOps <- liftIO $ FFI.getNumOperands (FFI.upCast i)
+    md <- meta i
+    let op n = decodeM =<< (liftIO $ FFI.getOperand (FFI.upCast i) n)
+        successor n = decodeM =<< (liftIO $ FFI.isABasicBlock =<< FFI.getOperand (FFI.upCast i) n)
+    case n of
+      [instrP|Ret|] -> do
+        returnOperand' <- if nOps == 0 then return Nothing else Just <$> op 0
+        return $ A.Ret { A.returnOperand = returnOperand', A.metadata' = md }
+      [instrP|Br|] -> do
+        n <- liftIO $ FFI.getNumOperands (FFI.upCast i)
+        case n of
+          1 -> do
+             dest <- successor 0
+             return $ A.Br { A.dest = dest, A.metadata' = md }
+          3 -> do
+             condition <- op 0
+             falseDest <- successor 1
+             trueDest <- successor 2
+             return $ A.CondBr {
+               A.condition = condition,
+               A.falseDest = falseDest, 
+               A.trueDest = trueDest,
+               A.metadata' = md
+             }
+      [instrP|Switch|] -> do
+        op0 <- op 0
+        dd <- successor 1
+        let nCases = (nOps - 2) `div` 2
+        values <- allocaArray nCases
+        dests <- allocaArray nCases
+        liftIO $ FFI.getSwitchCases i values dests
+        cases <- return zip `ap` peekArray nCases values `ap` peekArray nCases dests
+        dests <- forM cases $ \(c, d) -> return (,) `ap` decodeM c `ap` decodeM d
+        return A.Switch {
+          A.operand0' = op0,
+          A.defaultDest = dd,
+          A.dests = dests,
+          A.metadata' = md
+        }
+      [instrP|IndirectBr|] -> do
+        op0 <- op 0
+        let nDests = nOps - 1
+        dests <- allocaArray nDests
+        liftIO $ FFI.getIndirectBrDests i dests
+        dests <- decodeM (nDests, dests)
+        return A.IndirectBr {
+           A.operand0' = op0,
+           A.possibleDests = dests,
+           A.metadata' = md
+        }
+      [instrP|Invoke|] -> do
+        cc <- decodeM =<< liftIO (FFI.getCallSiteCallingConvention i)
+        attrs <- callInstAttributeSet i
+        fv <- liftIO $ FFI.getCallSiteCalledValue i
+        f <- decodeM fv
+        args <- forM [1..nOps-3] $ \j -> do
+                  let pAttrs = Map.findWithDefault [] (j-1) (parameterAttributes attrs)
+                  return (, pAttrs) `ap` op (j-1) 
+        rd <- successor (nOps - 2)
+        ed <- successor (nOps - 1)
+        return A.Invoke {
+          A.callingConvention' = cc,
+          A.returnAttributes' = returnAttributes attrs,
+          A.function' = f,
+          A.arguments' = args,
+          A.functionAttributes' = functionAttributes attrs,
+          A.returnDest = rd,
+          A.exceptionDest = ed,
+          A.metadata' = md
+        }
+      [instrP|Resume|] -> do
+        op0 <- op 0
+        return A.Resume {
+          A.operand0' = op0,
+          A.metadata' = md
+        }
+      [instrP|Unreachable|] -> do
+        return A.Unreachable {
+          A.metadata' = md
+        }
+      [instrP|CleanupRet|] -> do
+        dest <- decodeM =<< liftIO (FFI.upCast <$> (FFI.getCleanupPad i) :: IO (Ptr FFI.Value))
+        unwindDest <- decodeM =<< liftIO (FFI.getUnwindDest i)
+        return A.CleanupRet {
+          A.cleanupPad = dest,
+          A.unwindDest = unwindDest,
+          A.metadata' = md
+        }
+      [instrP|CatchRet|] -> do
+        catchPad <- decodeM =<< liftIO (FFI.catchRetGetCatchPad i)
+        successor <- decodeM =<< liftIO (FFI.catchRetGetSuccessor i)
+        return A.CatchRet {
+          A.catchPad = catchPad,
+          A.successor = successor,
+          A.metadata' = md
+        }
+      [instrP|CatchSwitch|] -> do
+        parentPad' <- decodeM =<< liftIO (FFI.catchSwitchGetParentPad i)
+        numHandlers <- liftIO (FFI.catchSwitchGetNumHandlers i)
+        handlers <- assert (numHandlers > 0) $
+          forM (0 :| [1..numHandlers - 1]) $ decodeM <=< liftIO . FFI.catchSwitchGetHandler i
+        unwindDest <- decodeM =<< liftIO (FFI.catchSwitchGetUnwindDest i)
+        return A.CatchSwitch {
+          A.parentPad' = parentPad',
+          A.catchHandlers = handlers,
+          A.defaultUnwindDest = unwindDest,
+          A.metadata' = md
+        }
+
+instance EncodeM EncodeAST A.Terminator (Ptr FFI.Instruction) where
+  encodeM t = scopeAnyCont $ do
+    builder <- gets encodeStateBuilder
+    s <- encodeM ""
+    t' <- case t of
+      A.Ret { A.returnOperand = r } -> do
+        rv <- maybe (return nullPtr) encodeM r
+        FFI.upCast <$> do liftIO $ FFI.buildRet builder rv
+      A.Br { A.dest = d } -> do
+        db <- encodeM d
+        FFI.upCast <$> do liftIO $ FFI.buildBr builder db
+      A.CondBr { A.condition = c, A.trueDest = t, A.falseDest = f } -> do
+        cv <- encodeM c
+        tb <- encodeM t
+        fb <- encodeM f
+        FFI.upCast <$> do liftIO $ FFI.buildCondBr builder cv tb fb
+      A.Switch {
+        A.operand0' = op0,
+        A.defaultDest = dd,
+        A.dests = ds
+      } -> do
+        op0' <- encodeM op0
+        dd' <- encodeM dd
+        i <- liftIO $ FFI.buildSwitch builder op0' dd' (fromIntegral $ length ds)
+        forM ds $ \(v,d) -> do
+          v' <- encodeM v
+          d' <- encodeM d
+          liftIO $ FFI.addCase i v' d'
+        return $ FFI.upCast i
+      A.IndirectBr { 
+        A.operand0' = op0,
+        A.possibleDests = dests
+      } -> do
+        op0' <- encodeM op0
+        i <- liftIO $ FFI.buildIndirectBr builder op0' (fromIntegral $ length dests)
+        forM dests $ \dest -> do
+          d <- encodeM dest
+          liftIO $ FFI.addDestination i d
+        return $ FFI.upCast i
+      A.Invoke {
+        A.callingConvention' = cc,
+        A.returnAttributes' = rAttrs,
+        A.function' = fun,
+        A.arguments' = args,
+        A.functionAttributes' = fAttrs,
+        A.returnDest = rd,
+        A.exceptionDest = ed
+      } -> do
+        fv <- encodeM fun
+        rb <- encodeM rd
+        eb <- encodeM ed
+        let (argvs, argAttrs) = unzip args
+        (n, argvs) <- encodeM argvs
+        i <- liftIO $ FFI.buildInvoke builder fv argvs n rb eb s
+        attrs <- encodeM $ MixedAttributeSet fAttrs rAttrs (Map.fromList (zip [0..] argAttrs))
+        liftIO $ FFI.setCallSiteAttributeSet i attrs
+        cc <- encodeM cc
+        liftIO $ FFI.setCallSiteCallingConvention i cc
+        return $ FFI.upCast i
+      A.Resume { 
+        A.operand0' = op0
+      } -> do
+        op0' <- encodeM op0
+        i <- liftIO $ FFI.buildResume builder op0'
+        return $ FFI.upCast i
+      A.Unreachable {
+      } -> do
+        i <- liftIO $ FFI.buildUnreachable builder
+        return $ FFI.upCast i
+      A.CleanupRet {
+        A.cleanupPad = cleanupPad,
+        A.unwindDest = unwindDest
+      } -> do
+        cleanupPad' <- encodeM cleanupPad
+        unwindDest' <- encodeM unwindDest
+        liftIO $ FFI.buildCleanupRet builder cleanupPad' unwindDest'
+      A.CatchRet {
+        A.catchPad = catchPad,
+        A.successor = successor
+      } -> do
+        catchPad' <- encodeM catchPad
+        successor' <- encodeM successor
+        liftIO $ FFI.buildCatchRet builder catchPad' successor'
+      A.CatchSwitch {
+        A.parentPad' = parentPad,
+        A.catchHandlers = catchHandlers,
+        A.defaultUnwindDest = unwindDest
+      } -> do
+        parentPad' <- encodeM parentPad
+        unwindDest' <- encodeM unwindDest
+        let numHandlers = fromIntegral (NonEmpty.length catchHandlers)
+        i <- liftIO $ FFI.buildCatchSwitch builder parentPad' unwindDest' numHandlers
+        mapM_ (liftIO . FFI.catchSwitchAddHandler i <=< encodeM) catchHandlers
+        return i
+    setMD t' (A.metadata' t)
+    return t'      
+
+$(do
+  let findInstrFields s = Map.findWithDefault (error $ "instruction missing from AST: " ++ show s) s
+                          ID.astInstructionRecs
+
+  [d|
+    instance DecodeM DecodeAST A.Instruction (Ptr FFI.Instruction) where
+      decodeM i = scopeAnyCont $ do
+        t <- typeOf i
+        nOps <- liftIO $ FFI.getNumOperands (FFI.upCast i)
+        let op n = decodeM =<< (liftIO $ FFI.getOperand (FFI.upCast i) n)
+            cop n = decodeM =<< (liftIO $ FFI.isAConstant =<< FFI.getOperand (FFI.upCast i) n)
+            get_nsw b = liftIO $ decodeM =<< FFI.hasNoSignedWrap (FFI.upCast b)
+            get_nuw b = liftIO $ decodeM =<< FFI.hasNoUnsignedWrap (FFI.upCast b)
+            get_exact b = liftIO $ decodeM =<< FFI.isExact (FFI.upCast b)
+            get_fastMathFlags b = liftIO $ decodeM =<< FFI.getFastMathFlags (FFI.upCast b)
+
+        n <- liftIO $ FFI.getInstructionDefOpcode i
+        $(
+          let fieldDecoders :: String -> String -> ([String], TH.ExpQ)
+              fieldDecoders lrn s = case s of
+                "b" -> ([], [| liftIO $ FFI.isABinaryOperator (FFI.upCast i) |])
+                "nsw" -> (["b"], [| get_nsw $(TH.dyn "b") |])
+                "nuw" -> (["b"], [| get_nuw $(TH.dyn "b") |])
+                "exact" -> (["b"], [| get_exact $(TH.dyn "b") |])
+                "fastMathFlags" -> (["b"], [| get_fastMathFlags $(TH.dyn "b") |])
+                "operand0" -> ([], [| op 0 |])
+                "operand1" -> ([], [| op 1 |])
+                "address" -> ([], case lrn of "Store" -> [| op 1 |]; _ -> [| op 0 |])
+                "value" -> ([], case lrn of "Store" -> [| op 0 |]; _ -> [| op 1 |])
+                "expected" -> ([], [| op 1 |])
+                "replacement" -> ([], [| op 2 |])
+                "condition'" -> ([], [| op 0 |])
+                "trueValue" -> ([], [| op 1 |])
+                "falseValue" -> ([], [| op 2 |])
+                "argList" -> ([], [| op 0 |])
+                "vector" -> ([], [| op 0 |])
+                "element" -> ([], [| op 1 |])
+                "index" -> ([], case lrn of "ExtractElement" -> [| op 1 |]; "InsertElement" -> [| op 2 |])
+                "mask" -> ([], [| cop 2 |])
+                "aggregate" -> ([], [| op 0 |])
+                "metadata" -> ([], [| meta i |])
+                "iPredicate" -> ([], [| decodeM =<< liftIO (FFI.getICmpPredicate i) |])
+                "fpPredicate" -> ([], [| decodeM =<< liftIO (FFI.getFCmpPredicate i) |])
+                "tailCallKind" -> ([], [| decodeM =<< liftIO (FFI.getTailCallKind i) |])
+                "callingConvention" -> ([], [| decodeM =<< liftIO (FFI.getCallSiteCallingConvention i) |])
+                "attrs" -> ([], [| callInstAttributeSet i |])
+                "returnAttributes" -> (["attrs"], [| return $ returnAttributes $(TH.dyn "attrs") |])
+                "f" -> ([], [| liftIO $ FFI.getCallSiteCalledValue i |])
+                "function" -> (["f"], [| decodeM $(TH.dyn "f") |])
+                "arguments" -> ([], [| forM [1..nOps-1] $ \j -> do
+                                         let pAttrs = Map.findWithDefault [] (j-1) (parameterAttributes $(TH.dyn "attrs"))
+                                         p <- op (j-1)
+                                         return (p, pAttrs) |])
+                "clauses" -> 
+                  ([], [|do
+                          nClauses <- liftIO $ FFI.getNumClauses i
+                          -- We need to convert nClauses to a signed
+                          -- value before subtracting
+                          forM [0..fromIntegral nClauses - (1 :: Int)] $ \j -> do
+                          v <- liftIO $ FFI.getClause i (fromIntegral j)
+                          c <- decodeM v
+                          t <- typeOf v
+                          return $ case t of { A.ArrayType _ _ -> A.Filter; _ -> A.Catch} $ c |])
+                "functionAttributes" -> (["attrs"], [| return $ functionAttributes $(TH.dyn "attrs") |])
+                "type'" -> ([], [| return t |])
+                "incomingValues" ->
+                    ([], [| do
+                            n <- liftIO $ FFI.countIncoming i
+                            forM [0..n-1] $ \m -> do
+                              iv <- decodeM =<< (liftIO $ FFI.getIncomingValue i m)
+                              ib <- decodeM =<< (liftIO $ FFI.getIncomingBlock i m)
+                              return (iv,ib) |])
+                "allocatedType" -> ([], [| decodeM =<< liftIO (FFI.getAllocatedType i) |])
+                "numElements" -> 
+                    ([], [| do
+                            n <- decodeM =<< (liftIO $ FFI.getAllocaNumElements i)
+                            return $ case n of
+                              A.ConstantOperand (A.C.Int { A.C.integerValue = 1 }) -> Nothing
+                              _ -> Just n
+                              |])                
+                "alignment" -> ([], [| decodeM =<< liftIO (FFI.getInstrAlignment i) |])
+                "maybeAtomicity" -> ([], [| decodeM =<< liftIO (FFI.getAtomicity i) |])
+                "atomicity" -> ([], [| decodeM =<< liftIO (FFI.getAtomicity i) |])
+                "volatile" -> ([], [| decodeM =<< liftIO (FFI.getVolatile i) |])
+                "inBounds" -> ([], [| decodeM =<< liftIO (FFI.getInBounds (FFI.upCast i)) |])
+                "failureMemoryOrdering" -> ([], [| decodeM =<< liftIO (FFI.getFailureAtomicOrdering i) |])
+                "indices" -> ([], [| mapM op [1..nOps-1] |])
+                "indices'" ->
+                  ([], [| do
+                          n <- liftIO $ FFI.countInstStructureIndices i
+                          a <- allocaArray n
+                          liftIO $ FFI.getInstStructureIndices i a
+                          decodeM (n, a) |])
+                "rmwOperation" -> ([], [| decodeM =<< liftIO (FFI.getAtomicRMWBinOp i) |])
+                "cleanup" -> ([], [| decodeM =<< liftIO (FFI.isCleanup i) |])
+                "parentPad" -> ([], [| decodeM =<< liftIO (FFI.getParentPad i) |])
+                "catchSwitch" -> ([], [| decodeM =<< liftIO (FFI.getParentPad i) |])
+                "args" -> ([], [| do numArgs <- liftIO (FFI.getNumArgOperands i)
+                                     if (numArgs == 0)
+                                       then return []
+                                       else forM [0..numArgs-1] $ \op ->
+                                              decodeM =<< liftIO (FFI.getArgOperand i op) |])
+                _ -> ([], [| error $ "unrecognized instruction field or depenency thereof: " ++ show s |])
+          in
+          TH.caseE [| n |] [
+            TH.match opcodeP (TH.normalB (TH.doE handlerBody)) []
+            | (lrn, iDef) <- Map.toList ID.instructionDefs,
+              ID.instructionKind iDef /= ID.Terminator,
+              let opcodeP = TH.dataToPatQ (const Nothing) (ID.cppOpcode iDef)
+                  handlerBody = 
+                    let TH.RecC fullName fields = findInstrFields lrn
+                        (fieldNames,_,_) = unzip3 fields
+                        allNames ns = List.nub $ [ d | n <- ns, d <- allNames . fst . fieldDecoders lrn $ n ] ++ ns
+                    in
+                      [ 
+                       TH.bindS (TH.varP (TH.mkName n)) (snd . fieldDecoders lrn $ n)
+                       | n <- allNames . map TH.nameBase $ fieldNames 
+                      ] ++ [ 
+                       TH.noBindS [| 
+                        return $(TH.recConE 
+                                 fullName
+                                 [ (f,) <$> (TH.varE . TH.mkName . TH.nameBase $ f) | f <- fieldNames ])
+                        |]
+                      ]
+                ]
+         )
+
+    instance EncodeM EncodeAST A.Instruction (Ptr FFI.Instruction, EncodeAST ()) where
+      encodeM o = scopeAnyCont $ do
+        builder <- gets encodeStateBuilder
+        let return' i = return (FFI.upCast i, return ())
+        s <- encodeM ""
+        (inst, act) <- case o of
+          A.ICmp { 
+            A.iPredicate = pred,
+            A.operand0 = op0,
+            A.operand1 = op1
+          } -> do
+            op0' <- encodeM op0
+            op1' <- encodeM op1
+            pred <- encodeM pred
+            i <- liftIO $ FFI.buildICmp builder pred op0' op1' s
+            return' i
+          A.FCmp {
+            A.fpPredicate = pred,
+            A.operand0 = op0,
+            A.operand1 = op1
+          } -> do
+            op0' <- encodeM op0
+            op1' <- encodeM op1
+            pred <- encodeM pred
+            i <- liftIO $ FFI.buildFCmp builder pred op0' op1' s
+            return' i
+          A.Phi { A.type' = t, A.incomingValues = ivs } -> do
+             t' <- encodeM t
+             i <- liftIO $ FFI.buildPhi builder t' s
+             return (
+               FFI.upCast i,
+               do
+                 let (ivs3, bs3) = unzip ivs
+                 ivs3' <- encodeM ivs3
+                 bs3' <- encodeM bs3
+                 liftIO $ FFI.addIncoming i ivs3' bs3'
+               )
+          A.Call {
+            A.tailCallKind = tck,
+            A.callingConvention = cc,
+            A.returnAttributes = rAttrs,
+            A.function = f,
+            A.arguments = args,
+            A.functionAttributes = fAttrs
+          } -> do
+            fv <- encodeM f
+            let (argvs, argAttrs) = unzip args
+            (n, argvs) <- encodeM argvs
+            i <- liftIO $ FFI.buildCall builder fv argvs n s
+            attrs <- encodeM $ MixedAttributeSet fAttrs rAttrs (Map.fromList (zip [0..] argAttrs))
+            liftIO $ FFI.setCallSiteAttributeSet i attrs     
+            tck <- encodeM tck
+            liftIO $ FFI.setTailCallKind i tck
+            cc <- encodeM cc
+            liftIO $ FFI.setCallSiteCallingConvention i cc
+            return' i
+          A.Select { A.condition' = c, A.trueValue = t, A.falseValue = f } -> do
+            c' <- encodeM c
+            t' <- encodeM t
+            f' <- encodeM f
+            i <- liftIO $ FFI.buildSelect builder c' t' f' s
+            return' i
+          A.VAArg { A.argList = al, A.type' = t } -> do
+            al' <- encodeM al
+            t' <- encodeM t
+            i <- liftIO $ FFI.buildVAArg builder al' t' s
+            return' i
+          A.ExtractElement { A.vector = v, A.index = idx } -> do
+            v' <- encodeM v
+            idx' <- encodeM idx
+            i <- liftIO $ FFI.buildExtractElement builder v' idx' s
+            return' i
+          A.InsertElement { A.vector = v, A.element = e, A.index = idx } -> do
+            v' <- encodeM v
+            e' <- encodeM e
+            idx' <- encodeM idx
+            i <- liftIO $ FFI.buildInsertElement builder v' e' idx' s
+            return' i
+          A.ShuffleVector { A.operand0 = o0, A.operand1 = o1, A.mask = mask } -> do
+            o0' <- encodeM o0
+            o1' <- encodeM o1
+            mask' <- encodeM mask
+            i <- liftIO $ FFI.buildShuffleVector builder o0' o1' mask' s
+            return' i
+          A.ExtractValue { A.aggregate = a, A.indices' = is } -> do
+            a' <- encodeM a
+            (n, is') <- encodeM is
+            i <- liftIO $ FFI.buildExtractValue builder a' is' n s
+            return' i
+          A.InsertValue { A.aggregate = a, A.element = e, A.indices' = is } -> do
+            a' <- encodeM a
+            e' <- encodeM e
+            (n, is') <- encodeM is
+            i <- liftIO $ FFI.buildInsertValue builder a' e' is' n s
+            return' i
+          A.LandingPad { 
+            A.type' = t,
+            A.cleanup = cl, 
+            A.clauses = cs
+          } -> do
+            t' <- encodeM t
+            i <- liftIO $ FFI.buildLandingPad builder t' (fromIntegral $ length cs) s
+            forM cs $ \c -> 
+              case c of
+                A.Catch a -> do
+                  cn <- encodeM a
+                  isArray <- liftIO $ isArrayType =<< FFI.typeOf (FFI.upCast cn)
+                  when isArray $ throwError $ "Catch clause cannot take an array: " ++ show c
+                  liftIO $ FFI.addClause i cn
+                A.Filter a -> do
+                  cn <- encodeM a
+                  isArray <- liftIO $ isArrayType =<< FFI.typeOf (FFI.upCast cn)
+                  unless isArray $ throwError $ "filter clause must take an array: " ++ show c
+                  liftIO $ FFI.addClause i cn
+            when cl $ do
+              cl <- encodeM cl
+              liftIO $ FFI.setCleanup i cl
+            return' i
+          A.Alloca { A.allocatedType = alt, A.numElements = n, A.alignment = alignment } -> do 
+             alt' <- encodeM alt
+             n' <- encodeM n
+             i <- liftIO $ FFI.buildAlloca builder alt' n' s
+             unless (alignment == 0) $ liftIO $ FFI.setInstrAlignment i (fromIntegral alignment)
+             return' i
+          A.CleanupPad { A.parentPad = parentPad, A.args = args } -> do
+            parentPad' <- encodeM parentPad
+            (numArgs, args') <- encodeM args
+            i <- liftIO $ FFI.buildCleanupPad builder parentPad' args' numArgs s
+            return' i
+          A.CatchPad { A.catchSwitch = catchSwitch, A.args = args } -> do
+            catchSwitch' <- encodeM catchSwitch
+            (numArgs, args') <- encodeM args
+            i <- liftIO $ FFI.buildCatchPad builder catchSwitch' args' numArgs s
+            return' i
+          o -> $(TH.caseE [| o |] [
+                   TH.match 
+                   (TH.recP fullName [ (f,) <$> (TH.varP . TH.mkName . TH.nameBase $ f) | f <- fieldNames ])
+                   (TH.normalB (TH.doE handlerBody))
+                   []
+                   |
+                   (name, ID.instructionKind -> k) <- Map.toList ID.instructionDefs,
+                   case (k, name) of
+                     (ID.Binary, _) -> True
+                     (ID.Cast, _) -> True
+                     (ID.Memory, "Alloca") -> False
+                     (ID.Memory, _) -> True
+                     _ -> False,
+                   let
+                     TH.RecC fullName (unzip3 -> (fieldNames, _, _)) = findInstrFields name
+                     encodeMFields = map TH.nameBase fieldNames List.\\ [ "metadata" ]
+                     handlerBody = ([
+                       TH.bindS (if s == "fastMathFlags" then TH.tupP [] else TH.varP (TH.mkName s))
+                           [| encodeM $(TH.dyn s) |] | s <- encodeMFields 
+                      ] ++ [
+                       TH.bindS (TH.varP (TH.mkName "i")) [| liftIO $ $(
+                          foldl1 TH.appE . map TH.dyn $ 
+                           [ "FFI.build" ++ name, "builder" ] ++ (encodeMFields List.\\ [ "fastMathFlags" ]) ++ [ "s" ] 
+                        ) |],
+                       TH.noBindS [| return' $(TH.dyn "i") |]
+                      ])
+                  ]
+                )
+
+        setMD inst (A.metadata o)
+        return (inst, act)
+   |]
+ )
+
+
+instance DecodeM DecodeAST a (Ptr FFI.Instruction) => DecodeM DecodeAST (DecodeAST (A.Named a)) (Ptr FFI.Instruction) where
+  decodeM i = do
+    t <- typeOf i
+    w <- if t == A.VoidType then (return A.Do) else (return (A.:=) `ap` getLocalName i)
+    return $ return w `ap` decodeM i
+
+instance EncodeM EncodeAST a (Ptr FFI.Instruction) => EncodeM EncodeAST (A.Named a) (Ptr FFI.Instruction) where
+  encodeM (A.Do o) = encodeM o
+  encodeM (n A.:= o) = do
+    i <- encodeM o
+    let v = FFI.upCast i
+    n' <- encodeM n
+    liftIO $ FFI.setValueName v n'
+    defineLocal n v
+    return i
+
+instance EncodeM EncodeAST a (Ptr FFI.Instruction, EncodeAST ()) => EncodeM EncodeAST (A.Named a) (EncodeAST ()) where
+  encodeM (A.Do o) = liftM snd $ (encodeM o :: EncodeAST (Ptr FFI.Instruction, EncodeAST ()))
+  encodeM (n A.:= o) = do
+    (i, later) <- encodeM o
+    let v = FFI.upCast (i :: Ptr FFI.Instruction)
+    n' <- encodeM n
+    liftIO $ FFI.setValueName v n'
+    defineLocal n v
+    return later
+
+
diff --git a/src/LLVM/Internal/InstructionDefs.hs b/src/LLVM/Internal/InstructionDefs.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/InstructionDefs.hs
@@ -0,0 +1,71 @@
+{-# LANGUAGE
+  TemplateHaskell,
+  CPP
+  #-}
+module LLVM.Internal.InstructionDefs (
+  astInstructionRecs,
+  astConstantRecs,
+  instructionDefs,
+  ID.InstructionKind(..),
+  ID.InstructionDef(..),
+  instrP,
+  innerJoin,
+  outerJoin
+  ) where
+
+import LLVM.Prelude
+
+import qualified Language.Haskell.TH as TH
+import qualified Language.Haskell.TH.Quote as TH
+
+import Data.Map (Map)
+import qualified Data.Map as Map
+
+import qualified LLVM.Internal.FFI.InstructionDefs as ID
+
+import qualified LLVM.AST.Instruction as A
+import qualified LLVM.AST.Constant as A.C
+
+$(do
+   let ctorRecs t = do
+#if __GLASGOW_HASKELL__ < 800
+         TH.TyConI (TH.DataD _ _ _ cons _) <- TH.reify t
+#else
+         TH.TyConI (TH.DataD _ _ _ _ cons _) <- TH.reify t
+#endif
+         TH.dataToExpQ (const Nothing) $ [ (TH.nameBase n, rec) | rec@(TH.RecC n _) <- cons ]
+
+   [d|
+      astInstructionRecs :: Map String TH.Con
+      astInstructionRecs = Map.fromList $(ctorRecs ''A.Instruction)
+      astConstantRecs :: Map String TH.Con
+      astConstantRecs = Map.fromList $(ctorRecs ''A.C.Constant)
+    |]
+ )
+
+instructionDefs :: Map String ID.InstructionDef
+instructionDefs = Map.fromList [ ((refName . ID.cAPIName $ i), i) | i <- ID.instructionDefs ]
+  where
+    refName "AtomicCmpXchg" = "CmpXchg"
+    refName "PHI" = "Phi"
+    refName x = x
+
+innerJoin :: Ord k => Map k a -> Map k b -> Map k (a,b)
+innerJoin = Map.intersectionWith (,)
+
+outerJoin :: Ord k => Map k a -> Map k b -> Map k (Maybe a, Maybe b)
+outerJoin xs ys = Map.unionWith combine
+                  (Map.map (\a -> (Just a, Nothing)) xs)
+                  (Map.map (\b -> (Nothing, Just b)) ys)
+    where
+      combine (Just a, Nothing) (Nothing, Just b) = (Just a, Just b)
+      combine _ _ = error "outerJoin: the impossible happened"
+
+instrP :: TH.QuasiQuoter
+instrP = TH.QuasiQuoter {
+  TH.quoteExp = undefined,
+  TH.quotePat = let m = Map.fromList [ (ID.cAPIName i, ID.cppOpcode i) | i <- ID.instructionDefs ]
+             in TH.dataToPatQ (const Nothing) . (m Map.!),
+  TH.quoteType = undefined,
+  TH.quoteDec = undefined
+ }
diff --git a/src/LLVM/Internal/IntegerPredicate.hs b/src/LLVM/Internal/IntegerPredicate.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/IntegerPredicate.hs
@@ -0,0 +1,23 @@
+{-# LANGUAGE
+  TemplateHaskell,
+  MultiParamTypeClasses
+  #-}
+module LLVM.Internal.IntegerPredicate where
+
+import LLVM.Internal.Coding
+
+import qualified LLVM.Internal.FFI.LLVMCTypes as FFI
+import qualified LLVM.AST.IntegerPredicate as A.IPred
+
+genCodingInstance [t| A.IPred.IntegerPredicate |] ''FFI.ICmpPredicate [
+  (FFI.iCmpPredEQ, A.IPred.EQ),
+  (FFI.iCmpPredNE, A.IPred.NE),
+  (FFI.iCmpPredUGT, A.IPred.UGT),
+  (FFI.iCmpPredUGE, A.IPred.UGE),
+  (FFI.iCmpPredULT, A.IPred.ULT),
+  (FFI.iCmpPredULE, A.IPred.ULE),
+  (FFI.iCmpPredSGT, A.IPred.SGT),
+  (FFI.iCmpPredSGE, A.IPred.SGE),
+  (FFI.iCmpPredSLT, A.IPred.SLT),
+  (FFI.iCmpPredSLE, A.IPred.SLE)
+ ]
diff --git a/src/LLVM/Internal/LibraryFunction.hsc b/src/LLVM/Internal/LibraryFunction.hsc
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/LibraryFunction.hsc
@@ -0,0 +1,37 @@
+{-# LANGUAGE
+  MultiParamTypeClasses
+ #-}
+module LLVM.Internal.LibraryFunction where
+
+import LLVM.Prelude
+
+import qualified LLVM.Internal.FFI.LLVMCTypes as FFI
+
+import LLVM.Internal.Coding
+
+#include "LLVM/Internal/FFI/LibFunc.h"
+
+#{
+define hsc_inject(m) { \
+  struct { const char *s; unsigned n; } *p, list[] = { LLVM_HS_FOR_EACH_LIB_FUNC(m) }; \
+  hsc_printf("data LibraryFunction\n"); \
+  for(p = list; p < list + sizeof(list)/sizeof(list[0]); ++p) { \
+    hsc_printf("  %s LF__%s\n", (p == list ? "=" : "|"), p->s); \
+  } \
+  hsc_printf("  deriving (Eq, Ord, Enum, Bounded, Read, Show)"); \
+  hsc_printf("\n"); \
+  hsc_printf("instance Monad m => EncodeM m LibraryFunction FFI.LibFunc where\n"); \
+  for(p = list; p < list + sizeof(list)/sizeof(list[0]); ++p) { \
+    hsc_printf("  encodeM LF__%s = return (FFI.LibFunc %u)\n", p->s, p->n); \
+  } \
+  hsc_printf("\n"); \
+  hsc_printf("instance Monad m => DecodeM m LibraryFunction FFI.LibFunc where\n"); \
+  for(p = list; p < list + sizeof(list)/sizeof(list[0]); ++p) { \
+    hsc_printf("  decodeM (FFI.LibFunc %u) = return LF__%s \n", p->n, p->s); \
+  } \
+}
+}
+
+-- | <http://llvm.org/doxygen/namespacellvm_1_1LibFunc.html#abf8f6830387f338fed0bce2e65108c6f>
+#define Mac(n) { #n, LLVMLibFunc__ ## n },
+#{inject Mac}
diff --git a/src/LLVM/Internal/MemoryBuffer.hs b/src/LLVM/Internal/MemoryBuffer.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/MemoryBuffer.hs
@@ -0,0 +1,57 @@
+{-# LANGUAGE
+  MultiParamTypeClasses,
+  UndecidableInstances
+  #-}
+module LLVM.Internal.MemoryBuffer where
+
+import LLVM.Prelude
+
+import Control.Exception
+import Control.Monad.AnyCont
+import Control.Monad.Error.Class
+import Control.Monad.IO.Class
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Unsafe as BS
+import Foreign.Ptr
+
+import LLVM.Internal.Coding
+import LLVM.Internal.String
+import LLVM.Internal.Inject
+import qualified LLVM.Internal.FFI.LLVMCTypes as FFI
+import qualified LLVM.Internal.FFI.MemoryBuffer as FFI
+
+data Specification 
+  = Bytes { name :: String,  content :: BS.ByteString }
+  | File { pathName :: String }
+
+instance (Inject String e, MonadError e m, Monad m, MonadIO m, MonadAnyCont IO m) => EncodeM m Specification (FFI.OwnerTransfered (Ptr FFI.MemoryBuffer)) where
+  encodeM spec = liftM FFI.OwnerTransfered $ do
+    case spec of
+      Bytes name content -> do
+        (s,l) <- anyContToM $ BS.unsafeUseAsCStringLen (BS.snoc content 0)
+        name <- encodeM name
+        nullTerminate <- encodeM True
+        liftIO $ FFI.createMemoryBufferWithMemoryRange s (fromIntegral (l-1)) name nullTerminate
+      File pathName -> do
+        pathName <- encodeM pathName
+        mbPtr <- alloca
+        msgPtr <- alloca
+        result <- decodeM =<< (liftIO $ FFI.createMemoryBufferWithContentsOfFile pathName mbPtr msgPtr)
+        when result $ do
+          msg <- decodeM msgPtr
+          throwError (inject (msg :: String))
+        peek mbPtr          
+
+instance (Inject String e, MonadError e m, Monad m, MonadIO m, MonadAnyCont IO m) => EncodeM m Specification (Ptr FFI.MemoryBuffer) where
+  encodeM spec = do
+    FFI.OwnerTransfered mb <- encodeM spec
+    anyContToM $ bracket (return mb) FFI.disposeMemoryBuffer
+
+instance MonadIO d => DecodeM d BS.ByteString (Ptr FFI.MemoryBuffer) where
+  decodeM p = do
+    s <- liftIO $ FFI.getBufferStart p
+    l <- liftIO $ FFI.getBufferSize p
+    liftIO $ BS.packCStringLen (s, fromIntegral l)
+
+instance MonadIO d => DecodeM d String (Ptr FFI.MemoryBuffer) where
+  decodeM = decodeM . UTF8ByteString <=< decodeM
diff --git a/src/LLVM/Internal/Metadata.hs b/src/LLVM/Internal/Metadata.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/Metadata.hs
@@ -0,0 +1,56 @@
+{-# LANGUAGE
+  MultiParamTypeClasses
+  #-}
+module LLVM.Internal.Metadata where
+
+import LLVM.Prelude
+
+import Control.Monad.State hiding (mapM, forM)
+import Control.Monad.AnyCont
+
+import Foreign.Ptr
+
+import qualified Foreign.Marshal.Array as FMA
+import qualified Data.Array as Array
+
+import qualified LLVM.Internal.FFI.LLVMCTypes as FFI
+import qualified LLVM.Internal.FFI.Metadata as FFI
+import qualified LLVM.Internal.FFI.PtrHierarchy as FFI
+
+import LLVM.Internal.Context
+import LLVM.Internal.Coding
+import LLVM.Internal.EncodeAST
+import LLVM.Internal.DecodeAST
+import LLVM.Internal.Value ()
+
+instance EncodeM EncodeAST String FFI.MDKindID where
+  encodeM s = do
+    Context c <- gets encodeStateContext
+    s <- encodeM s
+    liftIO $ FFI.getMDKindIDInContext c s
+
+getMetadataKindNames :: Context -> DecodeAST ()
+getMetadataKindNames (Context c) = scopeAnyCont $ do
+  let g n = do
+        ps <- allocaArray n
+        ls <- allocaArray n 
+        n' <- liftIO $ FFI.getMDKindNames c ps ls n
+        if n' > n
+         then g n'
+         else do
+           csls <- return zip
+                   `ap` liftIO (FMA.peekArray (fromIntegral n') ps)
+                   `ap` liftIO (FMA.peekArray (fromIntegral n') ls)
+           mapM decodeM csls
+  strs <- g 16
+  modify $ \s -> s { metadataKinds = Array.listArray (0, fromIntegral (length strs) - 1) strs }
+
+instance DecodeM DecodeAST String FFI.MDKindID where
+  decodeM (FFI.MDKindID k) = gets $ (Array.! (fromIntegral k)) . metadataKinds
+
+instance DecodeM DecodeAST String (Ptr FFI.MDString) where
+  decodeM p = do
+    np <- alloca
+    s <- liftIO $ FFI.getMDString p np
+    n <- peek np
+    decodeM (s, n)
diff --git a/src/LLVM/Internal/Module.hs b/src/LLVM/Internal/Module.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/Module.hs
@@ -0,0 +1,526 @@
+{-#
+  LANGUAGE
+  TemplateHaskell,
+  ScopedTypeVariables,
+  MultiParamTypeClasses
+  #-}
+-- | This Haskell module is for/of functions for handling LLVM modules.
+module LLVM.Internal.Module where
+
+import LLVM.Prelude
+
+import Control.Exception
+import Control.Monad.AnyCont
+import Control.Monad.Error.Class
+import Control.Monad.Trans.Except
+import Control.Monad.State (gets)
+import Control.Monad.Trans
+
+import Foreign.Ptr
+import Foreign.C
+import Data.IORef
+import qualified Data.ByteString as BS
+import qualified Data.Map as Map
+
+import qualified LLVM.Internal.FFI.Assembly as FFI
+import qualified LLVM.Internal.FFI.Builder as FFI
+import qualified LLVM.Internal.FFI.Bitcode as FFI
+import qualified LLVM.Internal.FFI.Function as FFI
+import qualified LLVM.Internal.FFI.GlobalAlias as FFI
+import qualified LLVM.Internal.FFI.GlobalValue as FFI
+import qualified LLVM.Internal.FFI.GlobalVariable as FFI
+import qualified LLVM.Internal.FFI.Iterate as FFI
+import qualified LLVM.Internal.FFI.LLVMCTypes as FFI
+import qualified LLVM.Internal.FFI.MemoryBuffer as FFI
+import qualified LLVM.Internal.FFI.Metadata as FFI
+import qualified LLVM.Internal.FFI.Module as FFI
+import qualified LLVM.Internal.FFI.PtrHierarchy as FFI
+import qualified LLVM.Internal.FFI.RawOStream as FFI
+import qualified LLVM.Internal.FFI.Target as FFI
+import qualified LLVM.Internal.FFI.Value as FFI
+
+import LLVM.Internal.Attribute
+import LLVM.Internal.BasicBlock  
+import LLVM.Internal.Coding
+import LLVM.Internal.Context
+import LLVM.Internal.DecodeAST
+import LLVM.Internal.EncodeAST
+import LLVM.Internal.Function
+import LLVM.Internal.Global
+import LLVM.Internal.Inject
+import LLVM.Internal.Instruction ()
+import qualified LLVM.Internal.MemoryBuffer as MB
+import LLVM.Internal.Metadata
+import LLVM.Internal.Operand
+import LLVM.Internal.RawOStream
+import LLVM.Internal.String
+import LLVM.Internal.Target
+import LLVM.Internal.Type
+import LLVM.Internal.Value
+
+import LLVM.DataLayout
+import LLVM.Diagnostic
+
+import qualified LLVM.AST as A
+import qualified LLVM.AST.DataLayout as A
+import qualified LLVM.AST.AddrSpace as A
+import qualified LLVM.AST.Global as A.G
+
+-- | <http://llvm.org/doxygen/classllvm_1_1Module.html>
+newtype Module = Module (IORef (Ptr FFI.Module))
+
+newModule :: Ptr FFI.Module -> IO (Module)
+newModule m = fmap Module (newIORef m)
+
+readModule :: MonadIO m => Module -> m (Ptr FFI.Module)
+readModule (Module ref) = liftIO $ readIORef ref
+
+-- | Signal that a module does no longer exist and thus must not be
+-- disposed. It is the responsibility of the caller to ensure that the
+-- module has been disposed. If you use only the functions provided by
+-- llvm-hs you should never call this yourself.
+deleteModule :: Module -> IO ()
+deleteModule (Module r) = writeIORef r nullPtr
+
+-- | A newtype to distinguish strings used for paths from other strings
+newtype File = File FilePath
+  deriving (Eq, Ord, Read, Show)
+
+instance Inject String (Either String Diagnostic) where
+    inject = Left
+
+-- | link LLVM modules - move or copy parts of a source module into a
+-- destination module.  Note that this operation is not commutative -
+-- not only concretely (e.g. the destination module is modified,
+-- becoming the result) but abstractly (e.g. unused private globals in
+-- the source module do not appear in the result, but similar globals
+-- in the destination remain). The source module is destroyed.
+linkModules ::
+     Module -- ^ The module into which to link
+  -> Module -- ^ The module to link into the other (this module is destroyed)
+  -> ExceptT String IO ()
+linkModules dest src  = flip runAnyContT return $ do
+  dest' <- readModule dest
+  src' <- readModule src
+  result <- decodeM =<< liftIO (FFI.linkModules dest' src')
+  -- linkModules takes care of deleting the sourcemodule
+  liftIO $ deleteModule src
+  when result (throwError "Couldn’t link modules")
+
+class LLVMAssemblyInput s where
+  llvmAssemblyMemoryBuffer :: (Inject String e, MonadError e m, MonadIO m, MonadAnyCont IO m)
+                              => s -> m (FFI.OwnerTransfered (Ptr FFI.MemoryBuffer))
+
+instance LLVMAssemblyInput (String, String) where
+  llvmAssemblyMemoryBuffer (id, s) = do
+    UTF8ByteString bs <- encodeM s
+    encodeM (MB.Bytes id bs)
+
+instance LLVMAssemblyInput String where
+  llvmAssemblyMemoryBuffer s = llvmAssemblyMemoryBuffer ("<string>", s)
+
+instance LLVMAssemblyInput File where
+  llvmAssemblyMemoryBuffer (File p) = encodeM (MB.File p)
+
+-- | parse 'Module' from LLVM assembly
+withModuleFromLLVMAssembly :: LLVMAssemblyInput s
+                              => Context -> s -> (Module -> IO a) -> ExceptT String IO a
+withModuleFromLLVMAssembly (Context c) s f = flip runAnyContT return $ do
+  mb <- llvmAssemblyMemoryBuffer s
+  msgPtr <- alloca
+  m <- anyContToM $ bracket (newModule =<< FFI.parseLLVMAssembly c mb msgPtr) (FFI.disposeModule <=< readModule)
+  m' <- readModule m
+  when (m' == nullPtr) $ throwError =<< decodeM msgPtr
+  liftIO $ f m
+
+-- | generate LLVM assembly from a 'Module'
+moduleLLVMAssembly :: Module -> IO String
+moduleLLVMAssembly m = do
+  resultRef <- newIORef Nothing
+  let saveBuffer :: Ptr CChar -> CSize -> IO ()
+      saveBuffer start size = do
+        r <- decodeM (start, fromIntegral size)
+        writeIORef resultRef (Just r)
+  m' <- readModule m
+  FFI.withBufferRawPWriteStream saveBuffer $ FFI.writeLLVMAssembly m' . FFI.upCast
+  Just s <- readIORef resultRef
+  return s
+
+-- | write LLVM assembly for a 'Module' to a file
+writeLLVMAssemblyToFile :: File -> Module -> ExceptT String IO ()
+writeLLVMAssemblyToFile (File path) m = flip runAnyContT return $ do
+  m' <- readModule m
+  withFileRawOStream path False True $ liftIO . (FFI.writeLLVMAssembly m')
+
+class BitcodeInput b where
+  bitcodeMemoryBuffer :: (Inject String e, MonadError e m, MonadIO m, MonadAnyCont IO m)
+                         => b -> m (Ptr FFI.MemoryBuffer)
+
+instance BitcodeInput (String, BS.ByteString) where
+  bitcodeMemoryBuffer (s, bs) = encodeM (MB.Bytes s bs)
+
+instance BitcodeInput File where
+  bitcodeMemoryBuffer (File p) = encodeM (MB.File p)
+
+-- | parse 'Module' from LLVM bitcode
+withModuleFromBitcode :: BitcodeInput b => Context -> b -> (Module -> IO a) -> ExceptT String IO a
+withModuleFromBitcode (Context c) b f = flip runAnyContT return $ do
+  mb <- bitcodeMemoryBuffer b
+  msgPtr <- alloca
+  m <- anyContToM $ bracket (newModule =<< FFI.parseBitcode c mb msgPtr) (FFI.disposeModule <=< readModule)
+  m' <- readModule m
+  when (m' == nullPtr) $ throwError =<< decodeM msgPtr
+  liftIO $ f m
+
+-- | generate LLVM bitcode from a 'Module'
+moduleBitcode :: Module -> IO BS.ByteString
+moduleBitcode m = do
+  m' <- readModule m
+  r <- runExceptT $ withBufferRawOStream (liftIO . FFI.writeBitcode m')
+  either fail return r
+
+-- | write LLVM bitcode from a 'Module' into a file
+writeBitcodeToFile :: File -> Module -> ExceptT String IO ()
+writeBitcodeToFile (File path) m = flip runAnyContT return $ do
+  m' <- readModule m
+  withFileRawOStream path False False $ liftIO . FFI.writeBitcode m'
+
+targetMachineEmit :: FFI.CodeGenFileType -> TargetMachine -> Module -> Ptr FFI.RawPWriteStream -> ExceptT String IO ()
+targetMachineEmit fileType (TargetMachine tm) m os = flip runAnyContT return $ do
+  msgPtr <- alloca
+  m' <- readModule m
+  r <- decodeM =<< (liftIO $ FFI.targetMachineEmit tm m' os fileType msgPtr)
+  when r $ throwError =<< decodeM msgPtr
+
+emitToFile :: FFI.CodeGenFileType -> TargetMachine -> File -> Module -> ExceptT String IO ()
+emitToFile fileType tm (File path) m = flip runAnyContT return $ do
+  withFileRawPWriteStream path False False $ targetMachineEmit fileType tm m
+
+emitToByteString :: FFI.CodeGenFileType -> TargetMachine -> Module -> ExceptT String IO BS.ByteString
+emitToByteString fileType tm m = flip runAnyContT return $ do
+  withBufferRawPWriteStream $ targetMachineEmit fileType tm m
+
+-- | write target-specific assembly directly into a file
+writeTargetAssemblyToFile :: TargetMachine -> File -> Module -> ExceptT String IO ()
+writeTargetAssemblyToFile = emitToFile FFI.codeGenFileTypeAssembly
+
+-- | produce target-specific assembly as a 'String'
+moduleTargetAssembly :: TargetMachine -> Module -> ExceptT String IO String
+moduleTargetAssembly tm m = decodeM . UTF8ByteString =<< emitToByteString FFI.codeGenFileTypeAssembly tm m
+
+-- | produce target-specific object code as a 'ByteString'
+moduleObject :: TargetMachine -> Module -> ExceptT String IO BS.ByteString
+moduleObject = emitToByteString FFI.codeGenFileTypeObject
+
+-- | write target-specific object code directly into a file
+writeObjectToFile :: TargetMachine -> File -> Module -> ExceptT String IO ()
+writeObjectToFile = emitToFile FFI.codeGenFileTypeObject
+
+setTargetTriple :: Ptr FFI.Module -> String -> EncodeAST ()
+setTargetTriple m t = do
+  t <- encodeM t
+  liftIO $ FFI.setTargetTriple m t
+
+getTargetTriple :: Ptr FFI.Module -> IO (Maybe String)
+getTargetTriple m = do
+  s <- decodeM =<< liftIO (FFI.getTargetTriple m)
+  return $ if s == "" then Nothing else Just s
+
+setDataLayout :: Ptr FFI.Module -> A.DataLayout -> EncodeAST ()
+setDataLayout m dl = do
+  s <- encodeM (dataLayoutToString dl)
+  liftIO $ FFI.setDataLayout m s
+
+getDataLayout :: Ptr FFI.Module -> IO (Maybe A.DataLayout)
+getDataLayout m = do
+  dlString <- decodeM =<< FFI.getDataLayout m
+  either fail return . runExcept . parseDataLayout A.BigEndian $ dlString
+
+-- | This function will call disposeModule after the callback
+-- exits. Calling 'deleteModule' prevents double free errors. As long
+-- as you only call functions provided by llvm-hs this should not
+-- be necessary since llvm-hs takes care of this.
+withModuleFromAST :: Context -> A.Module -> (Module -> IO a) -> ExceptT String IO a
+withModuleFromAST context@(Context c) (A.Module moduleId sourceFileName dataLayout triple definitions) f = runEncodeAST context $ do
+  moduleId <- encodeM moduleId
+  m <- anyContToM $ bracket (newModule =<< FFI.moduleCreateWithNameInContext moduleId c) (FFI.disposeModule <=< readModule)
+  ffiMod <- readModule m
+  sourceFileName' <- encodeM sourceFileName
+  liftIO $ FFI.setSourceFileName ffiMod sourceFileName'
+  Context context <- gets encodeStateContext
+  maybe (return ()) (setDataLayout ffiMod) dataLayout
+  maybe (return ()) (setTargetTriple ffiMod) triple
+  let sequencePhases :: EncodeAST [EncodeAST (EncodeAST (EncodeAST (EncodeAST ())))] -> EncodeAST ()
+      sequencePhases l = (l >>= (sequence >=> sequence >=> sequence >=> sequence)) >> (return ())
+  sequencePhases $ forM definitions $ \d -> case d of
+   A.TypeDefinition n t -> do
+     t' <- createNamedType n
+     defineType n t'
+     return $ do
+       maybe (return ()) (setNamedType t') t
+       return . return . return . return $ ()
+
+   A.COMDAT n csk -> do
+     n' <- encodeM n
+     csk <- encodeM csk
+     cd <- liftIO $ FFI.getOrInsertCOMDAT ffiMod n'
+     liftIO $ FFI.setCOMDATSelectionKind cd csk
+     defineCOMDAT n cd
+     return . return . return . return . return $ ()
+     
+   A.MetadataNodeDefinition i os -> return . return $ do
+     t <- liftIO $ FFI.createTemporaryMDNodeInContext context
+     defineMDNode i t
+     return $ do
+       n <- encodeM (A.MetadataNode os)
+       liftIO $ FFI.metadataReplaceAllUsesWith (FFI.upCast t) (FFI.upCast n)
+       defineMDNode i n
+       return $ return ()
+
+   A.NamedMetadataDefinition n ids -> return . return . return . return $ do
+     n <- encodeM n
+     ids <- encodeM (map A.MetadataNodeReference ids)
+     nm <- liftIO $ FFI.getOrAddNamedMetadata ffiMod n
+     liftIO $ FFI.namedMetadataAddOperands nm ids
+     return ()
+
+   A.ModuleInlineAssembly s -> do
+     s <- encodeM s
+     liftIO $ FFI.moduleAppendInlineAsm ffiMod (FFI.ModuleAsm s)
+     return . return . return . return . return $ ()
+
+   A.FunctionAttributes gid attrs -> do
+     attrs <- encodeM attrs
+     defineAttributeGroup gid attrs
+     return . return . return . return . return $ ()
+
+   A.GlobalDefinition g -> return . phase $ do
+     eg' :: EncodeAST (Ptr FFI.GlobalValue) <- case g of
+       g@(A.GlobalVariable { A.G.name = n }) -> do
+         typ <- encodeM (A.G.type' g)
+         g' <- liftIO $ withName n $ \gName ->
+                   FFI.addGlobalInAddressSpace ffiMod typ gName
+                          (fromIntegral ((\(A.AddrSpace a) -> a) $ A.G.addrSpace g))
+         defineGlobal n g'
+         setThreadLocalMode g' (A.G.threadLocalMode g)
+         liftIO $ do
+           hua <- encodeM (A.G.unnamedAddr g)
+           FFI.setUnnamedAddr (FFI.upCast g') hua
+           ic <- encodeM (A.G.isConstant g)
+           FFI.setGlobalConstant g' ic
+         return $ do
+           maybe (return ()) ((liftIO . FFI.setInitializer g') <=< encodeM) (A.G.initializer g)
+           setSection g' (A.G.section g)
+           setCOMDAT g' (A.G.comdat g)
+           setAlignment g' (A.G.alignment g)
+           return (FFI.upCast g')
+       (a@A.G.GlobalAlias { A.G.name = n }) -> do
+         let A.PointerType typ as = A.G.type' a
+         typ <- encodeM typ
+         as <- encodeM as
+         a' <- liftIO $ withName n $ \name -> FFI.justAddAlias ffiMod typ as name
+         defineGlobal n a'
+         liftIO $ do
+           hua <- encodeM (A.G.unnamedAddr a)
+           FFI.setUnnamedAddr (FFI.upCast a') hua
+         return $ do
+           setThreadLocalMode a' (A.G.threadLocalMode a)
+           (liftIO . FFI.setAliasee a') =<< encodeM (A.G.aliasee a)
+           return (FFI.upCast a')
+       (A.Function _ _ _ cc rAttrs resultType fName (args, isVarArgs) attrs _ _ _ gc prefix blocks personality) -> do
+         typ <- encodeM $ A.FunctionType resultType [t | A.Parameter t _ _ <- args] isVarArgs
+         f <- liftIO . withName fName $ \fName -> FFI.addFunction ffiMod fName typ
+         defineGlobal fName f
+         cc <- encodeM cc
+         liftIO $ FFI.setFunctionCallingConvention f cc
+         setFunctionAttributes f (MixedAttributeSet attrs rAttrs (Map.fromList $ zip [0..] [pa | A.Parameter _ _ pa <- args]))
+         setPrefixData f prefix
+         setSection f (A.G.section g)
+         setCOMDAT f (A.G.comdat g)
+         setAlignment f (A.G.alignment g)
+         setGC f gc
+         setPersonalityFn f personality
+         forM blocks $ \(A.BasicBlock bName _ _) -> do
+           b <- liftIO $ withName bName $ \bName -> FFI.appendBasicBlockInContext context f bName
+           defineBasicBlock fName bName b
+         phase $ do
+           let nParams = length args
+           ps <- allocaArray nParams
+           liftIO $ FFI.getParams f ps
+           params <- peekArray nParams ps
+           forM (zip args params) $ \(A.Parameter _ n _, p) -> do
+             defineLocal n p
+             n <- encodeM n
+             liftIO $ FFI.setValueName (FFI.upCast p) n
+           finishInstrs <- forM blocks $ \(A.BasicBlock bName namedInstrs term) -> do
+             b <- encodeM bName
+             (do
+               builder <- gets encodeStateBuilder
+               liftIO $ FFI.positionBuilderAtEnd builder b)
+             finishes <- mapM encodeM namedInstrs :: EncodeAST [EncodeAST ()]
+             (encodeM term :: EncodeAST (Ptr FFI.Instruction))
+             return (sequence_ finishes)
+           sequence_ finishInstrs
+           locals <- gets $ Map.toList . encodeStateLocals
+           forM [ n | (n, ForwardValue _) <- locals ] $ \n -> undefinedReference "local" n
+           return (FFI.upCast f)
+     return $ do
+       g' <- eg'
+       setLinkage g' (A.G.linkage g)
+       setVisibility g' (A.G.visibility g)
+       setDLLStorageClass g' (A.G.dllStorageClass g)
+       return $ return ()
+  liftIO $ f m
+
+
+-- This returns a nested DecodeAST to allow interleaving of different
+-- decoding steps. Take a look at the call site in moduleAST for more
+-- details.
+decodeGlobalVariables :: Ptr FFI.Module -> DecodeAST (DecodeAST [A.G.Global])
+decodeGlobalVariables mod = do
+  ffiGlobals <- liftIO $ FFI.getXs (FFI.getFirstGlobal mod) FFI.getNextGlobal
+  fmap sequence . forM ffiGlobals $ \g -> do
+    A.PointerType t as <- typeOf g
+    n <- getGlobalName g
+    return $
+      A.GlobalVariable
+        <$> return n
+        <*> getLinkage g
+        <*> getVisibility g
+        <*> getDLLStorageClass g
+        <*> getThreadLocalMode g
+        <*> return as
+        <*> (liftIO $ decodeM =<< FFI.getUnnamedAddr (FFI.upCast g))
+        <*> (liftIO $ decodeM =<< FFI.isGlobalConstant g)
+        <*> return t
+        <*> (do i <- liftIO $ FFI.getInitializer g
+                if i == nullPtr
+                  then return Nothing
+                  else Just <$> decodeM i)
+        <*> getSection g
+        <*> getCOMDATName g
+        <*> getAlignment g
+
+-- This returns a nested DecodeAST to allow interleaving of different
+-- decoding steps. Take a look at the call site in moduleAST for more
+-- details.
+decodeGlobalAliases :: Ptr FFI.Module -> DecodeAST (DecodeAST [A.G.Global])
+decodeGlobalAliases mod = do
+  ffiAliases <- liftIO $ FFI.getXs (FFI.getFirstAlias mod) FFI.getNextAlias
+  fmap sequence . forM ffiAliases $ \a -> do
+    n <- getGlobalName a
+    return $
+      A.G.GlobalAlias
+        <$> return n
+        <*> getLinkage a
+        <*> getVisibility a
+        <*> getDLLStorageClass a
+        <*> getThreadLocalMode a
+        <*> (liftIO $ decodeM =<< FFI.getUnnamedAddr (FFI.upCast a))
+        <*> typeOf a
+        <*> (decodeM =<< (liftIO $ FFI.getAliasee a))
+
+-- This returns a nested DecodeAST to allow interleaving of different
+-- decoding steps. Take a look at the call site in moduleAST for more
+-- details.
+decodeFunctions :: Ptr FFI.Module -> DecodeAST (DecodeAST [A.G.Global])
+decodeFunctions mod = do
+  ffiFunctions <-
+    liftIO $ FFI.getXs (FFI.getFirstFunction mod) FFI.getNextFunction
+  fmap sequence . forM ffiFunctions $ \f ->
+    localScope $ do
+      A.PointerType (A.FunctionType returnType _ isVarArg) _ <- typeOf f
+      n <- getGlobalName f
+      MixedAttributeSet fAttrs rAttrs pAttrs <- getMixedAttributeSet f
+      parameters <- getParameters f pAttrs
+      decodeBlocks <- do
+        ffiBasicBlocks <-
+          liftIO $ FFI.getXs (FFI.getFirstBasicBlock f) FFI.getNextBasicBlock
+        fmap sequence . forM ffiBasicBlocks $ \b -> do
+          n <- getLocalName b
+          decodeInstructions <- getNamedInstructions b
+          decodeTerminator <- getBasicBlockTerminator b
+          return $
+            A.BasicBlock
+              <$> return n
+              <*> decodeInstructions
+              <*> decodeTerminator
+      return $
+        A.Function
+          <$> getLinkage f
+          <*> getVisibility f
+          <*> getDLLStorageClass f
+          <*> (liftIO $ decodeM =<< FFI.getFunctionCallingConvention f)
+          <*> return rAttrs
+          <*> return returnType
+          <*> return n
+          <*> return (parameters, isVarArg)
+          <*> return fAttrs
+          <*> getSection f
+          <*> getCOMDATName f
+          <*> getAlignment f
+          <*> getGC f
+          <*> getPrefixData f
+          <*> decodeBlocks
+          <*> getPersonalityFn f
+
+decodeNamedMetadataDefinitions :: Ptr FFI.Module -> DecodeAST [A.Definition]
+decodeNamedMetadataDefinitions mod = do
+  ffiNamedMetadataNodes <-
+    liftIO $ FFI.getXs (FFI.getFirstNamedMetadata mod) FFI.getNextNamedMetadata
+  forM ffiNamedMetadataNodes $ \nm ->
+    scopeAnyCont $ do
+      n <- liftIO $ FFI.getNamedMetadataNumOperands nm
+      os <- allocaArray n
+      liftIO $ FFI.getNamedMetadataOperands nm os
+      A.NamedMetadataDefinition
+        <$> (decodeM $ FFI.getNamedMetadataName nm)
+        <*> fmap
+              (map (\(A.MetadataNodeReference mid) -> mid))
+              (decodeM (n, os))
+
+-- | Get an LLVM.AST.'LLVM.AST.Module' from a LLVM.'Module' - i.e.
+-- raise C++ objects into an Haskell AST.
+moduleAST :: Module -> IO A.Module
+moduleAST m = runDecodeAST $ do
+  mod <- readModule m
+  c <- return Context `ap` liftIO (FFI.getModuleContext mod)
+  getMetadataKindNames c
+  A.Module
+    <$> (liftIO $ decodeM =<< FFI.getModuleIdentifier mod)
+    <*> (liftIO $ decodeM =<< FFI.getSourceFileName mod)
+    <*> (liftIO $ getDataLayout mod)
+    <*> (liftIO $ do
+           s <- decodeM =<< FFI.getTargetTriple mod
+           return $ if s == "" then Nothing else Just s)
+    <*> (do
+      globalDefinitions <-
+        map A.GlobalDefinition . concat <$>
+        -- Variables, aliases & functions can reference each other. To
+        -- resolve this references properly during decoding a two step
+        -- process is used: In the first step, the names of the
+        -- different definitions are stored. In the second step we can
+        -- then decode the definitions and look up the previously
+        -- stored references.
+        (join . fmap sequence . sequence)
+          [ decodeGlobalVariables mod
+          , decodeGlobalAliases mod
+          , decodeFunctions mod
+          ]
+      structDefinitions <- getStructDefinitions
+      inlineAsm <- decodeM =<< liftIO (FFI.moduleGetInlineAsm mod)
+      namedMetadata <- decodeNamedMetadataDefinitions mod
+      metadata <- getMetadataDefinitions
+      functionAttributes <- do
+        functionAttributes <- gets $ Map.toList . functionAttributeSetIDs
+        forM functionAttributes $ \(as, gid) ->
+          A.FunctionAttributes <$> return gid <*> decodeM as
+      comdats <- gets $ map (uncurry A.COMDAT) . Map.elems . comdats
+      return $
+        structDefinitions ++
+        inlineAsm ++
+        globalDefinitions ++
+        namedMetadata ++
+        metadata ++
+        functionAttributes ++
+        comdats)
diff --git a/src/LLVM/Internal/Operand.hs b/src/LLVM/Internal/Operand.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/Operand.hs
@@ -0,0 +1,131 @@
+{-# LANGUAGE
+  MultiParamTypeClasses
+  #-}
+module LLVM.Internal.Operand where
+
+import LLVM.Prelude
+
+import Control.Monad.State
+import Control.Monad.AnyCont
+import qualified Data.Map as Map
+
+import Foreign.Ptr
+
+import qualified LLVM.Internal.FFI.Constant as FFI
+import qualified LLVM.Internal.FFI.InlineAssembly as FFI
+import qualified LLVM.Internal.FFI.Metadata as FFI
+import qualified LLVM.Internal.FFI.PtrHierarchy as FFI
+import qualified LLVM.Internal.FFI.Value as FFI
+
+import LLVM.Internal.Coding
+import LLVM.Internal.Constant ()
+import LLVM.Internal.Context
+import LLVM.Internal.DecodeAST
+import LLVM.Internal.EncodeAST
+import LLVM.Internal.InlineAssembly ()
+import LLVM.Internal.Metadata ()
+
+import qualified LLVM.AST as A
+
+instance DecodeM DecodeAST A.Operand (Ptr FFI.Value) where
+  decodeM v = do
+    c <- liftIO $ FFI.isAConstant v
+    if (c /= nullPtr) 
+     then
+      return A.ConstantOperand `ap` decodeM c
+     else
+      do m <- liftIO $ FFI.isAMetadataOperand v
+         if (m /= nullPtr)
+            then A.MetadataOperand <$> decodeM m
+            else return A.LocalReference
+                           `ap` (decodeM =<< (liftIO $ FFI.typeOf v))
+                           `ap` getLocalName v
+
+instance DecodeM DecodeAST A.Metadata (Ptr FFI.Metadata) where
+  decodeM md = do
+    s <- liftIO $ FFI.isAMDString md
+    if (s /= nullPtr)
+       then A.MDString <$> decodeM s
+       else do n <- liftIO $ FFI.isAMDNode md
+               if (n /= nullPtr)
+                  then A.MDNode <$> decodeM n
+                  else do v <- liftIO $ FFI.isAMDValue md
+                          if (v /= nullPtr)
+                              then A.MDValue <$> decodeM v
+                              else fail "Metadata was not one of [MDString, MDValue, MDNode]"
+
+instance DecodeM DecodeAST A.CallableOperand (Ptr FFI.Value) where
+  decodeM v = do
+    ia <- liftIO $ FFI.isAInlineAsm v
+    if ia /= nullPtr
+     then liftM Left (decodeM ia)
+     else liftM Right (decodeM v)
+
+instance EncodeM EncodeAST A.Operand (Ptr FFI.Value) where
+  encodeM (A.ConstantOperand c) = (FFI.upCast :: Ptr FFI.Constant -> Ptr FFI.Value) <$> encodeM c
+  encodeM (A.LocalReference t n) = do
+    lv <- refer encodeStateLocals n $ do
+      lv <- do
+        n <- encodeM n
+        t <- encodeM t
+        v <- liftIO $ FFI.createArgument t n
+        return $ ForwardValue v
+      modify $ \s -> s { encodeStateLocals = Map.insert n lv $ encodeStateLocals s }
+      return lv
+    return $ case lv of DefinedValue v -> v; ForwardValue v -> v
+  encodeM (A.MetadataOperand md) = do
+    md' <- encodeM md
+    Context c <- gets encodeStateContext
+    liftIO $ FFI.upCast <$> FFI.metadataOperand c md'
+
+instance EncodeM EncodeAST A.Metadata (Ptr FFI.Metadata) where
+  encodeM (A.MDString s) = do
+    Context c <- gets encodeStateContext
+    s <- encodeM s
+    liftM FFI.upCast $ liftIO $ FFI.mdStringInContext c s
+  encodeM (A.MDNode mdn) = (FFI.upCast :: Ptr FFI.MDNode -> Ptr FFI.Metadata) <$> encodeM mdn
+  encodeM (A.MDValue v) = do
+     v <- encodeM v
+     liftIO $ FFI.upCast <$> FFI.mdValue v
+
+instance EncodeM EncodeAST A.CallableOperand (Ptr FFI.Value) where
+  encodeM (Right o) = encodeM o
+  encodeM (Left i) = liftM (FFI.upCast :: Ptr FFI.InlineAsm -> Ptr FFI.Value) (encodeM i)
+
+instance EncodeM EncodeAST A.MetadataNode (Ptr FFI.MDNode) where
+  encodeM (A.MetadataNode ops) = scopeAnyCont $ do
+    Context c <- gets encodeStateContext
+    ops <- encodeM ops
+    liftIO $ FFI.createMDNodeInContext c ops
+  encodeM (A.MetadataNodeReference n) = referMDNode n
+
+instance DecodeM DecodeAST [Maybe A.Metadata] (Ptr FFI.MDNode) where
+  decodeM p = scopeAnyCont $ do
+    n <- liftIO $ FFI.getMDNodeNumOperands p
+    ops <- allocaArray n
+    liftIO $ FFI.getMDNodeOperands p ops
+    decodeM (n, ops)
+
+instance DecodeM DecodeAST A.Operand (Ptr FFI.MDValue) where
+  decodeM = decodeM <=< liftIO . FFI.getMDValue
+
+instance DecodeM DecodeAST A.Metadata (Ptr FFI.MetadataAsVal) where
+  decodeM = decodeM <=< liftIO . FFI.getMetadataOperand
+
+instance DecodeM DecodeAST A.MetadataNode (Ptr FFI.MDNode) where
+  decodeM p = scopeAnyCont $ do
+
+    -- fl <- decodeM =<< liftIO (FFI.mdNodeIsFunctionLocal p)
+    -- if fl
+    --  then
+    --    return A.MetadataNode `ap` decodeM p
+    --  else
+       return A.MetadataNodeReference `ap` getMetadataNodeID p
+
+getMetadataDefinitions :: DecodeAST [A.Definition]
+getMetadataDefinitions = fix $ \continue -> do
+  mdntd <- takeMetadataNodeToDefine
+  flip (maybe (return [])) mdntd $ \(mid, p) -> do
+    return (:)
+      `ap` (return A.MetadataNodeDefinition `ap` return mid `ap` decodeM p)
+      `ap` continue
diff --git a/src/LLVM/Internal/OrcJIT.hs b/src/LLVM/Internal/OrcJIT.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/OrcJIT.hs
@@ -0,0 +1,107 @@
+{-# LANGUAGE MultiParamTypeClasses #-}
+module LLVM.Internal.OrcJIT where
+
+import LLVM.Prelude
+
+import Control.Exception
+import Control.Monad.AnyCont
+import Control.Monad.IO.Class
+import Data.Bits
+import Data.ByteString (ByteString, packCString, useAsCString)
+import Data.IORef
+import Foreign.C.String
+import Foreign.Ptr
+
+import LLVM.Internal.Coding
+import qualified LLVM.Internal.FFI.LLVMCTypes as FFI
+import qualified LLVM.Internal.FFI.OrcJIT as FFI
+
+newtype MangledSymbol = MangledSymbol ByteString
+  deriving (Show, Eq, Ord)
+
+instance EncodeM (AnyContT IO) MangledSymbol CString where
+  encodeM (MangledSymbol bs) = anyContToM $ useAsCString bs
+
+instance MonadIO m => DecodeM m MangledSymbol CString where
+  decodeM str = liftIO $ MangledSymbol <$> packCString str
+
+data JITSymbolFlags =
+  JITSymbolFlags {
+    jitSymbolWeak :: !Bool,
+    jitSymbolExported :: !Bool
+  }
+  deriving (Show, Eq, Ord)
+
+data JITSymbol =
+  JITSymbol {
+    jitSymbolAddress :: !WordPtr,
+    jitSymbolFlags :: !JITSymbolFlags
+  }
+  deriving (Show, Eq, Ord)
+
+type SymbolResolverFn = MangledSymbol -> IO JITSymbol
+
+data SymbolResolver =
+  SymbolResolver {
+    dylibResolver :: !SymbolResolverFn,
+    externalResolver :: !SymbolResolverFn
+  }
+
+newtype ObjectLinkingLayer = ObjectLinkingLayer (Ptr FFI.ObjectLinkingLayer)
+
+instance Monad m => EncodeM m JITSymbolFlags FFI.JITSymbolFlags where
+  encodeM f = return $ foldr1 (.|.) [
+      if a f
+         then b
+         else 0
+    | (a,b) <- [
+          (jitSymbolWeak, FFI.jitSymbolFlagsWeak),
+          (jitSymbolExported, FFI.jitSymbolFlagsExported)
+        ]
+    ]
+
+instance Monad m => DecodeM m JITSymbolFlags FFI.JITSymbolFlags where
+  decodeM f =
+    return $ JITSymbolFlags {
+      jitSymbolWeak = FFI.jitSymbolFlagsWeak .&. f /= 0,
+      jitSymbolExported = FFI.jitSymbolFlagsExported .&. f /= 0
+    }
+
+instance MonadIO m => EncodeM m JITSymbol (Ptr FFI.JITSymbol -> IO ()) where
+  encodeM (JITSymbol addr flags) = return $ \jitSymbol -> do
+    flags' <- encodeM flags
+    FFI.setJITSymbol jitSymbol (FFI.TargetAddress (fromIntegral addr)) flags'
+
+instance MonadIO m => DecodeM m JITSymbol (Ptr FFI.JITSymbol) where
+  decodeM jitSymbol = do
+    FFI.TargetAddress addr <- liftIO $ FFI.getAddress jitSymbol
+    flags <- liftIO $ decodeM =<< FFI.getFlags jitSymbol
+    return (JITSymbol (fromIntegral addr) flags)
+
+instance MonadIO m =>
+  EncodeM m SymbolResolver (IORef [IO ()] -> IO (Ptr FFI.LambdaResolver)) where
+  encodeM (SymbolResolver dylib external) = return $ \cleanups -> do
+    dylib' <- allocFunPtr cleanups (encodeM dylib)
+    external' <- allocFunPtr cleanups (encodeM external)
+    FFI.createLambdaResolver dylib' external'
+
+instance MonadIO m => EncodeM m SymbolResolverFn (FunPtr FFI.SymbolResolverFn) where
+  encodeM callback =
+    liftIO $ FFI.wrapSymbolResolverFn
+      (\symbol result -> do
+         setSymbol <- encodeM =<< callback =<< decodeM symbol
+         setSymbol result)
+
+-- | allocate a function pointer and register it for cleanup
+allocFunPtr :: IORef [IO ()] -> IO (FunPtr a) -> IO (FunPtr a)
+allocFunPtr cleanups alloc = mask $ \restore -> do
+  funPtr <- restore alloc
+  modifyIORef cleanups (freeHaskellFunPtr funPtr :)
+  pure funPtr
+
+withObjectLinkingLayer :: (ObjectLinkingLayer -> IO a) -> IO a
+withObjectLinkingLayer f =
+  bracket
+    FFI.createObjectLinkingLayer
+    FFI.disposeObjectLinkingLayer $ \objectLayer ->
+      f (ObjectLinkingLayer objectLayer)
diff --git a/src/LLVM/Internal/OrcJIT/CompileOnDemandLayer.hs b/src/LLVM/Internal/OrcJIT/CompileOnDemandLayer.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/OrcJIT/CompileOnDemandLayer.hs
@@ -0,0 +1,146 @@
+{-# LANGUAGE MultiParamTypeClasses #-}
+module LLVM.Internal.OrcJIT.CompileOnDemandLayer where
+
+import LLVM.Prelude
+
+import Control.Exception
+import Control.Monad.AnyCont
+import Control.Monad.IO.Class
+import Data.IORef
+import Foreign.Marshal.Array
+import Foreign.Ptr
+
+import LLVM.Internal.Coding
+import LLVM.Internal.Module
+import LLVM.Internal.OrcJIT
+import LLVM.Internal.OrcJIT.IRCompileLayer (IRCompileLayer(..))
+import qualified LLVM.Internal.OrcJIT.IRCompileLayer as IRCompileLayer
+import qualified LLVM.Internal.FFI.OrcJIT as FFI
+import qualified LLVM.Internal.FFI.OrcJIT.CompileOnDemandLayer as FFI
+import qualified LLVM.Internal.FFI.PtrHierarchy as FFI
+
+type PartitioningFn = Ptr FFI.Function -> IO [Ptr FFI.Function]
+
+newtype JITCompileCallbackManager =
+  CallbackMgr (Ptr FFI.JITCompileCallbackManager)
+
+newtype IndirectStubsManagerBuilder =
+  StubsMgr (Ptr FFI.IndirectStubsManagerBuilder)
+
+data CompileOnDemandLayer =
+  CompileOnDemandLayer {
+    compileLayer :: !(Ptr FFI.CompileOnDemandLayer),
+    baseLayer :: !IRCompileLayer,
+    cleanupActions :: !(IORef [IO ()])
+  }
+  deriving Eq
+
+newtype ModuleSet = ModuleSet (Ptr FFI.ModuleSetHandle)
+
+instance MonadIO m =>
+  EncodeM m PartitioningFn (IORef [IO ()] -> IO (FunPtr FFI.PartitioningFn)) where
+  encodeM partition = return $ \cleanups -> do
+    allocFunPtr
+      cleanups
+      (FFI.wrapPartitioningFn
+         (\f set -> do
+           fs <- partition f
+           traverse_ (FFI.insertFun set) fs
+           return ()))
+
+instance (MonadIO m, MonadAnyCont IO m) =>
+  EncodeM m (Maybe (IO ())) FFI.TargetAddress where
+  encodeM Nothing = return $ FFI.TargetAddress 0
+  encodeM (Just f) = do
+    f' <- anyContToM $ bracket (FFI.wrapErrorHandler f) freeHaskellFunPtr
+    return . FFI.TargetAddress . fromIntegral . ptrToWordPtr . castFunPtrToPtr $ f'
+
+withIndirectStubsManagerBuilder ::
+  String {- ^ triple -} ->
+  (IndirectStubsManagerBuilder -> IO a) ->
+  IO a
+withIndirectStubsManagerBuilder triple f = flip runAnyContT return $ do
+  triple' <- encodeM triple
+  stubsMgr <- anyContToM $ bracket
+    (FFI.createLocalIndirectStubsManagerBuilder triple')
+    FFI.disposeIndirectStubsManagerBuilder
+  liftIO $ f (StubsMgr stubsMgr)
+
+withJITCompileCallbackManager ::
+  String {- ^ triple -} ->
+  Maybe (IO ()) ->
+  (JITCompileCallbackManager -> IO a) ->
+  IO a
+withJITCompileCallbackManager triple errorHandler f = flip runAnyContT return $ do
+  triple' <- encodeM triple
+  errorHandler' <- encodeM errorHandler
+  callbackMgr <- anyContToM $ bracket
+    (FFI.createLocalCompileCallbackManager triple' errorHandler')
+    FFI.disposeCallbackManager
+  liftIO $ f (CallbackMgr callbackMgr)
+
+withCompileOnDemandLayer ::
+  IRCompileLayer ->
+  PartitioningFn ->
+  JITCompileCallbackManager ->
+  IndirectStubsManagerBuilder ->
+  Bool ->
+  (CompileOnDemandLayer -> IO a) ->
+  IO a
+withCompileOnDemandLayer
+ baseLayer@(IRCompileLayer base _ _)
+ partition
+ (CallbackMgr callbackMgr)
+ (StubsMgr stubsMgr)
+ cloneStubsIntoPartitions
+ f
+ = flip runAnyContT return $ do
+ cleanup <- anyContToM $ bracket (newIORef []) (sequence <=< readIORef)
+ partitionAct <- encodeM partition
+ partition' <- liftIO $ partitionAct cleanup
+ cloneStubsIntoPartitions' <- encodeM cloneStubsIntoPartitions
+ cl <- anyContToM $ bracket
+         (FFI.createCompileOnDemandLayer
+            base
+            partition'
+            callbackMgr
+            stubsMgr
+            cloneStubsIntoPartitions')
+         FFI.disposeCompileOnDemandLayer
+ liftIO $ f (CompileOnDemandLayer cl baseLayer cleanup)
+
+mangleSymbol :: CompileOnDemandLayer -> String -> IO MangledSymbol
+mangleSymbol (CompileOnDemandLayer _ bl _) symbol =
+  IRCompileLayer.mangleSymbol bl symbol
+
+findSymbol :: CompileOnDemandLayer -> MangledSymbol -> Bool -> IO JITSymbol
+findSymbol (CompileOnDemandLayer cl _ _) symbol exportedSymbolsOnly = flip runAnyContT return $ do
+  symbol' <- encodeM symbol
+  exportedSymbolsOnly' <- encodeM exportedSymbolsOnly
+  symbol <- anyContToM $ bracket
+    (FFI.findSymbol cl symbol' exportedSymbolsOnly') FFI.disposeSymbol
+  decodeM symbol
+
+addModuleSet :: CompileOnDemandLayer -> [Module] -> SymbolResolver -> IO ModuleSet
+addModuleSet
+  (CompileOnDemandLayer cl (IRCompileLayer _ dl _) cleanups)
+  modules
+  resolver
+  = flip runAnyContT return $ do
+  resolverAct <- encodeM resolver
+  resolver' <- liftIO $ resolverAct cleanups
+  modules' <- liftIO $ mapM readModule modules
+  (moduleCount, modules'') <-
+    anyContToM $ \f -> withArrayLen modules' $ \n hs -> f (fromIntegral n, hs)
+  moduleSet <- liftIO $ FFI.addModuleSet cl dl modules'' moduleCount resolver'
+  pure (ModuleSet moduleSet)
+
+removeModuleSet :: CompileOnDemandLayer -> ModuleSet -> IO ()
+removeModuleSet (CompileOnDemandLayer cl _ _) (ModuleSet handle) =
+  FFI.removeModuleSet cl handle
+
+withModuleSet :: CompileOnDemandLayer -> [Module] -> SymbolResolver -> (ModuleSet -> IO a) -> IO a
+withModuleSet compileLayer modules resolver =
+  bracket
+    (addModuleSet compileLayer modules resolver)
+    (removeModuleSet compileLayer)
diff --git a/src/LLVM/Internal/OrcJIT/IRCompileLayer.hs b/src/LLVM/Internal/OrcJIT/IRCompileLayer.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/OrcJIT/IRCompileLayer.hs
@@ -0,0 +1,74 @@
+{-# LANGUAGE MultiParamTypeClasses #-}
+module LLVM.Internal.OrcJIT.IRCompileLayer where
+
+import LLVM.Prelude
+
+import Control.Exception
+import Control.Monad.AnyCont
+import Control.Monad.IO.Class
+import Data.IORef
+import Foreign.Marshal.Array (withArrayLen)
+import Foreign.Ptr
+
+import LLVM.Internal.Coding
+import qualified LLVM.Internal.FFI.DataLayout as FFI
+import qualified LLVM.Internal.FFI.OrcJIT as FFI
+import qualified LLVM.Internal.FFI.OrcJIT.IRCompileLayer as FFI
+import qualified LLVM.Internal.FFI.Target as FFI
+import LLVM.Internal.Module
+import LLVM.Internal.OrcJIT
+import LLVM.Internal.Target
+
+data IRCompileLayer =
+  IRCompileLayer {
+    compileLayer :: !(Ptr FFI.IRCompileLayer),
+    dataLayout :: !(Ptr FFI.DataLayout),
+    cleanupActions :: !(IORef [IO ()])
+  }
+  deriving Eq
+
+newtype ModuleSet = ModuleSet (Ptr FFI.ModuleSetHandle)
+
+withIRCompileLayer :: ObjectLinkingLayer -> TargetMachine -> (IRCompileLayer -> IO a) -> IO a
+withIRCompileLayer (ObjectLinkingLayer oll) (TargetMachine tm) f = flip runAnyContT return $ do
+  dl <- anyContToM $ bracket (FFI.createTargetDataLayout tm) FFI.disposeDataLayout
+  cl <- anyContToM $ bracket (FFI.createIRCompileLayer oll tm) FFI.disposeIRCompileLayer
+  cleanup <- anyContToM $ bracket (newIORef []) (sequence <=< readIORef)
+  liftIO $ f (IRCompileLayer cl dl cleanup)
+
+mangleSymbol :: IRCompileLayer -> String -> IO MangledSymbol
+mangleSymbol (IRCompileLayer _ dl _) symbol = flip runAnyContT return $ do
+  mangledSymbol <- alloca
+  symbol' <- encodeM symbol
+  anyContToM $ bracket
+    (FFI.getMangledSymbol mangledSymbol symbol' dl)
+    (\_ -> FFI.disposeMangledSymbol =<< peek mangledSymbol)
+  decodeM =<< peek mangledSymbol
+
+findSymbol :: IRCompileLayer -> MangledSymbol -> Bool -> IO JITSymbol
+findSymbol (IRCompileLayer cl _ _) symbol exportedSymbolsOnly = flip runAnyContT return $ do
+  symbol' <- encodeM symbol
+  exportedSymbolsOnly' <- encodeM exportedSymbolsOnly
+  symbol <- anyContToM $ bracket
+    (FFI.findSymbol cl symbol' exportedSymbolsOnly') FFI.disposeSymbol
+  decodeM symbol
+
+addModuleSet :: IRCompileLayer -> [Module] -> SymbolResolver -> IO ModuleSet
+addModuleSet (IRCompileLayer cl dl cleanups) modules resolver = flip runAnyContT return $ do
+  resolverAct <- encodeM resolver
+  resolver' <- liftIO $ resolverAct cleanups
+  modules' <- liftIO $ mapM readModule modules
+  (moduleCount, modules'') <-
+    anyContToM $ \f -> withArrayLen modules' $ \n hs -> f (fromIntegral n, hs)
+  moduleSet <- liftIO $ FFI.addModuleSet cl dl modules'' moduleCount resolver'
+  pure (ModuleSet moduleSet)
+
+removeModuleSet :: IRCompileLayer -> ModuleSet -> IO ()
+removeModuleSet (IRCompileLayer cl _ _) (ModuleSet handle) =
+  FFI.removeModuleSet cl handle
+
+withModuleSet :: IRCompileLayer -> [Module] -> SymbolResolver -> (ModuleSet -> IO a) -> IO a
+withModuleSet compileLayer modules resolver =
+  bracket
+    (addModuleSet compileLayer modules resolver)
+    (removeModuleSet compileLayer)
diff --git a/src/LLVM/Internal/PassManager.hs b/src/LLVM/Internal/PassManager.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/PassManager.hs
@@ -0,0 +1,147 @@
+{-# LANGUAGE
+  TemplateHaskell,
+  MultiParamTypeClasses,
+  CPP
+  #-}
+module LLVM.Internal.PassManager where
+
+import LLVM.Prelude
+
+import qualified Language.Haskell.TH as TH
+
+import Control.Exception
+import Control.Monad.IO.Class
+
+import Control.Monad.AnyCont
+
+import Foreign.C (CString)
+import Foreign.Ptr
+
+import qualified LLVM.Internal.FFI.PassManager as FFI
+import qualified LLVM.Internal.FFI.Transforms as FFI
+
+import LLVM.Internal.Module
+import LLVM.Internal.Target
+import LLVM.Internal.Coding
+import LLVM.Transforms
+
+import LLVM.AST.DataLayout
+
+-- | <http://llvm.org/doxygen/classllvm_1_1PassManager.html>
+-- Note: a PassManager does substantive behind-the-scenes work, arranging for the
+-- results of various analyses to be available as needed by transform passes, shared
+-- as possible.
+newtype PassManager = PassManager (Ptr FFI.PassManager)
+
+-- | There are different ways to get a 'PassManager'. This type embodies them.
+data PassSetSpec
+  -- | a 'PassSetSpec' is a lower-level, detailed specification of a set of passes. It
+  -- allows fine-grained control of what passes are to be run when, and the specification
+  -- of passes not available through 'CuratedPassSetSpec'.
+  = PassSetSpec {
+      transforms :: [Pass],
+      dataLayout :: Maybe DataLayout,
+      targetLibraryInfo :: Maybe TargetLibraryInfo,
+      targetMachine :: Maybe TargetMachine
+    }
+  -- | This type is a high-level specification of a set of passes. It uses the same
+  -- collection of passes chosen by the LLVM team in the command line tool 'opt'.  The fields
+  -- of this spec are much like typical compiler command-line flags - e.g. -O\<n\>, etc.
+  | CuratedPassSetSpec {
+      optLevel :: Maybe Word,
+      sizeLevel :: Maybe Word,
+      unitAtATime :: Maybe Bool,
+      simplifyLibCalls :: Maybe Bool,
+      loopVectorize :: Maybe Bool,
+      superwordLevelParallelismVectorize :: Maybe Bool,
+      useInlinerWithThreshold :: Maybe Word,
+      dataLayout :: Maybe DataLayout,
+      targetLibraryInfo :: Maybe TargetLibraryInfo,
+      targetMachine :: Maybe TargetMachine
+    }
+
+-- | Helper to make a curated 'PassSetSpec'
+defaultCuratedPassSetSpec :: PassSetSpec
+defaultCuratedPassSetSpec = CuratedPassSetSpec {
+  optLevel = Nothing,
+  sizeLevel = Nothing,
+  unitAtATime = Nothing,
+  simplifyLibCalls = Nothing,
+  loopVectorize = Nothing,
+  superwordLevelParallelismVectorize = Nothing,
+  useInlinerWithThreshold = Nothing,
+  dataLayout = Nothing,
+  targetLibraryInfo = Nothing,
+  targetMachine = Nothing
+}
+
+-- | an empty 'PassSetSpec'
+defaultPassSetSpec :: PassSetSpec
+defaultPassSetSpec = PassSetSpec {
+  transforms = [],
+  dataLayout = Nothing,
+  targetLibraryInfo = Nothing,
+  targetMachine = Nothing
+}
+
+instance (Monad m, MonadAnyCont IO m) => EncodeM m GCOVVersion CString where
+  encodeM (GCOVVersion cs@[_,_,_,_]) = encodeM cs
+
+createPassManager :: PassSetSpec -> IO (Ptr FFI.PassManager)
+createPassManager pss = flip runAnyContT return $ do
+  pm <- liftIO $ FFI.createPassManager
+  forM_ (targetLibraryInfo pss) $ \(TargetLibraryInfo tli) -> do
+    liftIO $ FFI.addTargetLibraryInfoPass pm tli
+  forM_ (targetMachine pss) $ \(TargetMachine tm) -> liftIO $ FFI.addAnalysisPasses tm pm
+  case pss of
+    s@CuratedPassSetSpec {} -> liftIO $ do
+      bracket FFI.passManagerBuilderCreate FFI.passManagerBuilderDispose $ \b -> do
+        let handleOption g m = forM_ (m s) (g b <=< encodeM) 
+        handleOption FFI.passManagerBuilderSetOptLevel optLevel
+        handleOption FFI.passManagerBuilderSetSizeLevel sizeLevel
+        handleOption FFI.passManagerBuilderSetDisableUnitAtATime (liftM not . unitAtATime)
+        handleOption FFI.passManagerBuilderSetDisableSimplifyLibCalls (liftM not . simplifyLibCalls)
+        handleOption FFI.passManagerBuilderUseInlinerWithThreshold useInlinerWithThreshold
+        handleOption FFI.passManagerBuilderSetLoopVectorize loopVectorize
+        handleOption FFI.passManagerBuilderSetSuperwordLevelParallelismVectorize superwordLevelParallelismVectorize
+        FFI.passManagerBuilderPopulateModulePassManager b pm
+    PassSetSpec ps dl tli tm' -> do
+      let tm = maybe nullPtr (\(TargetMachine tm) -> tm) tm'
+      forM_ ps $ \p -> $(
+        do
+#if __GLASGOW_HASKELL__ < 800
+          TH.TyConI (TH.DataD _ _ _ cons _) <- TH.reify ''Pass
+#else
+          TH.TyConI (TH.DataD _ _ _ _ cons _) <- TH.reify ''Pass
+#endif
+          TH.caseE [| p |] $ flip map cons $ \con -> do
+            let
+              (n, fns) = case con of
+                            TH.RecC n fs -> (n, [ TH.nameBase fn | (fn, _, _) <- fs ])
+                            TH.NormalC n [] -> (n, [])
+              actions = 
+                [ TH.bindS (TH.varP . TH.mkName $ fn) [| encodeM $(TH.dyn fn) |] | fn <- fns ]
+                ++ [
+                 TH.noBindS [|
+                   liftIO $(
+                     foldl1 TH.appE
+                     (map TH.dyn $
+                        ["FFI.add" ++ TH.nameBase n ++ "Pass", "pm"]
+                        ++ ["tm" | FFI.needsTargetMachine (TH.nameBase n)]
+                        ++ fns)
+                    )
+                   |]
+                 ]
+            TH.match (TH.conP n $ map (TH.varP . TH.mkName) fns) (TH.normalB (TH.doE actions)) []
+       )
+  return pm
+
+-- | bracket the creation of a 'PassManager'
+withPassManager :: PassSetSpec -> (PassManager -> IO a) -> IO a
+withPassManager s = bracket (createPassManager s) FFI.disposePassManager . (. PassManager)
+
+-- | run the passes in a 'PassManager' on a 'Module', modifying the 'Module'.
+runPassManager :: PassManager -> Module -> IO Bool
+runPassManager (PassManager p) m = do
+  m' <- readModule m
+  toEnum . fromIntegral <$> FFI.runPassManager p m'
diff --git a/src/LLVM/Internal/RMWOperation.hs b/src/LLVM/Internal/RMWOperation.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/RMWOperation.hs
@@ -0,0 +1,25 @@
+{-# LANGUAGE
+  TemplateHaskell,
+  MultiParamTypeClasses
+  #-}
+module LLVM.Internal.RMWOperation where
+
+import LLVM.AST.RMWOperation
+
+import qualified LLVM.Internal.FFI.LLVMCTypes as FFI
+
+import LLVM.Internal.Coding
+
+genCodingInstance [t| RMWOperation |] ''FFI.RMWOperation [
+  (FFI.rmwOperationXchg, Xchg),
+  (FFI.rmwOperationAdd, Add),
+  (FFI.rmwOperationSub, Sub),
+  (FFI.rmwOperationAnd, And),
+  (FFI.rmwOperationNand, Nand),
+  (FFI.rmwOperationOr, Or),
+  (FFI.rmwOperationXor, Xor),
+  (FFI.rmwOperationMax, Max),
+  (FFI.rmwOperationMin, Min),
+  (FFI.rmwOperationUMax, UMax),
+  (FFI.rmwOperationUMin, UMin)
+ ]
diff --git a/src/LLVM/Internal/RawOStream.hs b/src/LLVM/Internal/RawOStream.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/RawOStream.hs
@@ -0,0 +1,79 @@
+module LLVM.Internal.RawOStream where
+
+import LLVM.Prelude
+
+import Control.Monad.AnyCont
+import Control.Monad.Error.Class
+import Control.Monad.IO.Class
+import Control.Monad.Trans.Except
+
+import Data.IORef
+import Foreign.C
+import Foreign.Ptr
+
+import qualified LLVM.Internal.FFI.RawOStream as FFI
+import qualified LLVM.Internal.FFI.PtrHierarchy as FFI
+
+import LLVM.Internal.Coding
+import LLVM.Internal.Inject
+import LLVM.Internal.String ()
+
+withFileRawOStream ::
+  (Inject String e, MonadError e m, MonadAnyCont IO m, MonadIO m)
+  => String
+  -> Bool
+  -> Bool
+  -> (Ptr FFI.RawOStream -> ExceptT String IO ())
+  -> m ()
+withFileRawOStream path excl text c =
+  withFileRawPWriteStream path excl text (c . FFI.upCast)
+
+withFileRawPWriteStream ::
+  (Inject String e, MonadError e m, MonadAnyCont IO m, MonadIO m)
+  => String
+  -> Bool
+  -> Bool
+  -> (Ptr FFI.RawPWriteStream -> ExceptT String IO ())
+  -> m ()
+withFileRawPWriteStream path excl text c = do
+  path <- encodeM path
+  excl <- encodeM excl
+  text <- encodeM text
+  msgPtr <- alloca
+  errorRef <- liftIO $ newIORef undefined
+  succeeded <- decodeM =<< (liftIO $ FFI.withFileRawPWriteStream path excl text msgPtr $ \os -> do
+                              r <- runExceptT (c os)
+                              writeIORef errorRef r)
+  unless succeeded $ do
+    s <- decodeM msgPtr
+    throwError $ inject (s :: String)
+  e <- liftIO $ readIORef errorRef
+  either (throwError . inject) return e
+
+withBufferRawOStream ::
+  (Inject String e, MonadError e m, MonadIO m, DecodeM IO a (Ptr CChar, CSize))
+  => (Ptr FFI.RawOStream -> ExceptT String IO ())
+  -> m a
+withBufferRawOStream c = withBufferRawPWriteStream (c . FFI.upCast)
+
+withBufferRawPWriteStream ::
+  (Inject String e, MonadError e m, MonadIO m, DecodeM IO a (Ptr CChar, CSize))
+  => (Ptr FFI.RawPWriteStream -> ExceptT String IO ())
+  -> m a
+withBufferRawPWriteStream c = do
+  resultRef <- liftIO $ newIORef Nothing
+  errorRef <- liftIO $ newIORef undefined
+  let saveBuffer :: Ptr CChar -> CSize -> IO ()
+      saveBuffer start size = do
+        r <- decodeM (start, size)
+        writeIORef resultRef (Just r)
+      saveError os = do
+        r <- runExceptT (c os)
+        writeIORef errorRef r
+  liftIO $ FFI.withBufferRawPWriteStream saveBuffer saveError
+  e <- liftIO $ readIORef errorRef
+  case e of
+    Left e -> throwError $ inject e
+    _ -> do
+      Just r <- liftIO $ readIORef resultRef
+      return r
diff --git a/src/LLVM/Internal/String.hs b/src/LLVM/Internal/String.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/String.hs
@@ -0,0 +1,61 @@
+{-# LANGUAGE
+  MultiParamTypeClasses,
+  UndecidableInstances
+  #-}
+module LLVM.Internal.String where
+
+import LLVM.Prelude
+
+import Control.Arrow
+import Control.Monad.AnyCont
+import Control.Monad.IO.Class
+import Control.Exception (finally)
+import Data.Maybe (fromMaybe)
+import Foreign.C (CString, CChar)
+import Foreign.Ptr
+import Foreign.Storable (Storable)
+import Foreign.Marshal.Alloc as F.M (alloca, free)
+
+import LLVM.Internal.FFI.LLVMCTypes
+
+import LLVM.Internal.Coding
+
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Unsafe as BS
+import qualified Data.ByteString.UTF8 as BSUTF8
+
+newtype UTF8ByteString = UTF8ByteString { utf8Bytes :: BS.ByteString }
+
+instance (Monad e) => EncodeM e String UTF8ByteString where
+  encodeM = return . UTF8ByteString . BSUTF8.fromString
+
+instance (Monad d) => DecodeM d String UTF8ByteString where
+  decodeM = return . BSUTF8.toString . utf8Bytes
+
+instance (MonadAnyCont IO e) => EncodeM e String CString where
+  encodeM s = anyContToM (BS.unsafeUseAsCString . utf8Bytes =<< encodeM (s ++ "\0"))
+
+instance (Integral i, MonadAnyCont IO e) => EncodeM e String (Ptr CChar, i) where
+  encodeM s = anyContToM ((. (. second fromIntegral)) $ BS.useAsCStringLen . utf8Bytes =<< encodeM s)
+
+instance (MonadIO d) => DecodeM d String CString where
+  decodeM = decodeM . UTF8ByteString <=< liftIO . BS.packCString
+
+instance (MonadIO d) => DecodeM d String (OwnerTransfered CString) where
+  decodeM (OwnerTransfered s) = liftIO $ finally (decodeM s) (free s)
+
+instance (MonadIO d) => DecodeM d String (Ptr (OwnerTransfered CString)) where
+  decodeM = liftIO . decodeM <=< peek
+
+instance (Integral i, MonadIO d) => DecodeM d String (Ptr CChar, i) where
+  decodeM = decodeM . UTF8ByteString <=< liftIO . BS.packCStringLen . second fromIntegral
+
+instance (Integral i, MonadIO d) => DecodeM d BS.ByteString (Ptr CChar, i) where
+  decodeM = liftIO . BS.packCStringLen . second fromIntegral
+
+instance (Integral i, Storable i, MonadIO d) => DecodeM d String (Ptr i -> IO (Ptr CChar)) where
+  decodeM f = decodeM =<< (liftIO $ F.M.alloca $ \p -> (,) `liftM` f p `ap` peek p)
+
+instance (Monad e, EncodeM e String c) => EncodeM e (Maybe String) (NothingAsEmptyString c) where
+  encodeM = liftM NothingAsEmptyString . encodeM . fromMaybe ""
+  
diff --git a/src/LLVM/Internal/TailCallKind.hs b/src/LLVM/Internal/TailCallKind.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/TailCallKind.hs
@@ -0,0 +1,18 @@
+{-# LANGUAGE
+  TemplateHaskell,
+  MultiParamTypeClasses
+  #-}
+module LLVM.Internal.TailCallKind where
+
+import LLVM.Prelude
+
+import qualified LLVM.Internal.FFI.LLVMCTypes as FFI
+
+import LLVM.Internal.Coding
+import qualified LLVM.AST as A
+
+genCodingInstance [t| Maybe A.TailCallKind |] ''FFI.TailCallKind [
+  (FFI.tailCallKindNone, Nothing),
+  (FFI.tailCallKindTail, Just A.Tail),
+  (FFI.tailCallKindMustTail, Just A.MustTail)
+ ]
diff --git a/src/LLVM/Internal/Target.hs b/src/LLVM/Internal/Target.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/Target.hs
@@ -0,0 +1,314 @@
+{-# LANGUAGE
+  TemplateHaskell,
+  MultiParamTypeClasses,
+  RecordWildCards,
+  UndecidableInstances
+  #-}
+module LLVM.Internal.Target where
+
+import LLVM.Prelude
+
+import Control.Exception
+import Control.Monad.AnyCont
+import Control.Monad.Error.Class
+import Control.Monad.IO.Class
+import Control.Monad.Trans.Except
+
+import Foreign.Ptr
+import Foreign.C.String
+import Data.List (intercalate)
+import Data.Map (Map)
+import qualified Data.Map as Map
+
+import Text.ParserCombinators.Parsec hiding (many)
+
+import LLVM.Internal.Coding
+import LLVM.Internal.String ()
+import LLVM.Internal.LibraryFunction
+import LLVM.DataLayout
+
+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)
+ ]
+
+-- | <http://llvm.org/doxygen/classllvm_1_1Target.html>
+newtype Target = Target (Ptr FFI.Target)
+
+-- | e.g. an instruction set extension
+newtype CPUFeature = CPUFeature String
+  deriving (Eq, Ord, Read, Show)
+
+instance EncodeM e String es => EncodeM e (Map CPUFeature Bool) es where
+  encodeM = encodeM . intercalate "," . map (\(CPUFeature f, enabled) -> (if enabled then "+" else "-") ++ f) . Map.toList
+
+instance (Monad d, DecodeM d String es) => DecodeM d (Map CPUFeature Bool) es where
+  decodeM es = do
+    s <- decodeM es
+    let flag = do
+          en <- choice [char '-' >> return False, char '+' >> return True]
+          s <- many1 (noneOf ",")
+          return (CPUFeature s, en)
+        features = liftM Map.fromList (flag `sepBy` (char ','))
+    case parse (do f <- features; eof; return f) "CPU Feature string" (s :: String) of
+      Right features -> return features
+      Left _ -> fail "failure to parse CPUFeature string"
+                       
+-- | Find a 'Target' given an architecture and/or a \"triple\".
+-- | <http://llvm.org/doxygen/structllvm_1_1TargetRegistry.html#a3105b45e546c9cc3cf78d0f2ec18ad89>
+-- | Be sure to run either 'initializeAllTargets' or 'initializeNativeTarget' before expecting this to succeed, depending on what target(s) you want to use.
+lookupTarget ::
+  Maybe String -- ^ arch
+  -> String -- ^ \"triple\" - e.g. x86_64-unknown-linux-gnu
+  -> ExceptT String IO (Target, String)
+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) $ throwError =<< decodeM cErrorP
+  liftM (Target target, ) $ decodeM cNewTripleP
+
+-- | <http://llvm.org/doxygen/classllvm_1_1TargetOptions.html>
+newtype TargetOptions = TargetOptions (Ptr FFI.TargetOptions)
+
+-- | 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 (TargetOptions cOpts) = do
+  mapM_ (\(c, ha) -> FFI.setTargetOptionFlag cOpts c =<< encodeM (ha hOpts)) [
+    (FFI.targetOptionFlagPrintMachineCode, TO.printMachineCode),
+    (FFI.targetOptionFlagLessPreciseFPMADOption, TO.lessPreciseFloatingPointMultiplyAddOption),
+    (FFI.targetOptionFlagUnsafeFPMath, TO.unsafeFloatingPointMath),
+    (FFI.targetOptionFlagNoInfsFPMath, TO.noInfinitiesFloatingPointMath),
+    (FFI.targetOptionFlagNoNaNsFPMath, TO.noNaNsFloatingPointMath),
+    (FFI.targetOptionFlagHonorSignDependentRoundingFPMathOption, TO.honorSignDependentRoundingFloatingPointMathOption),
+    (FFI.targetOptionFlagNoZerosInBSS, TO.noZerosInBSS),
+    (FFI.targetOptionFlagGuaranteedTailCallOpt, TO.guaranteedTailCallOptimization),
+    (FFI.targetOptionFlagEnableFastISel, TO.enableFastInstructionSelection),
+    (FFI.targetOptionFlagUseInitArray, TO.useInitArray),
+    (FFI.targetOptionFlagDisableIntegratedAS, TO.disableIntegratedAssembler),
+    (FFI.targetOptionFlagCompressDebugSections, TO.compressDebugSections),
+    (FFI.targetOptionFlagTrapUnreachable, TO.trapUnreachable)
+   ]
+  FFI.setStackAlignmentOverride cOpts =<< encodeM (TO.stackAlignmentOverride hOpts)
+  FFI.setFloatABIType cOpts =<< encodeM (TO.floatABIType hOpts)
+  FFI.setAllowFPOpFusion cOpts =<< encodeM (TO.allowFloatingPointOperationFusion hOpts)
+
+-- | get all target options
+peekTargetOptions :: TargetOptions -> IO TO.Options
+peekTargetOptions (TargetOptions tOpts) = do
+  let gof = decodeM <=< FFI.getTargetOptionsFlag tOpts
+  printMachineCode
+    <- gof FFI.targetOptionFlagPrintMachineCode
+  lessPreciseFloatingPointMultiplyAddOption
+    <- gof FFI.targetOptionFlagLessPreciseFPMADOption
+  unsafeFloatingPointMath
+    <- gof FFI.targetOptionFlagUnsafeFPMath
+  noInfinitiesFloatingPointMath
+    <- gof FFI.targetOptionFlagNoInfsFPMath
+  noNaNsFloatingPointMath
+    <- gof FFI.targetOptionFlagNoNaNsFPMath
+  honorSignDependentRoundingFloatingPointMathOption
+    <- gof FFI.targetOptionFlagHonorSignDependentRoundingFPMathOption
+  noZerosInBSS
+    <- gof FFI.targetOptionFlagNoZerosInBSS
+  guaranteedTailCallOptimization
+    <- gof FFI.targetOptionFlagGuaranteedTailCallOpt
+  enableFastInstructionSelection
+    <- gof FFI.targetOptionFlagEnableFastISel
+  useInitArray
+    <- gof FFI.targetOptionFlagUseInitArray
+  disableIntegratedAssembler
+    <- gof FFI.targetOptionFlagDisableIntegratedAS
+  compressDebugSections
+    <- gof FFI.targetOptionFlagCompressDebugSections
+  trapUnreachable
+    <- gof FFI.targetOptionFlagTrapUnreachable
+  stackAlignmentOverride <- decodeM =<< FFI.getStackAlignmentOverride tOpts
+  floatABIType <- decodeM =<< FFI.getFloatABIType tOpts
+  allowFloatingPointOperationFusion <- decodeM =<< FFI.getAllowFPOpFusion tOpts
+  return TO.Options { .. }
+
+-- | <http://llvm.org/doxygen/classllvm_1_1TargetMachine.html>
+newtype TargetMachine = TargetMachine (Ptr FFI.TargetMachine)
+
+-- | bracket creation and destruction of a 'TargetMachine'
+withTargetMachine ::
+    Target
+    -> String -- ^ triple
+    -> String -- ^ 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)
+
+-- | <http://llvm.org/doxygen/classllvm_1_1TargetLowering.html>
+newtype TargetLowering = TargetLowering (Ptr FFI.TargetLowering)
+
+-- | get the 'TargetLowering' of a 'TargetMachine'
+getTargetLowering :: TargetMachine -> IO TargetLowering
+getTargetLowering (TargetMachine tm) = 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 String
+getTargetMachineTriple (TargetMachine m) = decodeM =<< FFI.getTargetMachineTriple m
+
+-- | the default target triple that LLVM has been configured to produce code for
+getDefaultTargetTriple :: IO String
+getDefaultTargetTriple = decodeM =<< FFI.getDefaultTargetTriple
+
+-- | a target triple suitable for loading code into the current process
+getProcessTargetTriple :: IO String
+getProcessTargetTriple = decodeM =<< FFI.getProcessTargetTriple
+
+-- | the LLVM name for the host CPU
+getHostCPUName :: IO String
+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) -> ExceptT String IO a
+withHostTargetMachine f = do
+  liftIO $ initializeAllTargets
+  triple <- liftIO $ getProcessTargetTriple
+  cpu <- liftIO $ getHostCPUName
+  features <- liftIO $ getHostCPUFeatures
+  (target, _) <- lookupTarget Nothing triple
+  liftIO $ withTargetOptions $ \options ->
+      withTargetMachine target triple cpu features options Reloc.Default CodeModel.Default CodeGenOpt.Default f
+
+-- | <http://llvm.org/docs/doxygen/html/classllvm_1_1TargetLibraryInfo.html>
+newtype TargetLibraryInfo = TargetLibraryInfo (Ptr FFI.TargetLibraryInfo)
+
+-- | Look up a 'LibraryFunction' by its standard name
+getLibraryFunction :: TargetLibraryInfo -> String -> 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 String
+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
+  -> String -- ^ 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 ::
+  String -- ^ 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)
diff --git a/src/LLVM/Internal/Threading.hs b/src/LLVM/Internal/Threading.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/Threading.hs
@@ -0,0 +1,13 @@
+module LLVM.Internal.Threading (
+  isMultithreaded
+  ) where
+
+import LLVM.Prelude
+
+import qualified LLVM.Internal.FFI.Threading as FFI
+
+import LLVM.Internal.Coding
+
+-- | Check if multithreading is enabled in LLVM
+isMultithreaded :: IO Bool
+isMultithreaded = FFI.isMultithreaded >>= decodeM
diff --git a/src/LLVM/Internal/Type.hs b/src/LLVM/Internal/Type.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/Type.hs
@@ -0,0 +1,164 @@
+{-# LANGUAGE
+  QuasiQuotes,
+  MultiParamTypeClasses
+  #-}
+module LLVM.Internal.Type where
+
+import LLVM.Prelude
+
+import Control.Monad.AnyCont
+import Control.Monad.Error.Class
+import Control.Monad.State
+
+import qualified Data.Set as Set
+
+import Foreign.Ptr
+
+import qualified LLVM.Internal.FFI.LLVMCTypes as FFI
+import LLVM.Internal.FFI.LLVMCTypes (typeKindP)
+import qualified LLVM.Internal.FFI.Type as FFI
+import qualified LLVM.Internal.FFI.PtrHierarchy as FFI
+
+import qualified LLVM.AST as A
+import qualified LLVM.AST.AddrSpace as A
+
+import LLVM.Internal.Context
+import LLVM.Internal.Coding
+import LLVM.Internal.DecodeAST
+import LLVM.Internal.EncodeAST
+
+getStructure :: Ptr FFI.Type -> DecodeAST A.Type
+getStructure t = scopeAnyCont $ do
+  return A.StructureType
+   `ap` (decodeM =<< liftIO (FFI.isPackedStruct t))
+   `ap` do
+       n <- liftIO (FFI.countStructElementTypes t)
+       ts <- allocaArray n
+       liftIO $ FFI.getStructElementTypes t ts
+       decodeM (n, ts)
+
+getStructDefinitions :: DecodeAST [A.Definition]
+getStructDefinitions = do
+  let getStructDefinition t = do
+       opaque <- decodeM =<< liftIO (FFI.structIsOpaque t)
+       if opaque then return Nothing else Just <$> getStructure t
+  flip fix Set.empty $ \continue done -> do
+    t <- takeTypeToDefine
+    flip (maybe (return [])) t $ \t -> do
+      if t `Set.member` done
+        then
+          continue done
+        else
+          return (:)
+            `ap` (return A.TypeDefinition `ap` getTypeName t `ap` getStructDefinition t)
+            `ap` (continue $ Set.insert t done)
+
+isArrayType :: Ptr FFI.Type -> IO Bool
+isArrayType t = do
+  k <- liftIO $ FFI.getTypeKind t
+  return $ k == FFI.typeKindArray
+
+instance Monad m => EncodeM m A.AddrSpace FFI.AddrSpace where
+  encodeM (A.AddrSpace a) = return FFI.AddrSpace `ap` encodeM a
+
+instance Monad m => DecodeM m A.AddrSpace FFI.AddrSpace where
+  decodeM (FFI.AddrSpace a) = return A.AddrSpace `ap` decodeM a
+
+instance EncodeM EncodeAST A.Type (Ptr FFI.Type) where
+  encodeM f = scopeAnyCont $ do
+    Context context <- gets encodeStateContext
+    case f of
+      A.IntegerType bits -> do
+        bits <- encodeM bits
+        liftIO $ FFI.intTypeInContext context bits
+      A.FunctionType returnTypeAST argTypeASTs isVarArg -> do
+        returnType <- encodeM returnTypeAST
+        argTypes <- encodeM argTypeASTs
+        isVarArg <- encodeM isVarArg
+        liftIO $ FFI.functionType returnType argTypes isVarArg
+      A.PointerType elementType addressSpace -> do
+        e <- encodeM elementType
+        a <- encodeM addressSpace
+        liftIO $ FFI.pointerType e a
+      A.VoidType -> liftIO $ FFI.voidTypeInContext context
+      A.FloatingPointType 16 A.IEEE -> liftIO $ FFI.halfTypeInContext context
+      A.FloatingPointType 32 A.IEEE -> liftIO $ FFI.floatTypeInContext context
+      A.FloatingPointType 64 A.IEEE -> liftIO $ FFI.doubleTypeInContext context
+      A.FloatingPointType 80 A.DoubleExtended -> liftIO $ FFI.x86FP80TypeInContext context
+      A.FloatingPointType 128 A.IEEE -> liftIO $ FFI.fP128TypeInContext context
+      A.FloatingPointType 128 A.PairOfFloats -> liftIO $ FFI.ppcFP128TypeInContext context
+      A.FloatingPointType _ _ -> throwError $ "unsupported floating point type: " ++ show f
+      A.VectorType sz e -> do
+        e <- encodeM e
+        sz <- encodeM sz
+        liftIO $ FFI.vectorType e sz
+      A.ArrayType sz e -> do
+        e <- encodeM e
+        sz <- encodeM sz
+        liftIO $ FFI.arrayType e sz
+      A.StructureType packed ets -> do
+        ets <- encodeM ets
+        packed <- encodeM packed
+        liftIO $ FFI.structTypeInContext context ets packed
+      A.NamedTypeReference n -> lookupNamedType n
+      A.MetadataType -> liftIO $ FFI.metadataTypeInContext context
+      A.TokenType -> liftIO $ FFI.tokenTypeInContext context
+
+instance DecodeM DecodeAST A.Type (Ptr FFI.Type) where
+  decodeM t = scopeAnyCont $ do
+    k <- liftIO $ FFI.getTypeKind t
+    case k of
+      [typeKindP|Void|] -> return A.VoidType
+      [typeKindP|Integer|] -> A.IntegerType <$> (decodeM =<< liftIO (FFI.getIntTypeWidth t))
+      [typeKindP|Function|] ->
+          return A.FunctionType
+               `ap` (decodeM =<< liftIO (FFI.getReturnType t))
+               `ap` (do
+                      n <- liftIO (FFI.countParamTypes t)
+                      ts <- allocaArray n
+                      liftIO $ FFI.getParamTypes t ts
+                      decodeM (n, ts)
+                   )
+               `ap` (decodeM =<< liftIO (FFI.isFunctionVarArg t))
+      [typeKindP|Pointer|] ->
+          return A.PointerType
+             `ap` (decodeM =<< liftIO (FFI.getElementType t))
+             `ap` (decodeM =<< liftIO (FFI.getPointerAddressSpace t))
+      [typeKindP|Half|] -> return $ A.FloatingPointType 16 A.IEEE
+      [typeKindP|Float|] -> return $ A.FloatingPointType 32 A.IEEE
+      [typeKindP|Double|] -> return $ A.FloatingPointType 64 A.IEEE
+      [typeKindP|FP128|] -> return $ A.FloatingPointType 128 A.IEEE
+      [typeKindP|X86_FP80|] -> return $ A.FloatingPointType 80 A.DoubleExtended
+      [typeKindP|PPC_FP128|] -> return $ A.FloatingPointType 128 A.PairOfFloats
+      [typeKindP|Vector|] ->
+        return A.VectorType
+         `ap` (decodeM =<< liftIO (FFI.getVectorSize t))
+         `ap` (decodeM =<< liftIO (FFI.getElementType t))
+      [typeKindP|Struct|] -> do
+        let ifM c a b = c >>= \x -> if x then a else b
+        ifM (decodeM =<< liftIO (FFI.structIsLiteral t))
+            (getStructure t)
+            (saveNamedType t >> return A.NamedTypeReference `ap` getTypeName t)
+      [typeKindP|Metadata|] -> return A.MetadataType
+      [typeKindP|Array|] ->
+        return A.ArrayType
+         `ap` (decodeM =<< liftIO (FFI.getArrayLength t))
+         `ap` (decodeM =<< liftIO (FFI.getElementType t))
+      [typeKindP|Token|] -> return A.TokenType
+      _ -> error $ "unhandled type kind " ++ show k
+
+createNamedType :: A.Name -> EncodeAST (Ptr FFI.Type)
+createNamedType n = do
+  Context c <- gets encodeStateContext
+  n <- case n of { A.Name n -> encodeM n; _ -> return nullPtr }
+  liftIO $ FFI.structCreateNamed c n
+
+
+setNamedType :: Ptr FFI.Type -> A.Type -> EncodeAST ()
+setNamedType t (A.StructureType packed ets) = do
+  ets <- encodeM ets
+  packed <- encodeM packed
+  liftIO $ FFI.structSetBody t ets packed
+
+
+
diff --git a/src/LLVM/Internal/Value.hs b/src/LLVM/Internal/Value.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Internal/Value.hs
@@ -0,0 +1,24 @@
+{-# LANGUAGE
+  MultiParamTypeClasses
+  #-}
+module LLVM.Internal.Value where
+
+import LLVM.Prelude
+
+import Control.Monad.State
+
+import Foreign.Ptr
+
+import qualified LLVM.Internal.FFI.PtrHierarchy as FFI
+import qualified LLVM.Internal.FFI.Value as FFI
+
+import LLVM.Internal.Coding
+import LLVM.Internal.DecodeAST
+import LLVM.Internal.Type () 
+import LLVM.Internal.Constant () 
+
+import qualified LLVM.AST.Type as A
+
+typeOf :: FFI.DescendentOf FFI.Value v => Ptr v -> DecodeAST A.Type
+typeOf = decodeM <=< liftIO . FFI.typeOf . FFI.upCast
+
diff --git a/src/LLVM/Module.hs b/src/LLVM/Module.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Module.hs
@@ -0,0 +1,27 @@
+-- | A 'Module' holds a C++ LLVM IR module. 'Module's may be converted to or from strings or Haskell ASTs, or
+-- added to an 'LLVM.ExecutionEngine.ExecutionEngine' and so JIT compiled to get function pointers.
+module LLVM.Module (
+    Module,
+    File(..),
+
+    withModuleFromAST,
+    moduleAST,
+
+    withModuleFromLLVMAssembly,
+    moduleLLVMAssembly,
+    writeLLVMAssemblyToFile,
+
+    withModuleFromBitcode,
+    moduleBitcode,
+    writeBitcodeToFile,
+
+    moduleTargetAssembly,
+    writeTargetAssemblyToFile,
+
+    moduleObject,
+    writeObjectToFile,
+
+    linkModules
+  ) where
+
+import LLVM.Internal.Module
diff --git a/src/LLVM/OrcJIT.hs b/src/LLVM/OrcJIT.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/OrcJIT.hs
@@ -0,0 +1,11 @@
+module LLVM.OrcJIT (
+    JITSymbol(..),
+    JITSymbolFlags(..),
+    MangledSymbol,
+    ObjectLinkingLayer,
+    SymbolResolver(..),
+    SymbolResolverFn,
+    withObjectLinkingLayer
+  ) where
+
+import LLVM.Internal.OrcJIT
diff --git a/src/LLVM/OrcJIT/CompileOnDemandLayer.hs b/src/LLVM/OrcJIT/CompileOnDemandLayer.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/OrcJIT/CompileOnDemandLayer.hs
@@ -0,0 +1,14 @@
+module LLVM.OrcJIT.CompileOnDemandLayer (
+    PartitioningFn,
+    JITCompileCallbackManager,
+    IndirectStubsManagerBuilder,
+    CompileOnDemandLayer,
+    withIndirectStubsManagerBuilder,
+    withJITCompileCallbackManager,
+    withCompileOnDemandLayer,
+    mangleSymbol,
+    findSymbol,
+    withModuleSet
+  ) where
+
+import LLVM.Internal.OrcJIT.CompileOnDemandLayer
diff --git a/src/LLVM/OrcJIT/IRCompileLayer.hs b/src/LLVM/OrcJIT/IRCompileLayer.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/OrcJIT/IRCompileLayer.hs
@@ -0,0 +1,10 @@
+module LLVM.OrcJIT.IRCompileLayer (
+    IRCompileLayer,
+    ModuleSet,
+    findSymbol,
+    mangleSymbol,
+    withIRCompileLayer,
+    withModuleSet
+  ) where
+
+import           LLVM.Internal.OrcJIT.IRCompileLayer
diff --git a/src/LLVM/PassManager.hs b/src/LLVM/PassManager.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/PassManager.hs
@@ -0,0 +1,14 @@
+-- | A 'PassManager' holds collection of passes, to be run on 'Module's.
+-- Build one with 'withPassManager':
+-- 
+--  * using 'CuratedPassSetSpec' if you want optimization but not to play with your compiler
+--
+--  * using 'PassSetSpec' if you do want to play with your compiler
+module LLVM.PassManager (
+  PassManager,
+  PassSetSpec(..), defaultPassSetSpec, defaultCuratedPassSetSpec,
+  withPassManager,
+  runPassManager
+  ) where
+
+import LLVM.Internal.PassManager
diff --git a/src/LLVM/Relocation.hs b/src/LLVM/Relocation.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Relocation.hs
@@ -0,0 +1,12 @@
+-- | Relocations, used in specifying TargetMachine
+module LLVM.Relocation where
+
+import LLVM.Prelude
+
+-- | <http://llvm.org/doxygen/namespacellvm_1_1Reloc.html>
+data Model 
+    = Default
+    | Static
+    | PIC
+    | DynamicNoPIC
+    deriving (Eq, Read, Show, Typeable, Data)
diff --git a/src/LLVM/Target.hs b/src/LLVM/Target.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Target.hs
@@ -0,0 +1,22 @@
+-- | A 'Target' describes that for which code may be intended. Targets are used both during actual
+-- | lowering of LLVM IR to machine code and by some optimization passes which use the target to
+-- | judge costs.
+module LLVM.Target (
+   lookupTarget,
+   TargetOptions,
+   Target, TargetMachine, TargetLowering,
+   CPUFeature(..),
+   withTargetOptions, peekTargetOptions, pokeTargetOptions,
+   withTargetMachine, withHostTargetMachine,
+   getTargetLowering,
+   getTargetMachineTriple, getDefaultTargetTriple, getProcessTargetTriple, getHostCPUName, getHostCPUFeatures,
+   getTargetMachineDataLayout, initializeNativeTarget, initializeAllTargets,
+   TargetLibraryInfo,
+   getLibraryFunction,
+   getLibraryFunctionName,
+   setLibraryFunctionAvailableWithName,
+   withTargetLibraryInfo
+ ) where
+
+import LLVM.Internal.Target
+
diff --git a/src/LLVM/Target/LibraryFunction.hs b/src/LLVM/Target/LibraryFunction.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Target/LibraryFunction.hs
@@ -0,0 +1,7 @@
+-- | A 'LibraryFunction' identifies a function of which LLVM has particular knowledge.
+module LLVM.Target.LibraryFunction (
+  LibraryFunction(..)
+ ) where
+
+
+import LLVM.Internal.LibraryFunction
diff --git a/src/LLVM/Target/Options.hs b/src/LLVM/Target/Options.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Target/Options.hs
@@ -0,0 +1,41 @@
+-- | <http://llvm.org/doxygen/classllvm_1_1TargetOptions.html>
+module LLVM.Target.Options where
+
+import LLVM.Prelude
+
+-- | <http://llvm.org/doxygen/namespacellvm_1_1FloatABI.html#aea077c52d84934aabf9445cef9eab2e2>
+data FloatABI
+  = FloatABIDefault
+  | FloatABISoft
+  | FloatABIHard
+  deriving (Eq, Ord, Read, Show, Enum, Bounded, Typeable, Data)
+
+-- | <http://llvm.org/doxygen/namespacellvm_1_1FPOpFusion.html#a9c71bae9f02af273833fde586d529fc5>
+data FloatingPointOperationFusionMode
+  = FloatingPointOperationFusionFast
+  | FloatingPointOperationFusionStandard
+  | FloatingPointOperationFusionStrict
+  deriving (Eq, Ord, Read, Show, Enum, Bounded, Typeable, Data)
+
+-- | The options of a 'LLVM.Target.TargetOptions'
+-- <http://llvm.org/doxygen/classllvm_1_1TargetOptions.html>
+data Options = Options {
+  printMachineCode :: Bool,
+  lessPreciseFloatingPointMultiplyAddOption :: Bool,
+  unsafeFloatingPointMath :: Bool,
+  noInfinitiesFloatingPointMath :: Bool,
+  noNaNsFloatingPointMath :: Bool,
+  honorSignDependentRoundingFloatingPointMathOption :: Bool,
+  noZerosInBSS :: Bool,
+  guaranteedTailCallOptimization :: Bool,
+  enableFastInstructionSelection :: Bool,
+  useInitArray :: Bool,
+  disableIntegratedAssembler :: Bool,
+  compressDebugSections :: Bool,
+  trapUnreachable :: Bool,
+  stackAlignmentOverride :: Word32,
+  floatABIType :: FloatABI,
+  allowFloatingPointOperationFusion :: FloatingPointOperationFusionMode
+  }
+  deriving (Eq, Ord, Read, Show)
+
diff --git a/src/LLVM/Threading.hs b/src/LLVM/Threading.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Threading.hs
@@ -0,0 +1,24 @@
+-- | functionality necessary when running LLVM in multiple threads at the same time.
+module LLVM.Threading (
+  setMultithreaded,
+  isMultithreaded
+  ) where
+
+import LLVM.Prelude
+
+import LLVM.Internal.Threading
+
+{-# DEPRECATED setMultithreaded "LLVM no longer features runtime control of multithreading support" #-}
+-- | This function used set the multithreading mode of LLVM, but that feature no longer exists. Threading is
+-- controlled only at runtime with the configure flag --enable-threads (default is YES). This function will
+-- now check that the the compiled-in multithreading support (returned by 'isMultithreaded') is
+-- sufficient to support the requested access, and fail if not, so as to prevent uncontrolled use of a
+-- version of LLVM compiled to be capable only of singled threaded use by haskell code requesting
+-- multithreading support.
+setMultithreaded :: Bool -> IO ()
+setMultithreaded desired = do
+  actual <- isMultithreaded
+  when (desired && not actual) $
+     fail $ "Multithreading support requested but not available. " ++ 
+            "Please use an LLVM built with threading enabled"
+  return ()
diff --git a/src/LLVM/Transforms.hs b/src/LLVM/Transforms.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Transforms.hs
@@ -0,0 +1,197 @@
+-- | This module provides an enumeration of the various transformation (e.g. optimization) passes
+-- provided by LLVM. They can be used to create a 'LLVM.PassManager.PassManager' to, in turn,
+-- run the passes on 'LLVM.Module.Module's. If you don't know what passes you want, consider
+-- instead using 'LLVM.PassManager.CuratedPassSetSpec'.
+module LLVM.Transforms where
+
+import LLVM.Prelude
+
+-- | <http://llvm.org/docs/Passes.html#transform-passes>
+-- A few passes can make use of information in a 'LLVM.Target.TargetMachine' if one
+-- is provided to 'LLVM.PassManager.createPassManager'.
+-- <http://llvm.org/doxygen/classllvm_1_1Pass.html>
+data Pass
+  -- here begin the Scalar passes
+  = AggressiveDeadCodeElimination
+  | BreakCriticalEdges
+  -- | can use a 'LLVM.Target.TargetMachine'
+  | CodeGenPrepare
+  | ConstantPropagation
+  | CorrelatedValuePropagation
+  | DeadCodeElimination
+  | DeadInstructionElimination
+  | DeadStoreElimination
+  | DemoteRegisterToMemory
+  | EarlyCommonSubexpressionElimination
+  | GlobalValueNumbering { noLoads :: Bool }
+  | InductionVariableSimplify
+  | InstructionCombining
+  | JumpThreading
+  | LoopClosedSingleStaticAssignment
+  | LoopInvariantCodeMotion
+  | LoopDeletion
+  | LoopIdiom
+  | LoopInstructionSimplify
+  | LoopRotate
+  | LoopStrengthReduce
+  | LoopUnroll { loopUnrollThreshold :: Maybe Word, count :: Maybe Word, allowPartial :: Maybe Bool }
+  | LoopUnswitch { optimizeForSize :: Bool }
+  | LowerAtomic
+  | LowerInvoke
+  | LowerSwitch
+  | LowerExpectIntrinsic
+  | MemcpyOptimization
+  | PromoteMemoryToRegister
+  | Reassociate
+  | ScalarReplacementOfAggregates { requiresDominatorTree :: Bool }
+  | OldScalarReplacementOfAggregates { 
+      oldScalarReplacementOfAggregatesThreshold :: Maybe Word, 
+      useDominatorTree :: Bool, 
+      structMemberThreshold :: Maybe Word,
+      arrayElementThreshold :: Maybe Word,
+      scalarLoadThreshold :: Maybe Word
+    }
+  | SparseConditionalConstantPropagation
+  | SimplifyLibCalls
+  | SimplifyControlFlowGraph
+  | Sinking
+  | TailCallElimination
+
+  -- here begin the Interprocedural passes
+  | AlwaysInline { insertLifetime :: Bool }
+  | ArgumentPromotion
+  | ConstantMerge
+  | FunctionAttributes
+  | FunctionInlining { 
+      functionInliningThreshold :: Word
+    }
+  | GlobalDeadCodeElimination
+  | InternalizeFunctions { exportList :: [String] }
+  | InterproceduralConstantPropagation
+  | InterproceduralSparseConditionalConstantPropagation
+  | MergeFunctions
+  | PartialInlining
+  | PruneExceptionHandling
+  | StripDeadDebugInfo
+  | StripDebugDeclare
+  | StripNonDebugSymbols
+  | StripSymbols { onlyDebugInfo :: Bool }
+
+  -- here begin the vectorization passes
+  | BasicBlockVectorize { 
+      vectorBits :: Word,
+      vectorizeBools :: Bool,
+      vectorizeInts :: Bool,
+      vectorizeFloats :: Bool,
+      vectorizePointers :: Bool,
+      vectorizeCasts :: Bool,
+      vectorizeMath :: Bool,
+      vectorizeFusedMultiplyAdd :: Bool,
+      vectorizeSelect :: Bool,
+      vectorizeCmp :: Bool,
+      vectorizeGetElementPtr :: Bool,
+      vectorizeMemoryOperations :: Bool,
+      alignedOnly :: Bool,
+      requiredChainDepth :: Word,
+      searchLimit :: Word,
+      maxCandidatePairsForCycleCheck :: Word,
+      splatBreaksChain :: Bool,
+      maxInstructions :: Word,
+      maxIterations :: Word,
+      powerOfTwoLengthsOnly :: Bool,
+      noMemoryOperationBoost :: Bool,
+      fastDependencyAnalysis :: Bool
+    }
+  | LoopVectorize {
+      noUnrolling :: Bool,
+      alwaysVectorize :: Bool
+    }
+  | SuperwordLevelParallelismVectorize
+
+  -- here begin the instrumentation passes
+  | GCOVProfiler {
+      emitNotes :: Bool,
+      emitData :: Bool,
+      version :: GCOVVersion, 
+      useCfgChecksum :: Bool,
+      noRedZone :: Bool,
+      functionNamesInData :: Bool
+    }
+  | AddressSanitizer
+  | AddressSanitizerModule
+  | MemorySanitizer {
+      trackOrigins :: Bool
+    }
+  | ThreadSanitizer
+  | BoundsChecking
+  deriving (Eq, Ord, Read, Show, Typeable, Data)
+
+-- | Defaults for the 'LoopVectorize' pass
+defaultLoopVectorize :: Pass
+defaultLoopVectorize = LoopVectorize {
+    noUnrolling = False,
+    alwaysVectorize = True
+  }
+
+-- | Defaults for the 'BasicBlockVectorize' pass - copied from the C++ code to keep these defaults
+-- constant. (The C++ defaults are modifiable through global objects used for command-line processing,
+-- in a design apparently oblivious to uses of LLVM besides the standard command-line tools).
+defaultVectorizeBasicBlocks :: Pass
+defaultVectorizeBasicBlocks = BasicBlockVectorize {
+    vectorBits = 128,
+    vectorizeBools = True,
+    vectorizeInts = True,
+    vectorizeFloats = True,
+    vectorizePointers = True,
+    vectorizeCasts = True,
+    vectorizeMath = True,
+    vectorizeFusedMultiplyAdd = True,
+    vectorizeSelect = True,
+    vectorizeCmp = True,
+    vectorizeGetElementPtr = True,
+    vectorizeMemoryOperations = True,
+    alignedOnly = True,
+
+    requiredChainDepth = 6,
+    searchLimit = 400,
+    maxCandidatePairsForCycleCheck = 200,
+    splatBreaksChain = False,
+    maxInstructions = 500,
+    maxIterations = 0,
+    powerOfTwoLengthsOnly = False,
+    noMemoryOperationBoost = False,
+    fastDependencyAnalysis = False
+  }
+
+-- | See <http://gcc.gnu.org/viewcvs/gcc/trunk/gcc/gcov-io.h?view=markup>.
+newtype GCOVVersion = GCOVVersion String
+  deriving (Eq, Ord, Read, Show, Typeable, Data)
+
+-- | Defaults for 'GCOVProfiler'.
+defaultGCOVProfiler :: Pass
+defaultGCOVProfiler = GCOVProfiler {
+    emitNotes = True,
+    emitData = True,
+    version = GCOVVersion "402*", 
+    useCfgChecksum = False,
+    noRedZone = False,
+    functionNamesInData = True
+  }
+
+-- | Defaults for 'AddressSanitizer'.
+defaultAddressSanitizer :: Pass
+defaultAddressSanitizer = AddressSanitizer
+
+-- | Defaults for 'AddressSanitizerModule'.
+defaultAddressSanitizerModule :: Pass
+defaultAddressSanitizerModule = AddressSanitizerModule
+
+-- | Defaults for 'MemorySanitizer'.
+defaultMemorySanitizer :: Pass
+defaultMemorySanitizer = MemorySanitizer {
+  trackOrigins = False
+}
+
+-- | Defaults for 'ThreadSanitizer'.
+defaultThreadSanitizer :: Pass
+defaultThreadSanitizer = ThreadSanitizer
diff --git a/test/LLVM/Test/Analysis.hs b/test/LLVM/Test/Analysis.hs
new file mode 100644
--- /dev/null
+++ b/test/LLVM/Test/Analysis.hs
@@ -0,0 +1,113 @@
+module LLVM.Test.Analysis where
+
+import Test.Tasty
+import Test.Tasty.HUnit
+
+import LLVM.Test.Support
+
+import Control.Monad.Trans.Except
+
+import LLVM.Module
+import LLVM.Context
+import LLVM.Analysis
+
+import LLVM.AST as A
+import LLVM.AST.Type as A.T
+import LLVM.AST.Name
+import LLVM.AST.AddrSpace
+import LLVM.AST.DataLayout
+import qualified LLVM.AST.IntegerPredicate as IPred
+import qualified LLVM.AST.Linkage as L
+import qualified LLVM.AST.Visibility as V
+import qualified LLVM.AST.CallingConvention as CC
+import qualified LLVM.AST.Attribute as A
+import qualified LLVM.AST.Global as G
+import qualified LLVM.AST.Constant as C
+
+import qualified LLVM.Relocation as R
+import qualified LLVM.CodeModel as CM
+import qualified LLVM.CodeGenOpt as CGO
+
+tests = testGroup "Analysis" [
+  testGroup "Verifier" [
+{-
+    -- this test will cause an assertion if LLVM is compiled with assertions on.
+    testCase "Module" $ do
+      let ast = Module "<string>" Nothing Nothing [
+            GlobalDefinition $ Function L.External V.Default CC.C [] A.T.void (Name "foo") ([
+                Parameter i32 (Name "x") []
+               ],False)
+             [] 
+             Nothing 0 Nothing         
+             [
+              BasicBlock (UnName 0) [
+                UnName 1 := Call {
+                  isTailCall = False,
+                  callingConvention = CC.C,
+                  returnAttributes = [],
+                  function = Right (ConstantOperand (C.GlobalReference (A.T.FunctionType A.T.void [A.T.i32] False) (Name "foo"))),
+                  arguments = [
+                   (ConstantOperand (C.Int 8 1), [])
+                  ],
+                  functionAttributes = [],
+                  metadata = []
+                 }
+              ] (
+                Do $ Ret Nothing []
+              )
+             ]
+            ]
+      Left s <- withContext $ \context -> withModuleFromAST' context ast $ runExceptT . verify
+      s @?= "Call parameter type does not match function signature!\n\
+            \i8 1\n\
+            \ i32  call void @foo(i8 1)\n\
+            \Broken module found, compilation terminated.\n\
+            \Broken module found, compilation terminated.\n",
+-}
+
+    testGroup "regression" [
+      testCase "load synchronization" $ do
+       let str = "; ModuleID = '<string>'\n\
+                 \source_filename = \"<string>\"\n\
+                 \\n\
+                 \define double @my_function2(double* %input_0) {\n\
+                 \foo:\n\
+                 \  %tmp_input_w0 = getelementptr inbounds double, double* %input_0, i64 0\n\
+                 \  %0 = load double, double* %tmp_input_w0, align 8\n\
+                 \  ret double %0\n\
+                 \}\n"
+           ast = 
+             Module "<string>" "<string>" Nothing Nothing [
+               GlobalDefinition $ functionDefaults {
+                 G.returnType = double,
+                 G.name = Name "my_function2",
+                 G.parameters = ([
+                   Parameter (ptr double) (Name "input_0") []
+                  ],False),
+                 G.basicBlocks = [
+                   BasicBlock (Name "foo") [ 
+                    Name "tmp_input_w0" := GetElementPtr {
+                      inBounds = True,
+                      address = LocalReference (ptr double) (Name "input_0"),
+                      indices = [ConstantOperand (C.Int 64 0)],
+                      metadata = []
+                    },
+                    UnName 0 := Load {
+                      volatile = False,
+                      address = LocalReference (ptr double) (Name "tmp_input_w0"),
+                      maybeAtomicity = Nothing,
+                      alignment = 8,
+                      metadata = []
+                    }
+                   ] (
+                     Do $ Ret (Just (LocalReference double (UnName 0))) []
+                   )
+                  ]
+                }
+              ]
+       strCheck ast str
+       s <- withContext $ \context -> withModuleFromAST' context ast $ runExceptT . verify
+       s @?= Right ()
+     ]
+   ]
+ ]
diff --git a/test/LLVM/Test/CallingConvention.hs b/test/LLVM/Test/CallingConvention.hs
new file mode 100644
--- /dev/null
+++ b/test/LLVM/Test/CallingConvention.hs
@@ -0,0 +1,57 @@
+module LLVM.Test.CallingConvention where
+
+import Test.Tasty
+import Test.Tasty.HUnit
+
+import LLVM.Test.Support
+
+import Data.Maybe
+import qualified Data.Set as Set
+import qualified Data.Map as Map
+
+import LLVM.Context
+import LLVM.Module
+import LLVM.AST
+import LLVM.AST.Type as T
+import qualified LLVM.AST.CallingConvention as CC
+import qualified LLVM.AST.Global as G
+
+tests = testGroup "CallingConvention" [
+  testCase name $ strCheck (defaultModule {
+    moduleDefinitions = [
+     GlobalDefinition $ functionDefaults {
+       G.returnType = i32,
+       G.name = Name "foo",
+       G.callingConvention = cc
+     }
+    ]
+   }) ("; ModuleID = '<string>'\n\
+       \source_filename = \"<string>\"\n\
+       \\n\
+       \declare" ++ (if name == "" then "" else (" " ++ name)) ++ " i32 @foo()\n")
+   | (name, cc) <- [
+   ("", CC.C),
+   ("fastcc", CC.Fast),
+   ("coldcc", CC.Cold),
+   ("ghccc", CC.GHC),
+   ("cc11", CC.HiPE),
+   ("webkit_jscc", CC.WebKit_JS),
+   ("anyregcc", CC.AnyReg),
+   ("preserve_mostcc", CC.PreserveMost),
+   ("preserve_allcc", CC.PreserveAll),
+   ("x86_stdcallcc", CC.X86_StdCall),
+   ("x86_fastcallcc", CC.X86_FastCall),
+   ("arm_apcscc", CC.ARM_APCS),
+   ("arm_aapcscc", CC.ARM_AAPCS),
+   ("arm_aapcs_vfpcc", CC.ARM_AAPCS_VFP),
+   ("msp430_intrcc", CC.MSP430_INTR),
+   ("x86_thiscallcc", CC.X86_ThisCall),
+   ("ptx_kernel", CC.PTX_Kernel),
+   ("ptx_device", CC.PTX_Device),
+   ("spir_func", CC.SPIR_FUNC),
+   ("spir_kernel", CC.SPIR_KERNEL),
+   ("intel_ocl_bicc", CC.Intel_OCL_BI),
+   ("x86_64_sysvcc", CC.X86_64_SysV),
+   ("x86_64_win64cc", CC.X86_64_Win64)
+  ]
+ ]
diff --git a/test/LLVM/Test/Constants.hs b/test/LLVM/Test/Constants.hs
new file mode 100644
--- /dev/null
+++ b/test/LLVM/Test/Constants.hs
@@ -0,0 +1,184 @@
+module LLVM.Test.Constants where
+
+import Test.Tasty
+import Test.Tasty.HUnit
+
+import LLVM.Test.Support
+
+import Control.Monad
+import Data.Functor
+import Data.Maybe
+import Foreign.Ptr
+import Data.Word
+
+import LLVM.Context
+import LLVM.Module
+import LLVM.Diagnostic
+import LLVM.AST
+import LLVM.AST.Type
+import LLVM.AST.Name
+import LLVM.AST.AddrSpace
+import qualified LLVM.AST.Linkage as L
+import qualified LLVM.AST.Visibility as V
+import qualified LLVM.AST.CallingConvention as CC
+import qualified LLVM.AST.Attribute as A
+import qualified LLVM.AST.Global as G
+import qualified LLVM.AST.Constant as C
+import qualified LLVM.AST.Float as F
+import qualified LLVM.AST.IntegerPredicate as IPred
+
+tests = testGroup "Constants" [
+  testCase name $ strCheck mAST mStr
+  | (name, type', value, str) <- [
+    (
+      "integer",
+      i32,
+      C.Int 32 1,
+      "global i32 1"
+    ), (
+      "wide integer",
+      IntegerType 65,
+      C.Int 65 1,
+      "global i65 1"
+    ), (
+      "big wide integer",
+      IntegerType 66,
+      C.Int 66 20000000000000000000,
+      "global i66 20000000000000000000"
+    ), (
+      "negative wide integer",
+      IntegerType 65,
+      C.Int 65 36893488147419103231,
+      "global i65 -1"
+    ), (
+      "half",
+      half,
+      C.Float (F.Half 0x1234),
+      "global half 0xH1234"
+    ), (
+      "float",
+      float,
+      C.Float (F.Single 1),
+      "global float 1.000000e+00"
+    ), (
+      "double",
+      double,
+      C.Float (F.Double 1),
+      "global double 1.000000e+00"
+    ), (
+      "quad",
+      fp128,
+      C.Float (F.Quadruple 0x0007000600050004 0x0003000200010000),
+      "global fp128 0xL00030002000100000007000600050004" -- yes, this order is weird
+    ), (
+      "quad 1.0",
+      fp128,
+      C.Float (F.Quadruple 0x3fff000000000000 0x0000000000000000),
+      "global fp128 0xL00000000000000003FFF000000000000" -- yes, this order is weird
+    ), (
+      "x86_fp80",
+      x86_fp80,
+      C.Float (F.X86_FP80 0x0004 0x0003000200010000),
+      "global x86_fp80 0xK00040003000200010000"
+{- don't know how to test this - LLVM's handling of this weird type is even weirder
+    ), (
+      "ppc_fp128",
+      ppc_fp128,
+      C.Float (F.PPC_FP128 0x0007000600050004 0x0003000200010000),
+      "global ppc_fp128 0xM????????????????"
+-}
+    ), (
+      "struct",
+      StructureType False (replicate 2 i32),
+      C.Struct Nothing False (replicate 2 (C.Int 32 1)),
+      "global { i32, i32 } { i32 1, i32 1 }"
+    ), (
+      "dataarray",
+      ArrayType 3 i32,
+      C.Array i32 [C.Int 32 i | i <- [1,2,1]],
+      "global [3 x i32] [i32 1, i32 2, i32 1]"
+    ), (
+      "array",
+      ArrayType 3 (StructureType False [i32]),
+      C.Array (StructureType False [i32]) [C.Struct Nothing False [C.Int 32 i] | i <- [1,2,1]],
+      "global [3 x { i32 }] [{ i32 } { i32 1 }, { i32 } { i32 2 }, { i32 } { i32 1 }]"
+    ), (
+      "datavector",
+      VectorType 3 i32,
+      C.Vector [C.Int 32 i | i <- [1,2,1]],
+      "global <3 x i32> <i32 1, i32 2, i32 1>"
+    ), (
+      "undef",
+      i32,
+      C.Undef i32,
+      "global i32 undef"
+    ), (
+      "binop/cast",
+      i64,
+      C.Add False False (C.PtrToInt (C.GlobalReference (ptr i32) (UnName 1)) i64) (C.Int 64 2),
+      "global i64 add (i64 ptrtoint (i32* @1 to i64), i64 2)"
+    ), (
+      "binop/cast nsw",
+      i64,
+      C.Add True False (C.PtrToInt (C.GlobalReference (ptr i32) (UnName 1)) i64) (C.Int 64 2),
+      "global i64 add nsw (i64 ptrtoint (i32* @1 to i64), i64 2)"
+    ), (
+      "icmp",
+      i1,
+      C.ICmp IPred.SGE (C.GlobalReference (ptr i32) (UnName 1)) (C.GlobalReference (ptr i32) (UnName 2)),
+      "global i1 icmp sge (i32* @1, i32* @2)"
+    ), (
+      "getelementptr",
+      ptr i32,
+      C.GetElementPtr True (C.GlobalReference (ptr i32) (UnName 1)) [C.Int 64 27],
+      "global i32* getelementptr inbounds (i32, i32* @1, i64 27)"
+    ), (
+      "selectvalue",
+      i32,
+      C.Select (C.PtrToInt (C.GlobalReference (ptr i32) (UnName 1)) i1) 
+         (C.Int 32 1)
+         (C.Int 32 2),
+      "global i32 select (i1 ptrtoint (i32* @1 to i1), i32 1, i32 2)"
+    ), (
+      "extractelement",
+      i32,
+      C.ExtractElement
+         (C.BitCast
+             (C.PtrToInt (C.GlobalReference (ptr i32) (UnName 1)) i64)
+             (VectorType 2 i32))
+         (C.Int 32 1),
+      "global i32 extractelement (<2 x i32> bitcast (i64 ptrtoint (i32* @1 to i64) to <2 x i32>), i32 1)"
+    ), (
+     "addrspacecast",
+     (PointerType i32 (AddrSpace 1)),
+     C.AddrSpaceCast (C.GlobalReference (ptr i32) (UnName 1)) (PointerType i32 (AddrSpace 1)),
+     "global i32 addrspace(1)* addrspacecast (i32* @1 to i32 addrspace(1)*)"
+{-
+    ), (
+--  This test made llvm abort as of llvm-3.2.  Now, as a new feature in llvm-3.4, it makes it report a fatal error!
+      "extractvalue",
+      i8,
+      C.ExtractValue
+        (C.Select (C.PtrToInt (C.GlobalReference (p i32) (UnName 1)) i1) 
+         (C.Array i8 [C.Int 8 0])
+         (C.Array i8 [C.Int 8 1]))
+        [0],
+      "global i8 extractvalue ([1 x i8] select (i1 ptrtoint (i32* @1 to i1), [1 x i8] [ i8 1 ], [1 x i8] [ i8 2 ]), 0)"
+-}
+    )
+   ],
+   let mAST = Module "<string>" "<string>" Nothing Nothing [
+             GlobalDefinition $ globalVariableDefaults {
+               G.name = UnName 0, G.type' = type', G.initializer = Just value 
+             },
+             GlobalDefinition $ globalVariableDefaults {
+               G.name = UnName 1, G.type' = i32, G.initializer = Nothing 
+             },
+             GlobalDefinition $ globalVariableDefaults {
+               G.name = UnName 2, G.type' = i32, G.initializer = Nothing 
+             }
+           ]
+       mStr = "; ModuleID = '<string>'\nsource_filename = \"<string>\"\n\n@0 = " ++ str ++ "\n\
+              \@1 = external global i32\n\
+              \@2 = external global i32\n"
+ ]
diff --git a/test/LLVM/Test/DataLayout.hs b/test/LLVM/Test/DataLayout.hs
new file mode 100644
--- /dev/null
+++ b/test/LLVM/Test/DataLayout.hs
@@ -0,0 +1,86 @@
+module LLVM.Test.DataLayout where
+
+import Test.Tasty
+import Test.Tasty.HUnit
+
+import LLVM.Test.Support
+
+import Data.Maybe
+import qualified Data.Set as Set
+import qualified Data.Map as Map
+
+import LLVM.Context
+import LLVM.Module
+import LLVM.AST
+import LLVM.AST.DataLayout
+import LLVM.AST.AddrSpace
+import qualified LLVM.AST.Global as G
+
+m s = "; ModuleID = '<string>'\nsource_filename = \"<string>\"\n" ++ s
+t s = "target datalayout = \"" ++ s ++ "\"\n"
+ddl = defaultDataLayout BigEndian
+
+tests = testGroup "DataLayout" [
+  testCase name $ strCheckC (Module "<string>" "<string>" mdl Nothing []) (m sdl) (m sdlc)
+  | (name, mdl, sdl, sdlc) <- [
+   ("none", Nothing, "", "")
+  ] ++ [
+   (name, Just mdl, t sdl, t (fromMaybe sdl msdlc))
+   | (name, mdl, sdl, msdlc) <- [
+    ("little-endian", defaultDataLayout LittleEndian, "e", Nothing),
+    ("big-endian", defaultDataLayout BigEndian, "E", Nothing),
+    ("native", ddl { nativeSizes = Just (Set.fromList [8,32]) }, "E-n8:32", Nothing),
+    (
+     "no pref",
+     ddl {
+       pointerLayouts = 
+         Map.singleton
+         (AddrSpace 0) 
+         (
+          8,
+          AlignmentInfo {
+            abiAlignment = 64,
+            preferredAlignment = Nothing
+          }
+         )
+     },
+     "E-p:8:64",
+     Nothing
+    ), (
+     "no pref",
+     ddl {
+       pointerLayouts = 
+         Map.insert (AddrSpace 1) (8, AlignmentInfo 32 (Just 64)) (pointerLayouts ddl)
+     },
+     "E-p1:8:32:64",
+     Nothing
+    ), (
+     "big",
+     ddl {
+       endianness = LittleEndian,
+       mangling = Just ELFMangling,
+       stackAlignment = Just 128,
+       pointerLayouts = Map.fromList [
+         (AddrSpace 0, (8, AlignmentInfo {abiAlignment = 8, preferredAlignment = Just 16}))
+        ],
+       typeLayouts = Map.fromList [
+         ((IntegerAlign, 1), AlignmentInfo {abiAlignment = 8, preferredAlignment = Just 256}),
+         ((IntegerAlign, 8), AlignmentInfo {abiAlignment = 8, preferredAlignment = Just 256}),
+         ((IntegerAlign, 16), AlignmentInfo {abiAlignment = 16, preferredAlignment = Just 256}),
+         ((IntegerAlign, 32), AlignmentInfo {abiAlignment = 32, preferredAlignment = Just 256}),
+         ((IntegerAlign, 64), AlignmentInfo {abiAlignment = 64, preferredAlignment = Just 256}),
+         ((VectorAlign, 64), AlignmentInfo {abiAlignment = 64, preferredAlignment = Just 256}),
+         ((VectorAlign, 128), AlignmentInfo {abiAlignment = 128, preferredAlignment = Just 256}),
+         ((FloatAlign, 32), AlignmentInfo {abiAlignment = 32, preferredAlignment = Just 256}),
+         ((FloatAlign, 64), AlignmentInfo {abiAlignment = 64, preferredAlignment = Just 256}),
+         ((FloatAlign, 80), AlignmentInfo {abiAlignment = 128, preferredAlignment = Just 256})
+        ] `Map.union` typeLayouts ddl, 
+       aggregateLayout = AlignmentInfo {abiAlignment = 0, preferredAlignment = Just 256},
+       nativeSizes = Just (Set.fromList [8,16,32,64])
+     },
+     "e-m:e-p:8:8:16-i1:8:256-i8:8:256-i16:16:256-i32:32:256-i64:64:256-v64:64:256-v128:128:256-f32:32:256-f64:64:256-f80:128:256-a:0:256-n8:16:32:64-S128",
+     Nothing
+    )
+   ]
+  ]
+ ]
diff --git a/test/LLVM/Test/ExecutionEngine.hs b/test/LLVM/Test/ExecutionEngine.hs
new file mode 100644
--- /dev/null
+++ b/test/LLVM/Test/ExecutionEngine.hs
@@ -0,0 +1,59 @@
+{-# LANGUAGE
+  ForeignFunctionInterface
+  #-}
+module LLVM.Test.ExecutionEngine where
+
+import Test.Tasty
+import Test.Tasty.HUnit
+
+import LLVM.Test.Support
+
+import Control.Monad
+import Data.Functor
+import Data.Maybe
+
+import Foreign.Ptr
+import Data.Word
+
+import LLVM.Context
+import LLVM.Module
+import LLVM.ExecutionEngine
+import LLVM.AST
+import LLVM.AST.Type
+import LLVM.AST.Name
+import LLVM.AST.AddrSpace
+import qualified LLVM.AST.IntegerPredicate as IPred
+
+import qualified LLVM.AST.Linkage as L
+import qualified LLVM.AST.Visibility as V
+import qualified LLVM.AST.CallingConvention as CC
+import qualified LLVM.AST.Attribute as A
+import qualified LLVM.AST.Global as G
+import qualified LLVM.AST.Constant as C
+
+foreign import ccall "dynamic" mkIO32Stub :: FunPtr (Word32 -> IO Word32) -> (Word32 -> IO Word32)
+
+testJIT :: ExecutionEngine e (FunPtr ()) => (Context -> (e -> IO ()) -> IO ()) -> Assertion
+testJIT withEE = withContext $ \context -> withEE context $ \executionEngine -> do
+  let mAST = Module "runSomethingModule" "runSomethingModule" Nothing Nothing [
+              GlobalDefinition $ functionDefaults {
+                G.returnType = i32,
+                G.name = Name "_foo",
+                G.parameters = ([Parameter i32 (Name "bar") []],False),
+                G.basicBlocks = [
+                  BasicBlock (UnName 0) [] (
+                    Do $ Ret (Just (ConstantOperand (C.Int 32 42))) []
+                  )
+                ]
+               }
+              ]
+
+  s <- withModuleFromAST' context mAST $ \m -> do
+        withModuleInEngine executionEngine m $ \em -> do
+          Just p <- getFunction em (Name "_foo")
+          (mkIO32Stub ((castFunPtr p) :: FunPtr (Word32 -> IO Word32))) 7
+  s @?= 42
+
+tests = testGroup "ExecutionEngine" [
+  testCase "run something with MCJIT" $ testJIT (\c -> withMCJIT c Nothing Nothing Nothing Nothing)
+ ]
diff --git a/test/LLVM/Test/Global.hs b/test/LLVM/Test/Global.hs
new file mode 100644
--- /dev/null
+++ b/test/LLVM/Test/Global.hs
@@ -0,0 +1,42 @@
+module LLVM.Test.Global where
+
+import Test.Tasty
+import Test.Tasty.HUnit
+
+import LLVM.Test.Support
+
+import LLVM.Context
+import LLVM.Module
+import LLVM.AST
+import LLVM.AST.Type as A.T
+import qualified LLVM.AST.Global as G
+
+tests = testGroup "Global" [
+  testGroup "Alignment" [
+    testCase name $ withContext $ \context -> do
+      let ast = Module "<string>" "<string>" Nothing Nothing [ GlobalDefinition g ]
+      ast' <- withModuleFromAST' context ast moduleAST
+      ast' @?= ast
+    | a <- [0,1],
+      s <- [Nothing, Just "foo"],
+      g <- [
+       globalVariableDefaults {
+        G.name = UnName 0,
+        G.type' = i32,
+        G.alignment = a,
+        G.section = s
+        },
+       functionDefaults {
+         G.returnType = A.T.void,
+         G.name = UnName 0,
+         G.parameters = ([], False),
+         G.alignment = a,
+         G.section = s
+       }
+       ],
+      let
+          gn (G.Function {}) = "function"
+          gn (G.GlobalVariable {}) = "variable"
+          name = gn g ++ ", align " ++ show a ++ (maybe "" ("  section " ++ ) s)
+   ]
+ ]
diff --git a/test/LLVM/Test/InlineAssembly.hs b/test/LLVM/Test/InlineAssembly.hs
new file mode 100644
--- /dev/null
+++ b/test/LLVM/Test/InlineAssembly.hs
@@ -0,0 +1,81 @@
+module LLVM.Test.InlineAssembly where
+
+import Test.Tasty
+import Test.Tasty.HUnit
+
+import LLVM.Test.Support
+
+import LLVM.Context
+import LLVM.Module
+
+import LLVM.AST
+import LLVM.AST.Type
+import LLVM.AST.InlineAssembly as IA
+import qualified LLVM.AST.Linkage as L
+import qualified LLVM.AST.Visibility as V
+import qualified LLVM.AST.CallingConvention as CC
+import qualified LLVM.AST.Constant as C
+import qualified LLVM.AST.Global as G
+
+tests = testGroup "InlineAssembly" [
+  testCase "expression" $ do
+    let ast = Module "<string>" "<string>" Nothing Nothing [
+                GlobalDefinition $ 
+                  functionDefaults {
+                    G.returnType = i32,
+                    G.name = Name "foo",
+                    G.parameters = ([Parameter i32 (Name "x") []],False),
+                    G.basicBlocks = [
+                      BasicBlock (UnName 0) [
+                        UnName 1 := Call {
+                          tailCallKind = Nothing,
+                          callingConvention = CC.C,
+                          returnAttributes = [],
+                          function = Left $ InlineAssembly {
+                                       IA.type' = FunctionType i32 [i32] False,
+                                       assembly = "bswap $0",
+                                       constraints = "=r,r",
+                                       hasSideEffects = False,
+                                       alignStack = False,
+                                       dialect = ATTDialect
+                                     },
+                          arguments = [
+                            (LocalReference i32 (Name "x"), [])
+                           ],
+                          functionAttributes = [],
+                          metadata = []
+                        }
+                      ] (
+                        Do $ Ret (Just (LocalReference i32 (UnName 1))) []
+                      )
+                    ]
+                }
+
+              ]
+        s = "; ModuleID = '<string>'\n\
+             \source_filename = \"<string>\"\n\
+             \\n\
+             \define i32 @foo(i32 %x) {\n\
+             \  %1 = call i32 asm \"bswap $0\", \"=r,r\"(i32 %x)\n\
+             \  ret i32 %1\n\
+             \}\n"
+    strCheck ast s,
+
+  testCase "module" $ do
+    let ast = Module "<string>" "<string>" Nothing Nothing [
+                ModuleInlineAssembly "foo",
+                ModuleInlineAssembly "bar",
+                GlobalDefinition $ globalVariableDefaults {
+                  G.name = UnName 0,
+                  G.type' = i32
+                }
+              ]
+        s = "; ModuleID = '<string>'\n\
+             \source_filename = \"<string>\"\n\
+             \\n\
+             \module asm \"foo\"\n\
+             \module asm \"bar\"\n\
+             \\n\
+             \@0 = external global i32\n"
+    strCheck ast s
+ ]
diff --git a/test/LLVM/Test/Instructions.hs b/test/LLVM/Test/Instructions.hs
new file mode 100644
--- /dev/null
+++ b/test/LLVM/Test/Instructions.hs
@@ -0,0 +1,1235 @@
+module LLVM.Test.Instructions where
+
+import Test.Tasty
+import Test.Tasty.HUnit
+
+import LLVM.Test.Support
+
+import Control.Monad
+import Data.Functor
+import Data.List.NonEmpty (NonEmpty((:|)))
+import Data.Maybe
+import Foreign.Ptr
+import Data.Word
+
+import LLVM.Context
+import LLVM.Module
+import LLVM.Diagnostic
+import LLVM.AST
+import LLVM.AST.Type as A.T
+import LLVM.AST.Name
+import LLVM.AST.AddrSpace
+import qualified LLVM.AST.IntegerPredicate as IPred
+import qualified LLVM.AST.FloatingPointPredicate as FPPred
+import qualified LLVM.AST.Linkage as L
+import qualified LLVM.AST.Visibility as V
+import qualified LLVM.AST.CallingConvention as CC
+import qualified LLVM.AST.Attribute as A
+import qualified LLVM.AST.Global as G
+import qualified LLVM.AST.Constant as C
+import qualified LLVM.AST.RMWOperation as RMWOp
+
+tests = testGroup "Instructions" [
+  testGroup "regular" [
+    testCase name $ do
+      let mAST = Module "<string>" "<string>" Nothing Nothing [
+            GlobalDefinition $ functionDefaults {
+              G.returnType = A.T.void,
+              G.name = UnName 0,
+              G.parameters = ([Parameter t (UnName n) [] | (t,n) <- zip ts [0..]], False),
+              G.basicBlocks = [
+                BasicBlock (UnName 7) [
+                  namedInstr
+                 ] (
+                  Do $ Ret Nothing []
+                 )
+               ]
+            }
+           ]
+          mStr = "; ModuleID = '<string>'\n\
+                 \source_filename = \"<string>\"\n\
+                 \\n\
+                 \define void @0(i32, float, i32*, i64, i1, <2 x i32>, { i32, i32 }) {\n\
+                 \  " ++ namedInstrS ++ "\n\
+                 \  ret void\n\
+                 \}\n"
+      strCheck mAST mStr
+    | let ts = [
+           i32,
+           float,
+           ptr i32,
+           i64,
+           i1,
+           VectorType 2 i32,
+           StructureType False [i32, i32]
+           ],
+      let a i = LocalReference (ts !! fromIntegral i) (UnName i),
+      (name, namedInstr, namedInstrS) <- (
+        [
+         (name, UnName 8 := instr, "%8 = " ++ instrS)
+         | (name, instr, instrS) <- [
+          ("add",
+           Add {
+             nsw = False,
+             nuw = False,
+             operand0 = a 0,
+             operand1 = a 0,
+             metadata = [] 
+           },
+           "add i32 %0, %0"),
+          ("nsw",
+           Add {
+             nsw = True,
+             nuw = False,
+             operand0 = a 0,
+             operand1 = a 0,
+             metadata = [] 
+           },
+           "add nsw i32 %0, %0"),
+          ("nuw",
+           Add {
+             nsw = False,
+             nuw = True,
+             operand0 = a 0,
+             operand1 = a 0,
+             metadata = [] 
+           },
+           "add nuw i32 %0, %0"),
+          ("fadd",
+           FAdd {
+             fastMathFlags = NoFastMathFlags,
+             operand0 = a 1,
+             operand1 = a 1,
+             metadata = [] 
+           },
+           "fadd float %1, %1"),
+          ("sub",
+           Sub {
+             nsw = False,
+             nuw = False,
+             operand0 = a 0,
+             operand1 = a 0,
+             metadata = [] 
+           },
+           "sub i32 %0, %0"),
+          ("fsub",
+           FSub {
+             fastMathFlags = NoFastMathFlags,
+             operand0 = a 1,
+             operand1 = a 1,
+             metadata = [] 
+           },
+           "fsub float %1, %1"),
+          ("mul",
+           Mul {
+             nsw = False,
+             nuw = False,
+             operand0 = a 0,
+             operand1 = a 0,
+             metadata = [] 
+           },
+           "mul i32 %0, %0"),
+          ("fmul",
+           FMul {
+             fastMathFlags = NoFastMathFlags,
+             operand0 = a 1,
+             operand1 = a 1,
+             metadata = [] 
+           },
+           "fmul float %1, %1"),
+          ("udiv",
+           UDiv {
+             exact = False,
+             operand0 = a 0,
+             operand1 = a 0,
+             metadata = [] 
+           },
+           "udiv i32 %0, %0"),
+          ("exact",
+           UDiv {
+             exact = True,
+             operand0 = a 0,
+             operand1 = a 0,
+             metadata = [] 
+           },
+           "udiv exact i32 %0, %0"),
+          ("sdiv",
+           SDiv {
+             exact = False,
+             operand0 = a 0,
+             operand1 = a 0,
+             metadata = [] 
+           },
+           "sdiv i32 %0, %0"),
+          ("fdiv",
+           FDiv {
+             fastMathFlags = NoFastMathFlags,
+             operand0 = a 1,
+             operand1 = a 1,
+             metadata = [] 
+           },
+           "fdiv float %1, %1"),
+          ("urem",
+           URem {
+             operand0 = a 0,
+             operand1 = a 0,
+             metadata = [] 
+           },
+           "urem i32 %0, %0"),
+          ("srem",
+           SRem {
+             operand0 = a 0,
+             operand1 = a 0,
+             metadata = [] 
+           },
+           "srem i32 %0, %0"),
+          ("frem",
+           FRem {
+             fastMathFlags = NoFastMathFlags,
+             operand0 = a 1,
+             operand1 = a 1,
+             metadata = [] 
+           },
+           "frem float %1, %1"),
+          ("frem fast",
+           FRem {
+             fastMathFlags = UnsafeAlgebra,
+             operand0 = a 1,
+             operand1 = a 1,
+             metadata = [] 
+           },
+           "frem fast float %1, %1"),
+          ("shl",
+           Shl {
+             nsw = False,
+             nuw = False,
+             operand0 = a 0,
+             operand1 = a 0,
+             metadata = [] 
+           },
+           "shl i32 %0, %0"),
+          ("ashr",
+           AShr {
+             exact = False,
+             operand0 = a 0,
+             operand1 = a 0,
+             metadata = [] 
+           },
+           "ashr i32 %0, %0"),
+          ("lshr",
+           LShr {
+             exact = False,
+             operand0 = a 0,
+             operand1 = a 0,
+             metadata = [] 
+           },
+           "lshr i32 %0, %0"),
+          ("and",
+           And {
+             operand0 = a 0,
+             operand1 = a 0,
+             metadata = [] 
+           },
+           "and i32 %0, %0"),
+          ("or",
+           Or {
+             operand0 = a 0,
+             operand1 = a 0,
+             metadata = [] 
+           },
+           "or i32 %0, %0"),
+          ("xor",
+           Xor {
+             operand0 = a 0,
+             operand1 = a 0,
+             metadata = [] 
+           },
+           "xor i32 %0, %0"),
+          ("alloca",
+           Alloca {
+             allocatedType = i32,
+             numElements = Nothing,
+             alignment = 0,
+             metadata = [] 
+           },
+           "alloca i32"),
+          ("alloca tricky",
+           Alloca {
+             allocatedType = IntegerType 7,
+             numElements = Just (ConstantOperand (C.Int 32 2)),
+             alignment = 128,
+             metadata = [] 
+           },
+           "alloca i7, i32 2, align 128"),
+          ("load",
+           Load {
+             volatile = False,
+             address = a 2,
+             maybeAtomicity = Nothing,
+             alignment = 0,
+             metadata = [] 
+           },
+           "load i32, i32* %2"),
+          ("volatile",
+           Load {
+             volatile = True,
+             address = a 2,
+             maybeAtomicity = Nothing,
+             alignment = 0,
+             metadata = [] 
+           },
+           "load volatile i32, i32* %2"),
+          ("acquire",
+           Load {
+             volatile = False,
+             address = a 2,
+             maybeAtomicity = Just (CrossThread, Acquire),
+             alignment = 1,
+             metadata = [] 
+           },
+           "load atomic i32, i32* %2 acquire, align 1"),
+          ("singlethread",
+           Load {
+             volatile = False,
+             address = a 2,
+             maybeAtomicity = Just (SingleThread, Monotonic),
+             alignment = 1,
+             metadata = [] 
+           },
+           "load atomic i32, i32* %2 singlethread monotonic, align 1"),
+          ("GEP",
+           GetElementPtr {
+             inBounds = False,
+             address = a 2,
+             indices = [ a 0 ],
+             metadata = [] 
+           },
+           "getelementptr i32, i32* %2, i32 %0"),
+          ("inBounds",
+           GetElementPtr {
+             inBounds = True,
+             address = a 2,
+             indices = [ a 0 ],
+             metadata = [] 
+           },
+           "getelementptr inbounds i32, i32* %2, i32 %0"),
+          ("cmpxchg",
+           CmpXchg {
+             volatile = False,
+             address = a 2,
+             expected = a 0,
+             replacement = a 0,
+             atomicity = (CrossThread, Monotonic),
+             failureMemoryOrdering = Monotonic,
+             metadata = [] 
+           },
+           "cmpxchg i32* %2, i32 %0, i32 %0 monotonic monotonic"),
+          ("atomicrmw",
+           AtomicRMW {
+             volatile = False,
+             rmwOperation = RMWOp.UMax,
+             address = a 2,
+             value = a 0,
+             atomicity = (CrossThread, Release),
+             metadata = []
+           },
+           "atomicrmw umax i32* %2, i32 %0 release"),
+
+          ("trunc",
+           Trunc {
+             operand0 = a 0,
+             type' = i16,
+             metadata = [] 
+           },
+           "trunc i32 %0 to i16"),
+          ("zext",
+           ZExt {
+             operand0 = a 0,
+             type' = i64,
+             metadata = [] 
+           },
+           "zext i32 %0 to i64"),
+          ("sext",
+           SExt {
+             operand0 = a 0,
+             type' = i64,
+             metadata = [] 
+           },
+           "sext i32 %0 to i64"),
+          ("fptoui",
+           FPToUI {
+             operand0 = a 1,
+             type' = i64,
+             metadata = [] 
+           },
+           "fptoui float %1 to i64"),
+          ("fptosi",
+           FPToSI {
+             operand0 = a 1,
+             type' = i64,
+             metadata = [] 
+           },
+           "fptosi float %1 to i64"),
+          ("uitofp",
+           UIToFP {
+             operand0 = a 0,
+             type' = float,
+             metadata = [] 
+           },
+           "uitofp i32 %0 to float"),
+          ("sitofp",
+           SIToFP {
+             operand0 = a 0,
+             type' = float,
+             metadata = [] 
+           },
+           "sitofp i32 %0 to float"),
+          ("fptrunc",
+           FPTrunc {
+             operand0 = a 1,
+             type' = half,
+             metadata = [] 
+           },
+           "fptrunc float %1 to half"),
+          ("fpext",
+           FPExt {
+             operand0 = a 1,
+             type' = double,
+             metadata = [] 
+           },
+           "fpext float %1 to double"),
+          ("ptrtoint",
+           PtrToInt {
+             operand0 = a 2,
+             type' = i32,
+             metadata = [] 
+           },
+           "ptrtoint i32* %2 to i32"),
+          ("inttoptr",
+           IntToPtr {
+             operand0 = a 0,
+             type' = ptr i32,
+             metadata = [] 
+           },
+           "inttoptr i32 %0 to i32*"),
+          ("bitcast",
+           BitCast {
+             operand0 = a 0,
+             type' = float,
+             metadata = [] 
+           },
+           "bitcast i32 %0 to float"),
+          ("addrspacecast",
+           AddrSpaceCast {
+             operand0 = a 2,
+             type' = PointerType i32 (AddrSpace 2),
+             metadata = [] 
+           },
+           "addrspacecast i32* %2 to i32 addrspace(2)*"),
+          ("select",
+           Select {
+             condition' = a 4,
+             trueValue = a 0,
+             falseValue = a 0,
+             metadata = []
+           },
+           "select i1 %4, i32 %0, i32 %0"),
+          ("vaarg",
+           VAArg {
+             argList = a 2,
+             type' = i16,
+             metadata = []
+           },
+           "va_arg i32* %2, i16"),
+          ("extractelement",
+           ExtractElement {
+             vector = a 5,
+             index = a 0,
+             metadata = []
+           },
+           "extractelement <2 x i32> %5, i32 %0"),
+          ("insertelement",
+           InsertElement {
+             vector = a 5,
+             element = a 0,
+             index = a 0,
+             metadata = []
+           },
+           "insertelement <2 x i32> %5, i32 %0, i32 %0"),
+          ("shufflevector",
+           ShuffleVector {
+             operand0 = a 5,
+             operand1 = a 5,
+             mask = C.Vector [ C.Int 32 p | p <- [0..1] ],
+             metadata = []
+           },
+           "shufflevector <2 x i32> %5, <2 x i32> %5, <2 x i32> <i32 0, i32 1>"),
+          ("extractvalue",
+           ExtractValue {
+             aggregate = a 6,
+             indices' = [0],
+             metadata = []
+           },
+           "extractvalue { i32, i32 } %6, 0"),
+          ("insertvalue",
+           InsertValue {
+             aggregate = a 6,
+             element = a 0,
+             indices' = [0],
+             metadata = []
+           },
+           "insertvalue { i32, i32 } %6, i32 %0, 0")
+         ] ++ [
+          ("landingpad-" ++ n,
+           LandingPad {
+             type' = StructureType False [ 
+                ptr i8,
+                i32
+               ],
+             cleanup = cp,
+             clauses = cls,
+             metadata = []
+           },
+           "landingpad { i8*, i32 }" ++ s)
+          | (clsn,cls,clss) <- [
+           ("catch",
+            [Catch (C.Null (ptr i8))],
+            "\n          catch i8* null"),
+           ("filter",
+            [Filter (C.Null (ArrayType 1 (ptr i8)))],
+            "\n          filter [1 x i8*] zeroinitializer")
+          ],
+          (cpn, cp, cps) <- [ ("-cleanup", True, "\n          cleanup"), ("", False, "") ],
+          let s = cps ++ clss
+              n = clsn ++ cpn
+         ] ++ [
+          ("icmp-" ++ ps,
+           ICmp { iPredicate = p, operand0 = a 0, operand1 = a 0, metadata = [] },
+           "icmp " ++ ps ++ " i32 %0, %0")
+           | (ps, p) <- [
+           ("eq", IPred.EQ),
+           ("ne", IPred.NE),
+           ("ugt", IPred.UGT),
+           ("uge", IPred.UGE),
+           ("ult", IPred.ULT),
+           ("ule", IPred.ULE),
+           ("sgt", IPred.SGT),
+           ("sge", IPred.SGE),
+           ("slt", IPred.SLT),
+           ("sle", IPred.SLE)
+          ]
+         ] ++ [
+          ("fcmp-" ++ ps,
+           FCmp { fpPredicate = p, operand0 = a 1, operand1 = a 1, metadata = [] },
+           "fcmp " ++ ps ++ " float %1, %1")
+           | (ps, p) <- [
+           ("false", FPPred.False),
+           ("oeq", FPPred.OEQ),
+           ("ogt", FPPred.OGT),
+           ("oge", FPPred.OGE),
+           ("olt", FPPred.OLT),
+           ("ole", FPPred.OLE),
+           ("one", FPPred.ONE),
+           ("ord", FPPred.ORD),
+           ("uno", FPPred.UNO),
+           ("ueq", FPPred.UEQ),
+           ("ugt", FPPred.UGT),
+           ("uge", FPPred.UGE),
+           ("ult", FPPred.ULT),
+           ("ule", FPPred.ULE),
+           ("une", FPPred.UNE),
+           ("true", FPPred.True)
+          ]
+         ]
+        ] ++ [
+         ("store",
+          Do $ Store {
+            volatile = False,
+            address = a 2,
+            value = a 0,
+            maybeAtomicity = Nothing,
+            alignment = 0,
+            metadata = [] 
+          },
+          "store i32 %0, i32* %2"),
+         ("fence",
+          Do $ Fence {
+            atomicity = (CrossThread, Acquire),
+            metadata = [] 
+          },
+          "fence acquire"),
+          ("call",
+           Do $ Call {
+             tailCallKind = Nothing,
+             callingConvention = CC.C,
+             returnAttributes = [],
+             function = Right (ConstantOperand (C.GlobalReference (ptr (FunctionType A.T.void ts False)) (UnName 0))),
+             arguments = [ (a i, []) | i <- [0..6] ],
+             functionAttributes = [],
+             metadata = []
+           },
+           "call void @0(i32 %0, float %1, i32* %2, i64 %3, i1 %4, <2 x i32> %5, { i32, i32 } %6)")
+        ]
+      )
+   ],
+  testCase "GEP inBounds constant" $ do
+    let mAST = Module "<string>" "<string>" Nothing Nothing [
+          GlobalDefinition $ globalVariableDefaults {
+            G.name = Name "fortytwo",
+            G.type' = i32,
+            G.isConstant = True,
+            G.initializer = Just $ C.Int 32 42
+          },
+          GlobalDefinition $ functionDefaults {
+            G.returnType = i32,
+            G.name = UnName 0,
+            G.basicBlocks = [
+              BasicBlock (UnName 1) [
+                UnName 2 := GetElementPtr {
+                  inBounds = True,
+                  address = ConstantOperand (C.GlobalReference (ptr i32) (Name "fortytwo")),
+                  indices = [ ConstantOperand (C.Int 32 0) ],
+                  metadata = []
+                },
+                UnName 3 := Load {
+                  volatile = False,
+                  address = LocalReference (ptr i32) (UnName 2),
+                  maybeAtomicity = Nothing,
+                  alignment = 1,
+                  metadata = []
+                }
+              ] (
+                Do $ Ret (Just (LocalReference i32 (UnName 3))) []
+              )
+             ]
+           }
+          ]
+        mStr = "; ModuleID = '<string>'\n\
+               \source_filename = \"<string>\"\n\
+               \\n\
+               \@0 = constant i32 42\n\
+               \\n\
+               \define i32 @1() {\n\
+               \  %1 = load i32, i32* @0, align 1\n\
+               \  ret i32 %1\n\
+               \}\n"
+    s <- withContext $ \context -> withModuleFromAST' context mAST moduleLLVMAssembly
+    s @?= mStr,
+    
+  testGroup "terminators" [
+    testCase name $ strCheck mAST mStr
+    | (name, mAST, mStr) <- [
+     (
+       "ret",
+       Module "<string>" "<string>" Nothing Nothing [
+        GlobalDefinition $ functionDefaults {
+          G.returnType = A.T.void,
+          G.name = UnName 0,
+          G.basicBlocks = [
+            BasicBlock (UnName 0) [
+             ] (
+              Do $ Ret Nothing []
+             )
+           ]
+         }
+        ],
+       "; ModuleID = '<string>'\n\
+       \source_filename = \"<string>\"\n\
+       \\n\
+       \define void @0() {\n\
+       \  ret void\n\
+       \}\n"
+     ), (
+       "br",
+       Module "<string>" "<string>" Nothing Nothing [
+        GlobalDefinition $ functionDefaults {
+          G.returnType = A.T.void,
+          G.name = UnName 0,
+          G.basicBlocks = [
+            BasicBlock (UnName 0) [] (
+              Do $ Br (Name "foo") []
+             ),
+            BasicBlock (Name "foo") [] (
+              Do $ Ret Nothing []
+             )
+           ]
+         }
+        ],
+       "; ModuleID = '<string>'\n\
+       \source_filename = \"<string>\"\n\
+       \\n\
+       \define void @0() {\n\
+       \  br label %foo\n\
+       \\n\
+       \foo:                                              ; preds = %0\n\
+       \  ret void\n\
+       \}\n"
+     ), (
+       "condbr",
+       Module "<string>" "<string>" Nothing Nothing [
+        GlobalDefinition $ functionDefaults {
+          G.returnType = A.T.void,
+          G.name = UnName 0,
+          G.basicBlocks = [
+            BasicBlock (Name "bar") [] (
+              Do $ CondBr (ConstantOperand (C.Int 1 1)) (Name "foo") (Name "bar") []
+             ),
+            BasicBlock (Name "foo") [] (
+              Do $ Ret Nothing []
+             )
+           ]
+          }
+        ],
+       "; ModuleID = '<string>'\n\
+       \source_filename = \"<string>\"\n\
+       \\n\
+       \define void @0() {\n\
+       \bar:\n\
+       \  br i1 true, label %foo, label %bar\n\
+       \\n\
+       \foo:                                              ; preds = %bar\n\
+       \  ret void\n\
+       \}\n"
+     ), (
+       "switch",
+       Module "<string>" "<string>" Nothing Nothing [
+         GlobalDefinition $ functionDefaults {
+           G.returnType = A.T.void,
+           G.name = UnName 0,
+           G.basicBlocks = [
+             BasicBlock (UnName 0) [] (
+               Do $ Switch {
+                 operand0' = ConstantOperand (C.Int 16 2),
+                 defaultDest = Name "foo",
+                 dests = [
+                  (C.Int 16 0, UnName 0),
+                  (C.Int 16 2, Name "foo"),
+                  (C.Int 16 3, UnName 0)
+                 ],
+                 metadata' = []
+              }
+             ),
+             BasicBlock (Name "foo") [] (
+               Do $ Ret Nothing []
+              )
+            ]
+          }
+        ],
+       "; ModuleID = '<string>'\n\
+       \source_filename = \"<string>\"\n\
+       \\n\
+       \define void @0() {\n\
+       \; <label>:0:\n\
+       \  switch i16 2, label %foo [\n\
+       \    i16 0, label %0\n\
+       \    i16 2, label %foo\n\
+       \    i16 3, label %0\n\
+       \  ]\n\
+       \\n\
+       \foo:                                              ; preds = %0, %0\n\
+       \  ret void\n\
+       \}\n"
+     ), (
+       "indirectbr",
+       Module "<string>" "<string>" Nothing Nothing [
+        GlobalDefinition $ globalVariableDefaults {
+          G.name = UnName 0,
+          G.type' = ptr i8,
+          G.initializer = Just (C.BlockAddress (Name "foo") (UnName 2))
+        },
+        GlobalDefinition $ functionDefaults {
+          G.returnType = A.T.void,
+          G.name = Name "foo",
+          G.basicBlocks = [
+            BasicBlock (UnName 0) [
+              UnName 1 := Load {
+                       volatile = False,
+                       address = ConstantOperand (C.GlobalReference (ptr (ptr i8)) (UnName 0)),
+                       maybeAtomicity = Nothing,
+                       alignment = 0,
+                       metadata = [] 
+                     }
+            ] (
+              Do $ IndirectBr {
+                operand0' = LocalReference (ptr i8) (UnName 1),
+                possibleDests = [UnName 2],
+                metadata' = []
+             }
+            ),
+            BasicBlock (UnName 2) [] (
+              Do $ Ret Nothing []
+             )
+           ]
+         }
+        ],
+--       \  indirectbr i8* null, [label %foo]\n\
+       "; ModuleID = '<string>'\n\
+       \source_filename = \"<string>\"\n\
+       \\n\
+       \@0 = global i8* blockaddress(@foo, %2)\n\
+       \\n\
+       \define void @foo() {\n\
+       \  %1 = load i8*, i8** @0\n\
+       \  indirectbr i8* %1, [label %2]\n\
+       \\n\
+       \; <label>:2:                                      ; preds = %0\n\
+       \  ret void\n\
+       \}\n"
+     ), (
+       "invoke",
+       Module "<string>" "<string>" Nothing Nothing [
+        GlobalDefinition $ functionDefaults {
+          G.returnType = A.T.void,
+          G.name = UnName 0,
+          G.personalityFunction = Just $ C.GlobalReference
+            (ptr (FunctionType A.T.void [i32,i16] False))
+            (UnName 0)
+          ,
+          G.parameters = ([
+            Parameter i32 (UnName 0) [],
+            Parameter i16 (UnName 1) []
+           ], False),
+          G.basicBlocks = [
+            BasicBlock (UnName 2) [] (
+              Do $ Invoke {
+               callingConvention' = CC.C,
+               returnAttributes' = [],
+               function' = Right (ConstantOperand (C.GlobalReference (ptr (FunctionType A.T.void [i32, i16] False)) (UnName 0))),
+               arguments' = [
+                (ConstantOperand (C.Int 32 4), []),
+                (ConstantOperand (C.Int 16 8), [])
+               ],
+               functionAttributes' = [],
+               returnDest = Name "foo",
+               exceptionDest = Name "bar",
+               metadata' = []
+              }
+             ),
+            BasicBlock (Name "foo") [] (
+              Do $ Ret Nothing []
+             ),
+            BasicBlock (Name "bar") [
+             UnName 3 := LandingPad {
+               type' = StructureType False [
+                  ptr i8,
+                  i32
+                 ],
+               cleanup = True,
+               clauses = [Catch (C.Null (ptr i8))],
+               metadata = []
+             }
+             ] (
+              Do $ Ret Nothing []
+             )
+           ]
+         }
+        ],
+       "; ModuleID = '<string>'\n\
+       \source_filename = \"<string>\"\n\
+       \\n\
+       \define void @0(i32, i16) personality void (i32, i16)* @0 {\n\
+       \  invoke void @0(i32 4, i16 8)\n\
+       \          to label %foo unwind label %bar\n\
+       \\n\
+       \foo:                                              ; preds = %2\n\
+       \  ret void\n\
+       \\n\
+       \bar:                                              ; preds = %2\n\
+       \  %3 = landingpad { i8*, i32 }\n\
+       \          cleanup\n\
+       \          catch i8* null\n\
+       \  ret void\n\
+       \}\n"
+      ), (
+       "resume",
+       Module "<string>" "<string>" Nothing Nothing [
+         GlobalDefinition $ functionDefaults {
+           G.returnType = A.T.void,
+           G.name = UnName 0,
+           G.basicBlocks = [
+             BasicBlock (UnName 0) [] (
+               Do $ Resume (ConstantOperand (C.Int 32 1)) []
+              )
+            ]
+          }
+        ],
+       "; ModuleID = '<string>'\n\
+       \source_filename = \"<string>\"\n\
+       \\n\
+       \define void @0() {\n\
+       \  resume i32 1\n\
+       \}\n"
+     ), (
+       "unreachable",
+       Module "<string>" "<string>" Nothing Nothing [
+        GlobalDefinition $ functionDefaults {
+          G.returnType = A.T.void,
+          G.name = UnName 0,
+          G.basicBlocks = [
+            BasicBlock (UnName 0) [] (
+              Do $ Unreachable []
+             )
+           ]
+         }
+        ],
+       "; ModuleID = '<string>'\n\
+       \source_filename = \"<string>\"\n\
+       \\n\
+       \define void @0() {\n\
+       \  unreachable\n\
+       \}\n"
+     ), ( -- This testcase is taken from test/Feature/exception.ll in LLVM
+       "cleanupret0",
+       Module {
+         moduleName = "<string>",
+         moduleSourceFileName = "<string>",
+         moduleDataLayout = Nothing,
+         moduleTargetTriple = Nothing,
+         moduleDefinitions = [
+           GlobalDefinition functionDefaults {
+             G.returnType = VoidType,
+             G.name = Name "_Z3quxv"
+           },
+           GlobalDefinition functionDefaults {
+             G.returnType = IntegerType {typeBits = 32},
+             G.name = Name "__gxx_personality_v0",
+             G.parameters = ([], True)
+           },
+           GlobalDefinition functionDefaults {
+             G.returnType = VoidType,
+             G.name = Name "cleanupret0",
+             G.basicBlocks = [
+               G.BasicBlock (Name "entry") [] (
+                 Do Invoke {
+                   callingConvention' = CC.C,
+                   returnAttributes' = [],
+                   function' = Right (
+                     ConstantOperand (
+                       C.GlobalReference PointerType {
+                         pointerReferent = FunctionType {resultType = VoidType, argumentTypes = [], isVarArg = False},
+                         pointerAddrSpace = AddrSpace 0
+                       } (Name "_Z3quxv")
+                     )
+                   ),
+                   arguments' = [],
+                   returnDest = Name "exit",
+                   exceptionDest = Name "pad",
+                   metadata' = [],
+                   functionAttributes' = []
+                 }
+               ),
+               G.BasicBlock
+                 (Name "pad")
+                 [Name "cp" := CleanupPad { parentPad = ConstantOperand C.TokenNone, args = [ConstantOperand C.Int { C.integerBits = 7, C.integerValue = 4 }], metadata = [] }]
+                 (
+                 Do CleanupRet {
+                   cleanupPad = LocalReference TokenType (Name "cp"),
+                   unwindDest = Nothing,
+                   metadata' = []
+                 }
+                 ),
+               G.BasicBlock (Name "exit") [] (Do Ret { returnOperand = Nothing, metadata' = [] })
+             ],
+             G.personalityFunction = Just (
+               C.GlobalReference PointerType {
+                 pointerReferent = FunctionType {resultType = IntegerType { typeBits = 32 }, argumentTypes = [], isVarArg = True},
+                 pointerAddrSpace = AddrSpace 0
+               } (Name "__gxx_personality_v0")
+             )
+           }
+         ]
+       },
+       "; ModuleID = '<string>'\n\
+       \source_filename = \"<string>\"\n\
+       \\n\
+       \declare void @_Z3quxv()\n\
+       \\n\
+       \declare i32 @__gxx_personality_v0(...)\n\
+       \\n\
+       \define void @cleanupret0() personality i32 (...)* @__gxx_personality_v0 {\n\
+       \entry:\n\
+       \  invoke void @_Z3quxv()\n\
+       \          to label %exit unwind label %pad\n\
+       \\n\
+       \pad:                                              ; preds = %entry\n\
+       \  %cp = cleanuppad within none [i7 4]\n\
+       \  cleanupret from %cp unwind to caller\n\
+       \\n\
+       \exit:                                             ; preds = %entry\n\
+       \  ret void\n\
+       \}\n"
+     ), ( -- This testcase is taken from test/Feature/exception.ll in LLVM
+       "cleanupret1",
+       Module {
+         moduleName = "<string>",
+         moduleSourceFileName = "<string>",
+         moduleDataLayout = Nothing,
+         moduleTargetTriple = Nothing,
+         moduleDefinitions = [
+           GlobalDefinition functionDefaults {
+             G.returnType = VoidType,
+             G.name = Name "_Z3quxv"
+           },
+           GlobalDefinition functionDefaults {
+             G.returnType = IntegerType {typeBits = 32},
+             G.name = Name "__gxx_personality_v0",
+             G.parameters = ([], True)
+           },
+           GlobalDefinition functionDefaults {
+             G.returnType = VoidType,
+             G.name = Name "cleanupret1",
+             G.basicBlocks = [
+               G.BasicBlock (Name "entry") [] (
+                 Do Invoke {
+                   callingConvention' = CC.C,
+                   returnAttributes' = [],
+                   function' = Right (
+                     ConstantOperand (
+                       C.GlobalReference PointerType {
+                         pointerReferent = FunctionType {resultType = VoidType, argumentTypes = [], isVarArg = False},
+                         pointerAddrSpace = AddrSpace 0
+                       } (Name "_Z3quxv")
+                     )
+                   ),
+                   arguments' = [],
+                   returnDest = Name "exit",
+                   exceptionDest = Name "pad",
+                   metadata' = [],
+                   functionAttributes' = []
+                 }
+               ),
+               G.BasicBlock
+                 (Name "cleanup")
+                 []
+                 (Do CleanupRet {
+                    cleanupPad = LocalReference TokenType (Name "cp"),
+                    unwindDest = Nothing,
+                    metadata' = []
+                 }),
+               G.BasicBlock
+                 (Name "pad")
+                 [Name "cp" := CleanupPad { parentPad = ConstantOperand C.TokenNone, args = [], metadata = [] }]
+                 (Do Br { dest = Name "cleanup", metadata' = [] }),
+               G.BasicBlock (Name "exit") [] (Do Ret { returnOperand = Nothing, metadata' = [] })
+             ],
+             G.personalityFunction = Just (
+               C.GlobalReference PointerType {
+                 pointerReferent = FunctionType {resultType = IntegerType { typeBits = 32 }, argumentTypes = [], isVarArg = True},
+                 pointerAddrSpace = AddrSpace 0
+               } (Name "__gxx_personality_v0")
+             )
+           }
+         ]
+       },
+       "; ModuleID = '<string>'\n\
+       \source_filename = \"<string>\"\n\
+       \\n\
+       \declare void @_Z3quxv()\n\
+       \\n\
+       \declare i32 @__gxx_personality_v0(...)\n\
+       \\n\
+       \define void @cleanupret1() personality i32 (...)* @__gxx_personality_v0 {\n\
+       \entry:\n\
+       \  invoke void @_Z3quxv()\n\
+       \          to label %exit unwind label %pad\n\
+       \\n\
+       \cleanup:                                          ; preds = %pad\n\
+       \  cleanupret from %cp unwind to caller\n\
+       \\n\
+       \pad:                                              ; preds = %entry\n\
+       \  %cp = cleanuppad within none []\n\
+       \  br label %cleanup\n\
+       \\n\
+       \exit:                                             ; preds = %entry\n\
+       \  ret void\n\
+       \}\n"
+     ), ( -- This testcase is taken from test/Feature/exception.ll in LLVM
+       "catchret0",
+       Module {
+         moduleName = "<string>",
+         moduleSourceFileName = "<string>",
+         moduleDataLayout = Nothing,
+         moduleTargetTriple = Nothing,
+         moduleDefinitions = [
+           GlobalDefinition functionDefaults {
+             G.returnType = VoidType,
+             G.name = Name "_Z3quxv"
+           },
+           GlobalDefinition functionDefaults {
+             G.returnType = IntegerType {typeBits = 32},
+             G.name = Name "__gxx_personality_v0",
+             G.parameters = ([], True)
+           },
+           GlobalDefinition functionDefaults {
+             G.returnType = VoidType,
+             G.name = Name "catchret0",
+             G.basicBlocks = [
+               G.BasicBlock (Name "entry") [] (
+                 Do Invoke {
+                   callingConvention' = CC.C,
+                   returnAttributes' = [],
+                   function' = Right (
+                     ConstantOperand (
+                       C.GlobalReference PointerType {
+                         pointerReferent = FunctionType {resultType = VoidType, argumentTypes = [], isVarArg = False},
+                         pointerAddrSpace = AddrSpace 0
+                       } (Name "_Z3quxv")
+                     )
+                   ),
+                   arguments' = [],
+                   returnDest = Name "exit",
+                   exceptionDest = Name "pad",
+                   metadata' = [],
+                   functionAttributes' = []
+                 }
+               ),
+               G.BasicBlock (Name "pad") [] (
+                 Name "cs1" := CatchSwitch {
+                   parentPad' = ConstantOperand C.TokenNone,
+                   catchHandlers = (Name "catch" :| []),
+                   defaultUnwindDest = Nothing,
+                   metadata' = []
+                 }
+               ),
+               G.BasicBlock
+                 (Name "catch")
+                 [Name "cp" := CatchPad { catchSwitch = LocalReference TokenType (Name "cs1"), args = [ConstantOperand C.Int { C.integerBits = 7, C.integerValue = 4 }], metadata = [] }] (
+                 Do CatchRet {
+                   catchPad = LocalReference TokenType (Name "cp"),
+                   successor = Name "exit",
+                   metadata' = []
+                 }
+               ),
+               G.BasicBlock (Name "exit") [] (Do Ret { returnOperand = Nothing, metadata' = [] })
+             ],
+             G.personalityFunction = Just (
+               C.GlobalReference PointerType {
+                 pointerReferent = FunctionType {resultType = IntegerType { typeBits = 32 }, argumentTypes = [], isVarArg = True},
+                 pointerAddrSpace = AddrSpace 0
+               } (Name "__gxx_personality_v0")
+             )
+           }
+         ]
+       },
+       "; ModuleID = '<string>'\n\
+       \source_filename = \"<string>\"\n\
+       \\n\
+       \declare void @_Z3quxv()\n\
+       \\n\
+       \declare i32 @__gxx_personality_v0(...)\n\
+       \\n\
+       \define void @catchret0() personality i32 (...)* @__gxx_personality_v0 {\n\
+       \entry:\n\
+       \  invoke void @_Z3quxv()\n\
+       \          to label %exit unwind label %pad\n\
+       \\n\
+       \pad:                                              ; preds = %entry\n\
+       \  %cs1 = catchswitch within none [label %catch] unwind to caller\n\
+       \\n\
+       \catch:                                            ; preds = %pad\n\
+       \  %cp = catchpad within %cs1 [i7 4]\n\
+       \  catchret from %cp to label %exit\n\
+       \\n\
+       \exit:                                             ; preds = %catch, %entry\n\
+       \  ret void\n\
+       \}\n"
+     ), ( -- This testcase is taken from test/Feature/exception.ll in LLVM
+       "catchret1",
+       Module {
+         moduleName = "<string>",
+         moduleSourceFileName = "<string>",
+         moduleDataLayout = Nothing,
+         moduleTargetTriple = Nothing,
+         moduleDefinitions = [
+           GlobalDefinition functionDefaults {
+             G.returnType = VoidType,
+             G.name = Name "_Z3quxv"
+           },
+           GlobalDefinition functionDefaults {
+             G.returnType = IntegerType {typeBits = 32},
+             G.name = Name "__gxx_personality_v0",
+             G.parameters = ([], True)
+           },
+           GlobalDefinition functionDefaults {
+             G.returnType = VoidType,
+             G.name = Name "catchret0",
+             G.basicBlocks = [
+               G.BasicBlock (Name "entry") [] (
+                 Do Invoke {
+                   callingConvention' = CC.C,
+                   returnAttributes' = [],
+                   function' = Right (
+                     ConstantOperand (
+                       C.GlobalReference PointerType {
+                         pointerReferent = FunctionType {resultType = VoidType, argumentTypes = [], isVarArg = False},
+                         pointerAddrSpace = AddrSpace 0
+                       } (Name "_Z3quxv")
+                     )
+                   ),
+                   arguments' = [],
+                   returnDest = Name "exit",
+                   exceptionDest = Name "pad",
+                   metadata' = [],
+                   functionAttributes' = []
+                 }
+               ),
+               G.BasicBlock (Name "catchret") [] (
+                 Do CatchRet {
+                    catchPad = LocalReference TokenType (Name "cp"),
+                    successor = Name "exit",
+                    metadata' = []
+                 }
+               ),
+               G.BasicBlock (Name "pad") [] (
+                 Name "cs1" := CatchSwitch {
+                   parentPad' = ConstantOperand C.TokenNone,
+                   catchHandlers = (Name "catch" :| []),
+                   defaultUnwindDest = Nothing,
+                   metadata' = []
+                 }
+               ),
+               G.BasicBlock
+                 (Name "catch")
+                 [Name "cp" := CatchPad { catchSwitch = LocalReference TokenType (Name "cs1"), args = [ConstantOperand C.Int { C.integerBits = 7, C.integerValue = 4 }], metadata = [] }] (
+                 Do Br { dest = (Name "catchret"), metadata' = [] }
+               ),
+               G.BasicBlock (Name "exit") [] (Do Ret { returnOperand = Nothing, metadata' = [] })
+             ],
+             G.personalityFunction = Just (
+               C.GlobalReference PointerType {
+                 pointerReferent = FunctionType {resultType = IntegerType { typeBits = 32 }, argumentTypes = [], isVarArg = True},
+                 pointerAddrSpace = AddrSpace 0
+               } (Name "__gxx_personality_v0")
+             )
+           }
+         ]
+       },
+       "; ModuleID = '<string>'\n\
+       \source_filename = \"<string>\"\n\
+       \\n\
+       \declare void @_Z3quxv()\n\
+       \\n\
+       \declare i32 @__gxx_personality_v0(...)\n\
+       \\n\
+       \define void @catchret0() personality i32 (...)* @__gxx_personality_v0 {\n\
+       \entry:\n\
+       \  invoke void @_Z3quxv()\n\
+       \          to label %exit unwind label %pad\n\
+       \\n\
+       \catchret:                                         ; preds = %catch\n\
+       \  catchret from %cp to label %exit\n\
+       \\n\
+       \pad:                                              ; preds = %entry\n\
+       \  %cs1 = catchswitch within none [label %catch] unwind to caller\n\
+       \\n\
+       \catch:                                            ; preds = %pad\n\
+       \  %cp = catchpad within %cs1 [i7 4]\n\
+       \  br label %catchret\n\
+       \\n\
+       \exit:                                             ; preds = %catchret, %entry\n\
+       \  ret void\n\
+       \}\n"
+     )
+    ]
+   ]
+ ]
diff --git a/test/LLVM/Test/Instrumentation.hs b/test/LLVM/Test/Instrumentation.hs
new file mode 100644
--- /dev/null
+++ b/test/LLVM/Test/Instrumentation.hs
@@ -0,0 +1,159 @@
+module LLVM.Test.Instrumentation where
+
+import Test.Tasty
+import Test.Tasty.HUnit
+
+import LLVM.Test.Support
+
+import Control.Monad.Trans.Except 
+import Control.Monad.IO.Class
+
+import Data.Functor hiding (void)
+import qualified Data.List as List
+import qualified Data.Set as Set
+import qualified Data.Map as Map
+
+import LLVM.Module
+import LLVM.Context
+import LLVM.PassManager
+import LLVM.Transforms
+import LLVM.Target
+
+import LLVM.AST as A
+import LLVM.AST.Type
+import LLVM.AST.Name
+import LLVM.AST.AddrSpace
+import LLVM.AST.DataLayout
+import qualified LLVM.AST.IntegerPredicate as IPred
+import qualified LLVM.AST.Linkage as L
+import qualified LLVM.AST.Visibility as V
+import qualified LLVM.AST.CallingConvention as CC
+import qualified LLVM.AST.Attribute as A
+import qualified LLVM.AST.Global as G
+import qualified LLVM.AST.Constant as C
+
+instrument :: PassSetSpec -> A.Module -> IO A.Module
+instrument s m = withContext $ \context -> withModuleFromAST' context m $ \mIn' -> do
+  withPassManager s $ \pm -> runPassManager pm mIn'
+  moduleAST mIn'
+
+ast = do
+ dl <- withHostTargetMachine getTargetMachineDataLayout
+ triple <- liftIO getDefaultTargetTriple
+ return $ Module "<string>" "<string>" (Just dl) (Just triple) [
+  -- This function is needed for AddressSanitizerModule
+  GlobalDefinition $ functionDefaults {
+    G.returnType = void,
+    G.name = Name "asan.module_ctor",
+    G.basicBlocks = [BasicBlock (UnName 0) [] (Do (Ret Nothing []))]
+  },
+  GlobalDefinition $ functionDefaults {
+    G.returnType = i32,
+    G.name = Name "foo",
+    G.parameters = ([Parameter i128 (Name "x") []],False),
+    G.basicBlocks = [
+      BasicBlock (UnName 0) [] (Do $ Br (Name "checkDone") []),
+      BasicBlock (Name "checkDone") [
+        UnName 1 := Phi {
+         type' = i128,
+         incomingValues = [
+          (LocalReference i128 (Name "x"), UnName 0),
+          (LocalReference i128 (Name "x'"), Name "even"),
+          (LocalReference i128 (Name "x''"), Name "odd")
+         ],
+         metadata = []
+        },
+        Name "count" := Phi {
+         type' = i32,
+         incomingValues = [
+          (ConstantOperand (C.Int 32 1), UnName 0),
+          (LocalReference i32 (Name "count'"), Name "even"),
+          (LocalReference i32 (Name "count'"), Name "odd")
+         ],
+         metadata = []
+        },
+        Name "count'" := Add {
+         nsw = False,
+         nuw = False,
+         operand0 = LocalReference i32 (Name "count"),
+         operand1 = ConstantOperand (C.Int 32 1),
+         metadata = []
+        },
+        Name "is one" := ICmp {
+         iPredicate = IPred.EQ,
+         operand0 = LocalReference i128 (UnName 1),
+         operand1 = ConstantOperand (C.Int 128 1),
+         metadata = []
+        }
+      ] (
+        Do $ CondBr (LocalReference i1 (Name "is one")) (Name "done") (Name "checkOdd") []
+      ),
+      BasicBlock (Name "checkOdd") [
+        Name "is odd" := Trunc (LocalReference i128 (UnName 1)) i1 []
+      ] (
+       Do $ CondBr (LocalReference i1 (Name "is odd")) (Name "odd") (Name "even") []
+      ),
+      BasicBlock (Name "even") [
+        Name "x'" := UDiv True (LocalReference i128 (UnName 1)) (ConstantOperand (C.Int 128 2)) []
+      ] (
+        Do $ Br (Name "checkDone") []
+      ),
+      BasicBlock (Name "odd") [
+        UnName 2 := Mul False False (LocalReference i128 (UnName 1)) (ConstantOperand (C.Int 128 3)) [],
+        Name "x''" := Add False False (LocalReference i128 (UnName 2)) (ConstantOperand (C.Int 128 1)) []
+      ] (
+        Do $ Br (Name "checkDone") []
+      ),
+      BasicBlock (Name "done") [
+      ] (
+        Do $ Ret (Just (LocalReference i32 (Name "count'"))) []
+      )
+     ]
+   },
+  GlobalDefinition $ functionDefaults {
+    G.returnType = i32,
+    G.name = Name "main",
+    G.parameters = ([
+      Parameter i32 (Name "argc") [],
+      Parameter (ptr (ptr i8)) (Name "argv") []
+     ],False),
+    G.basicBlocks = [
+      BasicBlock (UnName 0) [
+        UnName 1 := Call {
+          tailCallKind = Nothing,
+          callingConvention = CC.C,
+          returnAttributes = [],
+          function = Right (ConstantOperand (C.GlobalReference (FunctionType i32 [i32, ptr (ptr i8)] False) (Name "foo"))),
+          arguments = [
+           (ConstantOperand (C.Int 128 9491828328), [])
+          ],
+          functionAttributes = [],
+          metadata = []
+        }
+      ] (
+        Do $ Ret (Just (LocalReference i32 (UnName 1))) []
+      )
+     ]
+   }
+  ]
+
+tests = testGroup "Instrumentation" [
+  testGroup "basic" [
+    testCase n $ do
+      triple <- getProcessTargetTriple 
+      withTargetLibraryInfo triple $ \tli -> do
+        Right dl <- runExceptT $ withHostTargetMachine getTargetMachineDataLayout
+        Right ast <- runExceptT ast
+        ast' <- instrument (defaultPassSetSpec { transforms = [p], dataLayout = Just dl, targetLibraryInfo = Just tli }) ast
+        let names ast = [ n | GlobalDefinition d <- moduleDefinitions ast, Name n <- return (G.name d) ]
+        (names ast') `List.intersect` (names ast) @?= names ast
+    | (n,p) <- [
+     ("GCOVProfiler", defaultGCOVProfiler),
+     ("AddressSanitizer", defaultAddressSanitizer),
+     ("AddressSanitizerModule", defaultAddressSanitizerModule),
+     ("MemorySanitizer", defaultMemorySanitizer),
+     ("ThreadSanitizer", defaultThreadSanitizer),
+     ("BoundsChecking", BoundsChecking)--,
+    ]
+   ]
+ ]
diff --git a/test/LLVM/Test/Linking.hs b/test/LLVM/Test/Linking.hs
new file mode 100644
--- /dev/null
+++ b/test/LLVM/Test/Linking.hs
@@ -0,0 +1,66 @@
+module LLVM.Test.Linking where
+
+import Test.Tasty
+import Test.Tasty.HUnit
+
+import LLVM.Test.Support
+
+import Control.Monad.Trans.Except
+import Data.Functor
+import qualified Data.Set as Set
+import qualified Data.Map as Map
+
+import LLVM.Module
+import LLVM.Context
+import LLVM.PassManager
+import LLVM.Transforms
+import LLVM.Target
+
+import LLVM.AST as A
+import LLVM.AST.Type
+import LLVM.AST.Name
+import LLVM.AST.AddrSpace
+import LLVM.AST.DataLayout
+import qualified LLVM.AST.IntegerPredicate as IPred
+import qualified LLVM.AST.Linkage as L
+import qualified LLVM.AST.Visibility as V
+import qualified LLVM.AST.CallingConvention as CC
+import qualified LLVM.AST.Attribute as A
+import qualified LLVM.AST.Global as G
+import qualified LLVM.AST.Constant as C
+
+tests = testGroup "Linking" [
+  testCase "basic" $ do
+    let 
+      ast0 = Module "<string>" "<string>" Nothing Nothing [
+          GlobalDefinition $ functionDefaults {
+             G.linkage = L.Private,
+             G.returnType = i32,
+             G.name = Name "private0"
+           },
+          GlobalDefinition $ functionDefaults {
+             G.linkage = L.External,
+             G.returnType = i32,
+             G.name = Name "external0"
+           }
+        ]
+      ast1 = Module "<string>" "<string>" Nothing Nothing [
+          GlobalDefinition $ functionDefaults {
+             G.linkage = L.Private,
+             G.returnType = i32,
+             G.name = Name "private1"
+           },
+          GlobalDefinition $ functionDefaults {
+             G.linkage = L.External,
+             G.returnType = i32,
+             G.name = Name "external1"
+           }
+        ]      
+
+    Module { moduleDefinitions = defs } <- withContext $ \context -> 
+      withModuleFromAST' context ast0 $ \dest -> do
+      withModuleFromAST' context ast0 $ \src -> do
+        failInIO $ linkModules dest src
+        moduleAST dest
+    [ n | GlobalDefinition g <- defs, let Name n = G.name g ] @?= [ "private0", "external0" ]
+ ]
diff --git a/test/LLVM/Test/Metadata.hs b/test/LLVM/Test/Metadata.hs
new file mode 100644
--- /dev/null
+++ b/test/LLVM/Test/Metadata.hs
@@ -0,0 +1,157 @@
+module LLVM.Test.Metadata where
+
+import Test.Tasty
+import Test.Tasty.HUnit
+
+import LLVM.Test.Support
+
+import LLVM.AST as A
+import LLVM.AST.Type as A.T
+import LLVM.AST.AddrSpace as A
+import qualified LLVM.AST.Linkage as L
+import qualified LLVM.AST.Visibility as V
+import qualified LLVM.AST.CallingConvention as CC
+import qualified LLVM.AST.Constant as C
+import LLVM.AST.Global as G
+
+tests = testGroup "Metadata" [
+  -- testCase "local" $ do
+  --   let ast = Module "<string>" Nothing Nothing [
+  --         GlobalDefinition $ globalVariableDefaults { G.name = UnName 0, G.type' = i32 },
+  --         GlobalDefinition $ functionDefaults {
+  --           G.returnType = i32,
+  --           G.name = Name "foo",
+  --           G.basicBlocks = [
+  --             BasicBlock (UnName 0) [
+  --                UnName 1 := Load {
+  --                           volatile = False,
+  --                           address = ConstantOperand (C.GlobalReference (ptr i32) (UnName 0)),
+  --                           maybeAtomicity = Nothing,
+  --                           A.alignment = 0,
+  --                           metadata = []
+  --                         }
+  --                ] (
+  --                Do $ Ret (Just (ConstantOperand (C.Int 32 0))) [
+  --                  (
+  --                    "my-metadatum", 
+  --                    MetadataNode [
+  --                     Just $ MDValue $ LocalReference i32 (UnName 1),
+  --                     Just $ MDString "super hyper",
+  --                     Nothing
+  --                    ]
+  --                  )
+  --                ]
+  --              )
+  --            ]
+  --          }
+  --        ]
+  --   let s = "; ModuleID = '<string>'\n\
+  --           \\n\
+  --           \@0 = external global i32\n\
+  --           \\n\
+  --           \define i32 @foo() {\n\
+  --           \  %1 = load i32, i32* @0\n\
+  --           \  ret i32 0, !my-metadatum !{i32 %1, !\"super hyper\", null}\n\
+  --           \}\n"
+  --   strCheck ast s,
+
+  testCase "global" $ do
+    let ast = Module "<string>" "<string>" Nothing Nothing [
+          GlobalDefinition $ functionDefaults {
+            G.returnType = i32,
+            G.name = Name "foo",
+            G.basicBlocks = [
+              BasicBlock (UnName 0) [
+              ] (
+                Do $ Ret (Just (ConstantOperand (C.Int 32 0))) [
+                  ("my-metadatum", MetadataNodeReference (MetadataNodeID 0))
+                ]
+              )
+             ]
+            },
+          MetadataNodeDefinition (MetadataNodeID 0) [ Just $ MDValue $ ConstantOperand (C.Int 32 1) ]
+         ]
+    let s = "; ModuleID = '<string>'\n\
+            \source_filename = \"<string>\"\n\
+            \\n\
+            \define i32 @foo() {\n\
+            \  ret i32 0, !my-metadatum !0\n\
+            \}\n\
+            \\n\
+            \!0 = !{i32 1}\n"
+    strCheck ast s,
+
+  testCase "named" $ do
+    let ast = Module "<string>" "<string>" Nothing Nothing [
+          NamedMetadataDefinition "my-module-metadata" [ MetadataNodeID 0 ],
+          MetadataNodeDefinition (MetadataNodeID 0) [ Just $ MDValue $ ConstantOperand (C.Int 32 1) ]
+         ]
+    let s = "; ModuleID = '<string>'\n\
+            \source_filename = \"<string>\"\n\
+            \\n\
+            \!my-module-metadata = !{!0}\n\
+            \\n\
+            \!0 = !{i32 1}\n"
+    strCheck ast s,
+
+  testCase "null" $ do
+    let ast = Module "<string>" "<string>" Nothing Nothing [
+          NamedMetadataDefinition "my-module-metadata" [ MetadataNodeID 0 ],
+          MetadataNodeDefinition (MetadataNodeID 0) [ Nothing ]
+         ]
+    let s = "; ModuleID = '<string>'\n\
+            \source_filename = \"<string>\"\n\
+            \\n\
+            \!my-module-metadata = !{!0}\n\
+            \\n\
+            \!0 = !{null}\n"
+    strCheck ast s,
+
+  testGroup "cyclic" [
+    testCase "metadata-only" $ do
+      let ast = Module "<string>" "<string>" Nothing Nothing [
+            NamedMetadataDefinition "my-module-metadata" [MetadataNodeID 0],
+            MetadataNodeDefinition (MetadataNodeID 0) [
+              Just $ MDNode (MetadataNodeReference (MetadataNodeID 1)) 
+             ],
+            MetadataNodeDefinition (MetadataNodeID 1) [
+              Just $ MDNode (MetadataNodeReference (MetadataNodeID 0)) 
+             ]
+           ]
+      let s = "; ModuleID = '<string>'\n\
+              \source_filename = \"<string>\"\n\
+              \\n\
+              \!my-module-metadata = !{!0}\n\
+              \\n\
+              \!0 = !{!1}\n\
+              \!1 = !{!0}\n"
+      strCheck ast s,
+
+    testCase "metadata-global" $ do
+      let ast = Module "<string>" "<string>" Nothing Nothing [
+            GlobalDefinition $ functionDefaults {
+              G.returnType = A.T.void,
+              G.name = Name "foo",
+              G.basicBlocks = [
+                BasicBlock (UnName 0) [
+                 ] (
+                   Do $ Ret Nothing [ ("my-metadatum", MetadataNodeReference (MetadataNodeID 0)) ]
+                 )
+               ]
+             },
+            MetadataNodeDefinition (MetadataNodeID 0) [
+              Just $ MDValue $ ConstantOperand (C.GlobalReference (ptr (FunctionType A.T.void [] False)) (Name "foo"))
+             ]
+           ]
+      let s = "; ModuleID = '<string>'\n\
+              \source_filename = \"<string>\"\n\
+              \\n\
+              \define void @foo() {\n\
+              \  ret void, !my-metadatum !0\n\
+              \}\n\
+              \\n\
+              \!0 = !{void ()* @foo}\n"
+      strCheck ast s
+   ]
+
+ ]
diff --git a/test/LLVM/Test/Module.hs b/test/LLVM/Test/Module.hs
new file mode 100644
--- /dev/null
+++ b/test/LLVM/Test/Module.hs
@@ -0,0 +1,574 @@
+module LLVM.Test.Module where
+
+import Test.Tasty
+import Test.Tasty.HUnit
+
+import LLVM.Test.Support
+
+import Control.Monad.Trans.Except
+import Data.Bits
+import Data.Word
+
+import qualified Data.Map as Map
+
+import LLVM.Context
+import LLVM.Module
+import LLVM.Analysis
+import LLVM.Diagnostic
+import LLVM.Target
+import LLVM.AST
+import LLVM.AST.Type as T
+import LLVM.AST.AddrSpace
+import qualified LLVM.AST.IntegerPredicate as IPred
+
+import qualified LLVM.AST.Linkage as L
+import qualified LLVM.AST.Visibility as V
+import qualified LLVM.AST.DLL as DLL
+import qualified LLVM.AST.CallingConvention as CC
+import qualified LLVM.AST.FunctionAttribute as FA
+import qualified LLVM.AST.ParameterAttribute as PA
+import qualified LLVM.AST.ThreadLocalStorage as TLS
+import qualified LLVM.AST.Global as G
+import qualified LLVM.AST.Constant as C
+import qualified LLVM.AST.COMDAT as COMDAT
+
+import qualified LLVM.Relocation as R
+import qualified LLVM.CodeModel as CM
+import qualified LLVM.CodeGenOpt as CGO
+
+handString = "; ModuleID = '<string>'\n\
+    \source_filename = \"<string>\"\n\
+    \\n\
+    \%0 = type { i32, %1*, %0* }\n\
+    \%1 = type opaque\n\
+    \\n\
+    \$bob = comdat largest\n\
+    \\n\
+    \@0 = global i32 1\n\
+    \@1 = external protected addrspace(3) global i32, section \"foo\", comdat($bob)\n\
+    \@2 = unnamed_addr global i8 2\n\
+    \@3 = external dllimport global %0\n\
+    \@4 = external global [4294967296 x i32]\n\
+    \@.argyle = thread_local global i32 0\n\
+    \@5 = thread_local(localdynamic) global i32 1\n\
+    \\n\
+    \@three = private alias i32, i32 addrspace(3)* @1\n\
+    \@two = unnamed_addr alias i32, i32 addrspace(3)* @three\n\
+    \@one = thread_local(initialexec) alias i32, i32* @5\n\
+    \\n\
+    \define i32 @bar() prefix i32 1 {\n\
+    \  %1 = musttail call zeroext i32 @foo(i32 inreg align 16 1, i8 signext 4) #0\n\
+    \  ret i32 %1\n\
+    \}\n\
+    \\n\
+    \; Function Attrs: nounwind readnone uwtable\n\
+    \define zeroext i32 @foo(i32 inreg align 16 %x, i8 signext %y) #0 {\n\
+    \  %1 = mul nsw i32 %x, %x\n\
+    \  br label %here\n\
+    \\n\
+    \here:                                             ; preds = %0\n\
+    \  %go = icmp eq i32 %1, %x\n\
+    \  br i1 %go, label %there, label %elsewhere\n\
+    \\n\
+    \there:                                            ; preds = %here\n\
+    \  %2 = add nsw i32 %1, 3\n\
+    \  br label %elsewhere\n\
+    \\n\
+    \elsewhere:                                        ; preds = %there, %here\n\
+    \  %r = phi i32 [ 2, %there ], [ 57, %here ]\n\
+    \  ret i32 %r\n\
+    \}\n\
+    \\n\
+    \attributes #0 = { nounwind readnone uwtable \"eep\" }\n"
+
+handAST = Module "<string>" "<string>" Nothing Nothing [
+      TypeDefinition (UnName 0) (
+         Just $ StructureType False [
+           i32,
+           ptr (NamedTypeReference (UnName 1)),
+           ptr (NamedTypeReference (UnName 0))
+          ]),
+      TypeDefinition (UnName 1) Nothing,
+      GlobalDefinition $ globalVariableDefaults {
+        G.name = UnName 0,
+        G.type' = i32,
+        G.initializer = Just (C.Int 32 1)
+      },
+      GlobalDefinition $ globalVariableDefaults {
+        G.name = UnName 1,
+        G.visibility = V.Protected,
+        G.type' = i32,
+        G.addrSpace = AddrSpace 3,
+        G.section = Just "foo",
+        G.comdat = Just "bob"
+      },
+      GlobalDefinition $ globalVariableDefaults {
+        G.name = UnName 2,
+        G.unnamedAddr = Just GlobalAddr,
+        G.type' = i8,
+        G.initializer = Just (C.Int 8 2)
+      },
+      GlobalDefinition $ globalVariableDefaults {
+        G.name = UnName 3,
+        G.dllStorageClass = Just DLL.Import,
+        G.type' = NamedTypeReference (UnName 0)
+      },
+      GlobalDefinition $ globalVariableDefaults {
+        G.name = UnName 4,
+        G.type' = ArrayType (1 `shift` 32) i32
+      },
+      GlobalDefinition $ globalVariableDefaults {
+        G.name = Name ".argyle",
+        G.type' = i32,
+        G.initializer = Just (C.Int 32 0),
+        G.threadLocalMode = Just TLS.GeneralDynamic
+      },
+      GlobalDefinition $ globalVariableDefaults {
+        G.name = UnName 5,
+        G.type' = i32,
+        G.threadLocalMode = Just TLS.LocalDynamic,
+        G.initializer = Just (C.Int 32 1)
+      },
+      GlobalDefinition $ globalAliasDefaults {
+         G.name = Name "three",
+         G.linkage = L.Private,
+         G.type' = PointerType i32 (AddrSpace 3),
+         G.aliasee = C.GlobalReference (PointerType i32 (AddrSpace 3)) (UnName 1)
+      },
+      GlobalDefinition $ globalAliasDefaults {
+        G.name = Name "two",
+        G.unnamedAddr = Just GlobalAddr,
+        G.type' = PointerType i32 (AddrSpace 3),
+        G.aliasee = C.GlobalReference (PointerType i32 (AddrSpace 3)) (Name "three")
+      },
+      GlobalDefinition $ globalAliasDefaults {
+        G.name = Name "one",
+        G.type' = ptr i32,
+        G.aliasee = C.GlobalReference (ptr i32) (UnName 5),
+        G.threadLocalMode = Just TLS.InitialExec
+      },
+      GlobalDefinition $ functionDefaults {
+        G.returnType = i32,
+        G.name = Name "bar",
+        G.prefix = Just (C.Int 32 1),
+        G.basicBlocks = [
+          BasicBlock (UnName 0) [
+           UnName 1 := Call {
+             tailCallKind = Just MustTail,
+             callingConvention = CC.C,
+             returnAttributes = [PA.ZeroExt],
+             function = Right (ConstantOperand (C.GlobalReference (ptr (FunctionType i32 [i32, i8] False)) (Name "foo"))),
+             arguments = [
+              (ConstantOperand (C.Int 32 1), [PA.InReg, PA.Alignment 16]),
+              (ConstantOperand (C.Int 8 4), [PA.SignExt])
+             ],
+             functionAttributes = [Left (FA.GroupID 0)],
+             metadata = []
+           }
+         ] (
+           Do $ Ret (Just (LocalReference i32 (UnName 1))) []
+         )
+        ]
+      },
+      GlobalDefinition $ functionDefaults {
+        G.returnAttributes = [PA.ZeroExt],
+        G.returnType = i32,
+        G.name = Name "foo",
+        G.parameters = ([
+          Parameter i32 (Name "x") [PA.InReg, PA.Alignment 16],
+          Parameter i8 (Name "y") [PA.SignExt]
+         ], False),
+        G.functionAttributes = [Left (FA.GroupID 0)],
+        G.basicBlocks = [
+          BasicBlock (UnName 0) [
+           UnName 1 := Mul {
+             nsw = True,
+             nuw = False,
+             operand0 = LocalReference i32 (Name "x"),
+             operand1 = LocalReference i32 (Name "x"),
+             metadata = []
+           }
+           ] (
+             Do $ Br (Name "here") []
+           ),
+          BasicBlock (Name "here") [
+           Name "go" := ICmp {
+             iPredicate = IPred.EQ,
+             operand0 = LocalReference i32 (UnName 1),
+             operand1 = LocalReference i32 (Name "x"),
+             metadata = []
+           }
+           ] (
+              Do $ CondBr {
+                condition = LocalReference i1 (Name "go"),
+                trueDest = Name "there",
+                falseDest = Name "elsewhere",
+                metadata' = []
+              }
+           ),
+          BasicBlock (Name "there") [
+           UnName 2 := Add {
+             nsw = True,
+             nuw = False,
+             operand0 = LocalReference i32 (UnName 1),
+             operand1 = ConstantOperand (C.Int 32 3),
+             metadata = []
+           }
+           ] (
+             Do $ Br (Name "elsewhere") []
+           ),
+          BasicBlock (Name "elsewhere") [
+           Name "r" := Phi {
+             type' = i32,
+             incomingValues = [
+               (ConstantOperand (C.Int 32 2), Name "there"),
+               (ConstantOperand (C.Int 32 57), Name "here")
+             ],
+             metadata = []
+           }
+           ] (
+             Do $ Ret (Just (LocalReference i32 (Name "r"))) []
+           )
+         ]
+        },
+      FunctionAttributes (FA.GroupID 0) [FA.NoUnwind, FA.ReadNone, FA.UWTable, FA.StringAttribute "eep" ""],
+      COMDAT "bob" COMDAT.Largest
+     ]
+
+tests = testGroup "Module" [
+  testGroup "withModuleFromString" [
+    testCase "basic" $ withContext $ \context -> do
+      z <- withModuleFromLLVMAssembly' context handString (const $ return 0)
+      z @?= 0,
+    testCase "numbering" $ withContext $ \context -> do
+      let s = "@0 = global i32 3\
+              \define i32 @1(i32 %x) {\n\
+              \  %1 = mul i32 %x, %x\n\
+              \  %2 = add i32 %1, 3\n\
+              \  ret i32 %2\n\
+              \}\n"
+      z <- withModuleFromLLVMAssembly' context s (const $ return 0)
+      z @?= 0
+   ],
+
+  testGroup "emit" [
+    testCase "assemble" $ withContext $ \context -> do
+      let s = "define i32 @main(i32 %argc, i8** %argv) {\n\
+              \  ret i32 0\n\
+              \}\n"
+      a <- withModuleFromLLVMAssembly' context s $ \m -> do
+        (t, _) <- failInIO $ lookupTarget Nothing "x86_64-unknown-linux"
+        withTargetOptions $ \to -> do
+          withTargetMachine t "x86_64-unknown-linux" "" Map.empty to R.Default CM.Default CGO.Default $ \tm -> do
+            failInIO $ moduleTargetAssembly tm m
+      a @?= "\t.text\n\
+            \\t.file\t\"<string>\"\n\
+            \\t.globl\tmain\n\
+            \\t.p2align\t4, 0x90\n\
+            \\t.type\tmain,@function\n\
+            \main:\n\
+            \\t.cfi_startproc\n\
+            \\txorl\t%eax, %eax\n\
+            \\tretq\n\
+            \.Lfunc_end0:\n\
+            \\t.size\tmain, .Lfunc_end0-main\n\
+            \\t.cfi_endproc\n\
+            \\n\
+            \\n\
+            \\t.section\t\".note.GNU-stack\",\"\",@progbits\n"
+   ],
+
+  testCase "handStringIsCanonical" $ withContext $ \context -> do
+    s <- withModuleFromLLVMAssembly' context handString moduleLLVMAssembly
+    s @?= handString,
+
+  testCase "moduleAST" $ withContext $ \context -> do
+    ast <- withModuleFromLLVMAssembly' context handString moduleAST
+    ast @?= handAST,
+    
+  testCase "withModuleFromAST" $ withContext $ \context -> do
+    s <- withModuleFromAST' context handAST moduleLLVMAssembly
+    s @?= handString,
+
+  testCase "bitcode" $ withContext $ \context -> do
+    bs <- withModuleFromAST' context handAST moduleBitcode
+    s <- withModuleFromBitcode' context bs moduleLLVMAssembly
+    s @?= handString,
+
+  testCase "triple" $ withContext $ \context -> do
+   let hAST = "; ModuleID = '<string>'\n\
+              \target triple = \"x86_64-unknown-linux\"\n"
+   ast <- withModuleFromLLVMAssembly' context hAST moduleAST
+   ast @?= defaultModule { moduleTargetTriple = Just "x86_64-unknown-linux" },
+
+  testGroup "regression" [
+    testCase "minimal type info" $ withContext $ \context -> do
+      let s = "; ModuleID = '<string>'\n\
+              \source_filename = \"<string>\"\n\
+              \\n\
+              \define void @trouble() {\n\
+              \entry:\n\
+              \  ret void\n\
+              \\n\
+              \dead0:                                            ; preds = %dead1\n\
+              \  %x0 = add i32 %x1, %x1\n\
+              \  br label %dead1\n\
+              \\n\
+              \dead1:                                            ; preds = %dead0\n\
+              \  %x1 = add i32 %x0, %x0\n\
+              \  br label %dead0\n\
+              \}\n"
+          ast = Module "<string>" "<string>" Nothing Nothing [
+             GlobalDefinition $ functionDefaults {
+                G.returnType = T.void,
+                G.name = Name "trouble",
+                G.basicBlocks = [
+                 BasicBlock (Name "entry") [
+                  ] (
+                   Do $ Ret Nothing []
+                  ),
+                 BasicBlock (Name "dead0") [
+                   Name "x0" := Add {
+                     nsw = False,
+                     nuw = False,
+                     operand0 = LocalReference i32 (Name "x1"),
+                     operand1 = LocalReference i32 (Name "x1"),
+                     metadata = []
+                   }
+                  ] (
+                   Do $ Br (Name "dead1") []
+                  ),
+                 BasicBlock (Name "dead1") [
+                   Name "x1" := Add {
+                     nsw = False,
+                     nuw = False,
+                     operand0 = LocalReference i32 (Name "x0"),
+                     operand1 = LocalReference i32 (Name "x0"),
+                     metadata = []
+                   }
+                  ] (
+                   Do $ Br (Name "dead0") []
+                  )
+                 ]
+              }
+            ]
+      strCheck ast s
+      s' <- withContext $ \context -> withModuleFromAST' context ast $ runExceptT . verify
+      s' @?= Right (),
+
+    testCase "metadata type" $ withContext $ \context -> do
+      let s = "; ModuleID = '<string>'\n\
+              \source_filename = \"<string>\"\n\
+              \\n\
+              \define void @bar(metadata) {\n\
+              \  ret void\n\
+              \}\n"
+          ast = Module "<string>" "<string>" Nothing Nothing [
+             GlobalDefinition $ functionDefaults {
+               G.returnType = void,
+               G.name = Name "bar",
+               G.parameters = ([Parameter MetadataType (UnName 0) []], False),
+               G.basicBlocks = [
+                 BasicBlock (UnName 1) [] (Do $ Ret Nothing [])
+                ]
+             }
+           ]
+      strCheck ast s,
+
+    testCase "set flag on constant expr" $ withContext $ \context -> do
+      let ast = Module "<string>" "<string>" Nothing Nothing [
+             GlobalDefinition $ functionDefaults {
+               G.returnType = i32,
+               G.name = Name "foo",
+               G.parameters = ([Parameter i32 (Name "x") []], False),
+               G.basicBlocks = [
+                 BasicBlock (UnName 0) [
+                  UnName 1 := Mul {
+                    nsw = True,
+                    nuw = False,
+                    operand0 = ConstantOperand (C.Int 32 1),
+                    operand1 = ConstantOperand (C.Int 32 1),
+                    metadata = []
+                  }
+                  ] (
+                    Do $ Br (Name "here") []
+                  ),
+                 BasicBlock (Name "here") [
+                  ] (
+                    Do $ Ret (Just (LocalReference i32 (UnName 1))) []
+                  )
+                ]
+             }
+           ]
+      t <- withModuleFromAST' context ast $ \_ -> return True
+      t @?= True,
+
+    testCase "Phi node finishes" $ withContext $ \context -> do
+      let ast = Module "<string>" "<string>" Nothing Nothing [
+            GlobalDefinition $ functionDefaults {
+              G.returnType = i32,
+              G.name = Name "foo",
+              G.parameters = ([Parameter i32 (Name "x") []], False),
+              G.basicBlocks = [
+                BasicBlock (UnName 0) [
+                 UnName 1 := Mul {
+                   nsw = True,
+                   nuw = False,
+                   operand0 = LocalReference i32 (Name "x"),
+                   operand1 = LocalReference i32 (Name "x"),
+                   metadata = []
+                 }
+                 ] (
+                   Do $ Br (Name "here") []
+                 ),
+                BasicBlock (Name "here") [
+                 UnName 2 := Phi i32 [ (ConstantOperand (C.Int 32 42), UnName 0) ] []
+                 ] (
+                   Do $ Br (Name "elsewhere") []
+                 ),
+                BasicBlock (Name "elsewhere") [             
+                 ] (
+                   Do $ Br (Name "there") []
+                 ),
+                BasicBlock (Name "there") [
+                 ] (
+                   Do $ Ret (Just (LocalReference i32 (UnName 1))) []
+                 )
+               ]
+             }
+           ]
+          s = "; ModuleID = '<string>'\n\
+              \source_filename = \"<string>\"\n\
+              \\n\
+              \define i32 @foo(i32 %x) {\n\
+              \  %1 = mul nsw i32 %x, %x\n\
+              \  br label %here\n\
+              \\n\
+              \here:                                             ; preds = %0\n\
+              \  %2 = phi i32 [ 42, %0 ]\n\
+              \  br label %elsewhere\n\
+              \\n\
+              \elsewhere:                                        ; preds = %here\n\
+              \  br label %there\n\
+              \\n\
+              \there:                                            ; preds = %elsewhere\n\
+              \  ret i32 %1\n\
+              \}\n"
+      strCheck ast s,
+
+      testCase "switchblock" $ do
+        let count = 450
+            start = 2 -- won't come back the same w/o start = 2
+            ns = [start..count + start - 1]
+            vbps = zip [ ConstantOperand (C.Int 32 i) | i <- [0..] ] [ UnName n | n <- (1:ns) ]
+            cbps = zip [ C.Int 32 i | i <- [0..] ] [ UnName n | n <- ns ]
+
+        withContext $ \context -> do
+          let ast = Module "<string>" "<string>" Nothing Nothing [
+                GlobalDefinition $ functionDefaults {
+                  G.name = Name "foo",
+                  G.returnType = i32,
+                  G.parameters = ([Parameter i32 (UnName 0) []], False),
+                  G.basicBlocks = [
+                   BasicBlock (UnName 1) [] (Do $ Switch (LocalReference i32 (UnName 0)) (Name "end") cbps [])
+                  ] ++ [
+                   BasicBlock (UnName n) [] (Do $ Br (Name "end") []) | n <- ns
+                  ] ++ [
+                   BasicBlock (Name "end") [
+                     Name "val" := Phi i32 vbps []
+                   ] (
+                     Do $ Ret (Just (LocalReference i32 (Name "val"))) []
+                   )
+                  ]
+                }
+               ]
+          s <- withModuleFromAST' context ast moduleLLVMAssembly
+          m <- withModuleFromLLVMAssembly' context s moduleAST
+          m @?= ast,
+
+      testCase "struct constant" $ do
+        let s = "; ModuleID = '<string>'\n\
+                \source_filename = \"<string>\"\n\
+                \\n\
+                \%0 = type { i32 }\n\
+                \\n\
+                \@0 = constant %0 { i32 1 }, align 4\n"
+            ast = Module "<string>" "<string>" Nothing Nothing [
+              TypeDefinition (UnName 0) (Just $ StructureType False [i32]),
+              GlobalDefinition $ globalVariableDefaults {
+                G.name = UnName 0,
+                G.isConstant = True,
+                G.type' = NamedTypeReference (UnName 0),
+                G.initializer = Just $ C.Struct (Just $ UnName 0) False [ C.Int 32 1 ],
+                G.alignment = 4
+              }
+             ]
+        strCheck ast s
+   ],
+        
+  testGroup "failures" [
+    testCase "bad block reference" $ withContext $ \context -> do
+      let badAST = Module "<string>" "<string>" Nothing Nothing [
+            GlobalDefinition $ functionDefaults {
+              G.returnType = i32,
+              G.name = Name "foo",
+              G.parameters = ([Parameter i32 (Name "x") []], False),
+              G.basicBlocks = [
+                BasicBlock (UnName 0) [
+                 UnName 1 := Mul {
+                   nsw = True,
+                   nuw = False,
+                   operand0 = ConstantOperand (C.Int 32 1),
+                   operand1 = ConstantOperand (C.Int 32 1),
+                   metadata = []
+                 }
+                 ] (
+                   Do $ Br (Name "not here") []
+                 ),
+                BasicBlock (Name "here") [
+                 ] (
+                   Do $ Ret (Just (LocalReference i32 (UnName 1))) []
+                 )
+               ]
+             }
+           ]
+      t <- runExceptT $ withModuleFromAST context badAST $ \_ -> return True
+      t @?= Left "reference to undefined block: Name \"not here\"",
+
+    testCase "multiple" $ withContext $ \context -> do
+      let badAST = Module "<string>" "<string>" Nothing Nothing [
+            GlobalDefinition $ functionDefaults {
+              G.returnType = i32,
+              G.name = Name "foo",
+              G.basicBlocks = [
+                BasicBlock (UnName 0) [
+                 UnName 1 := Mul {
+                   nsw = False,
+                   nuw = False,
+                   operand0 = LocalReference i32 (Name "unknown"),
+                   operand1 = ConstantOperand (C.Int 32 1),
+                   metadata = []
+                 },
+                 UnName 2 := Mul {
+                   nsw = False,
+                   nuw = False,
+                   operand0 = LocalReference i32 (Name "unknown2"),
+                   operand1 = LocalReference i32 (UnName 1),
+                   metadata = []
+                 }
+                 ] (
+                   Do $ Ret (Just (LocalReference i32 (UnName 2))) []
+                 )
+               ]
+             }
+           ]
+      t <- runExceptT $ withModuleFromAST context badAST $ \_ -> return True
+      t @?= Left "reference to undefined local: Name \"unknown\"",
+
+    testCase "sourceFileName" $ withContext $ \context -> do
+      let s = "; ModuleID = '<string>'\n\
+              \source_filename = \"filename\"\n"
+          ast = Module "<string>" "filename" Nothing Nothing []
+      strCheck ast s
+   ]
+ ]
diff --git a/test/LLVM/Test/ObjectCode.hs b/test/LLVM/Test/ObjectCode.hs
new file mode 100644
--- /dev/null
+++ b/test/LLVM/Test/ObjectCode.hs
@@ -0,0 +1,35 @@
+module LLVM.Test.ObjectCode where
+
+import Test.Tasty
+import Test.Tasty.HUnit
+
+import LLVM.Test.Support
+
+import qualified Data.ByteString as ByteString
+import System.IO
+import System.IO.Temp
+
+import LLVM.Context
+import LLVM.Module
+import LLVM.Target
+
+ll :: String
+ll = unlines ["define i32 @main(i32 %argc, i8** %argv) {", "  ret i32 0", "}"]
+
+tests =
+  testGroup
+    "Object code serialization"
+    [ testCase
+        "serialization to ByteString and to file" $ do
+        withContext $ \ctx ->
+          withSystemTempFile "foo" $ \objFile handle -> do
+            hClose handle
+            failInIO $
+              withHostTargetMachine $ \machine ->
+                failInIO $
+                withModuleFromLLVMAssembly ctx ll $ \mdl -> do
+                  obj <- failInIO $ moduleObject machine mdl
+                  _ <- failInIO $ writeObjectToFile machine (File objFile) mdl
+                  obj' <- ByteString.readFile objFile
+                  obj @=? obj'
+    ]
diff --git a/test/LLVM/Test/Optimization.hs b/test/LLVM/Test/Optimization.hs
new file mode 100644
--- /dev/null
+++ b/test/LLVM/Test/Optimization.hs
@@ -0,0 +1,306 @@
+module LLVM.Test.Optimization where
+
+import Test.Tasty
+import Test.Tasty.HUnit
+
+import LLVM.Test.Support
+
+import Data.Functor
+import qualified Data.Set as Set
+import qualified Data.Map as Map
+
+import LLVM.Module
+import LLVM.Context
+import LLVM.PassManager
+import qualified LLVM.Transforms as T
+import LLVM.Target
+
+import LLVM.AST as A
+import LLVM.AST.Type as A.T
+import LLVM.AST.Name
+import LLVM.AST.AddrSpace
+import LLVM.AST.DataLayout
+import qualified LLVM.AST.IntegerPredicate as IPred
+import qualified LLVM.AST.Linkage as L
+import qualified LLVM.AST.Visibility as V
+import qualified LLVM.AST.CallingConvention as CC
+import qualified LLVM.AST.Attribute as A
+import qualified LLVM.AST.Global as G
+import qualified LLVM.AST.Constant as C
+
+import qualified LLVM.Relocation as R
+import qualified LLVM.CodeModel as CM
+import qualified LLVM.CodeGenOpt as CGO
+
+handAST = 
+  Module "<string>" "<string>" Nothing Nothing [
+      GlobalDefinition $ functionDefaults {
+        G.returnType = i32,
+        G.name = Name "foo",
+        G.parameters = ([Parameter i32 (Name "x") []], False),
+        G.functionAttributes = [Left (A.GroupID 0)],
+        G.basicBlocks = [
+          BasicBlock (UnName 0) [
+           UnName 1 := Mul {
+             nsw = False,
+             nuw = False,
+             operand0 = ConstantOperand (C.Int 32 6),
+             operand1 = ConstantOperand (C.Int 32 7),
+             metadata = []
+           }
+           ] (
+             Do $ Br (Name "here") []
+           ),
+          BasicBlock (Name "here") [
+           Name "go" := ICmp {
+             iPredicate = IPred.EQ,
+             operand0 = LocalReference i32 (UnName 1),
+             operand1 = ConstantOperand (C.Int 32 42),
+             metadata = []
+           }
+           ] (
+              Do $ CondBr {
+                condition = LocalReference i1 (Name "go"),
+                trueDest = Name "take",
+                falseDest = Name "done",
+                metadata' = []
+              }
+           ),
+          BasicBlock (Name "take") [
+           UnName 2 := Sub {
+             nsw = False,
+             nuw = False,
+             operand0 = LocalReference i32 (Name "x"),
+             operand1 = LocalReference i32 (Name "x"),
+             metadata = []
+           }
+           ] (
+             Do $ Br (Name "done") []
+           ),
+          BasicBlock (Name "done") [
+           Name "r" := Phi {
+             type' = i32,
+             incomingValues = [
+               (LocalReference i32 (UnName 2), Name "take"),
+               (ConstantOperand (C.Int 32 57), Name "here")
+             ],
+             metadata = []
+           }
+           ] (
+             Do $ Ret (Just (LocalReference i32 (Name "r"))) []
+           )
+         ]
+       },
+      FunctionAttributes (A.GroupID 0) [A.NoUnwind, A.ReadNone, A.UWTable]
+     ]
+
+isVectory :: A.Module -> Assertion
+isVectory Module { moduleDefinitions = ds } =
+  (@? "Module is not vectory") $ not $ null [ i 
+    | GlobalDefinition (Function { G.basicBlocks = b }) <- ds,
+      BasicBlock _ is _ <- b,
+      _ := i@(ExtractElement {}) <- is
+   ]
+
+optimize :: PassSetSpec -> A.Module -> IO A.Module
+optimize pss m = withContext $ \context -> withModuleFromAST' context m $ \mIn' -> do
+  withPassManager pss $ \pm -> runPassManager pm mIn'
+  moduleAST mIn'
+
+tests = testGroup "Optimization" [
+  testCase "curated" $ do
+    mOut <- optimize defaultCuratedPassSetSpec handAST
+
+    mOut @?= Module "<string>" "<string>" Nothing Nothing [
+      GlobalDefinition $ functionDefaults {
+        G.returnType = i32,
+         G.name = Name "foo",
+         G.parameters = ([Parameter i32 (Name "x") []], False),
+         G.functionAttributes = [Left (A.GroupID 0)],
+         G.basicBlocks = [
+           BasicBlock (Name "here") [
+              ] (
+              Do $ Ret (Just (ConstantOperand (C.Int 32 0))) []
+            )
+          ]
+        },
+      FunctionAttributes (A.GroupID 0) [A.NoRecurse, A.NoUnwind, A.ReadNone, A.UWTable]
+      ],
+
+  testGroup "individual" [
+    testCase "ConstantPropagation" $ do
+      mOut <- optimize defaultPassSetSpec { transforms = [T.ConstantPropagation] } handAST
+
+      mOut @?= Module "<string>" "<string>" Nothing Nothing [
+        GlobalDefinition $ functionDefaults {
+          G.returnType = i32,
+          G.name = Name "foo",
+          G.parameters = ([Parameter i32 (Name "x") []], False),
+          G.functionAttributes = [Left (A.GroupID 0)],
+          G.basicBlocks = [
+            BasicBlock (UnName 0) [] (Do $ Br (Name "here") []),
+            BasicBlock (Name "here") [] (
+               Do $ CondBr {
+                 condition = ConstantOperand (C.Int 1 1),
+                 trueDest = Name "take", 
+                 falseDest = Name "done",
+                 metadata' = []
+               }
+            ),
+            BasicBlock (Name "take") [
+             UnName 1 := Sub {
+               nsw = False,
+               nuw = False,
+               operand0 = LocalReference i32 (Name "x"),
+               operand1 = LocalReference i32 (Name "x"),
+               metadata = []
+              }
+            ] (
+             Do $ Br (Name "done") []
+            ),
+            BasicBlock (Name "done") [
+             Name "r" := Phi {
+               type' = i32,
+               incomingValues = [(LocalReference i32 (UnName 1), Name "take"),(ConstantOperand (C.Int 32 57), Name "here")],
+               metadata = []
+              }
+            ] (
+              Do $ Ret (Just (LocalReference i32 (Name "r"))) []
+            )
+           ]
+         },
+        FunctionAttributes (A.GroupID 0) [A.NoUnwind, A.ReadNone, A.UWTable]
+       ],
+
+    testCase "BasicBlockVectorization" $ do
+      let
+        mIn = Module "<string>" "<string>" Nothing Nothing [
+          GlobalDefinition $ functionDefaults {
+           G.returnType = double,
+            G.name = Name "foo",
+            G.parameters = ([
+              Parameter double (Name (l ++ n)) []
+                | l <- [ "a", "b" ], n <- ["1", "2"]
+             ], False),
+            G.basicBlocks = [
+              BasicBlock (UnName 0) ([
+                Name (l ++ n) := op NoFastMathFlags (LocalReference double (Name (o1 ++ n))) (LocalReference double (Name (o2 ++ n))) []
+                | (l, op, o1, o2) <- [
+                   ("x", FSub, "a", "b"),
+                   ("y", FMul, "x", "a"),
+                   ("z", FAdd, "y", "b")],
+                  n <- ["1", "2"]
+               ] ++ [
+                Name "r" := FMul NoFastMathFlags (LocalReference double (Name "z1")) (LocalReference double (Name "z2")) []
+              ]) (Do $ Ret (Just (LocalReference double (Name "r"))) [])
+             ]
+          }
+         ]
+      mOut <- optimize (defaultPassSetSpec {
+                    transforms = [
+                     T.defaultVectorizeBasicBlocks { T.requiredChainDepth = 3 },
+                     T.InstructionCombining, 
+                     T.GlobalValueNumbering False
+                    ] }) mIn
+      isVectory mOut,
+      
+    testCase "LoopVectorize" $ do
+      let
+        mIn = 
+          Module {
+            moduleName = "<string>",
+            moduleSourceFileName = "<string>",
+            moduleDataLayout = Just $ (defaultDataLayout BigEndian) { 
+              typeLayouts = Map.singleton (VectorAlign, 128) (AlignmentInfo 128 Nothing)
+             },
+            moduleTargetTriple = Just "x86_64",
+            moduleDefinitions = [
+              GlobalDefinition $ globalVariableDefaults {
+                G.name = Name "a",
+                G.linkage = L.Common,
+                G.type' = A.T.ArrayType 2048 i32,
+                G.initializer = Just (C.Null (A.T.ArrayType 2048 i32))
+               },
+              GlobalDefinition $ functionDefaults {
+                G.returnType = A.T.void,
+                G.name = Name "inc",
+                G.functionAttributes = [Left (A.GroupID 0)],
+                G.parameters = ([Parameter i32 (Name "n") []], False),
+                G.basicBlocks = [
+                  BasicBlock (UnName 0) [
+                    UnName 1 := ICmp IPred.SGT (LocalReference i32 (Name "n")) (ConstantOperand (C.Int 32 0)) []
+                   ] (Do $ CondBr (LocalReference i1 (UnName 1)) (Name ".lr.ph") (Name "._crit_edge") []),
+                  BasicBlock (Name ".lr.ph") [
+                    Name "indvars.iv" := Phi i64 [ 
+                      (ConstantOperand (C.Int 64 0), UnName 0),
+                      (LocalReference i64 (Name "indvars.iv.next"), Name ".lr.ph")
+                     ] [],
+                    UnName 2 := GetElementPtr True (ConstantOperand (C.GlobalReference (A.T.ArrayType 2048 i32) (Name "a"))) [ 
+                      ConstantOperand (C.Int 64 0),
+                      (LocalReference i64 (Name "indvars.iv"))
+                     ] [],
+                    UnName 3 := Load False (LocalReference (ptr i32) (UnName 2)) Nothing 4 [],
+                    UnName 4 := Trunc (LocalReference i64 (Name "indvars.iv")) i32 [],
+                    UnName 5 := Add True False (LocalReference i32 (UnName 3)) (LocalReference i32 (UnName 4)) [],
+                    Do $ Store False (LocalReference (ptr i32) (UnName 2)) (LocalReference i32 (UnName 5)) Nothing 4 [],
+                    Name "indvars.iv.next" := Add False False (LocalReference i64 (Name "indvars.iv")) (ConstantOperand (C.Int 64 1)) [],
+                    Name "lftr.wideiv" := Trunc (LocalReference i64 (Name "indvars.iv.next")) i32 [],
+                    Name "exitcond" := ICmp IPred.EQ (LocalReference i32 (Name "lftr.wideiv")) (LocalReference i32 (Name "n")) []
+                   ] (Do $ CondBr (LocalReference i1 (Name "exitcond")) (Name "._crit_edge") (Name ".lr.ph") []),
+                  BasicBlock (Name "._crit_edge") [
+                   ] (Do $ Ret Nothing [])
+                 ]
+               },
+              FunctionAttributes (A.GroupID 0) [A.NoUnwind, A.ReadNone, A.UWTable, A.StackProtect]
+             ]
+           }
+      mOut <- do
+        let triple = "x86_64"
+        (target, _) <- failInIO $ lookupTarget Nothing triple
+        withTargetOptions $ \targetOptions -> do
+          withTargetMachine target triple "" Map.empty targetOptions R.Default CM.Default CGO.Default $ \tm -> do
+            optimize (defaultPassSetSpec { 
+                        transforms = [ T.defaultLoopVectorize ],
+                        dataLayout = moduleDataLayout mIn,
+                        targetMachine = Just tm
+                      }) mIn
+      isVectory mOut,
+
+    testCase "LowerInvoke" $ do
+      -- This test doesn't test much about what LowerInvoke does, just that it seems to work.
+      -- The pass seems to be quite deeply dependent on weakly documented presumptions about
+      -- how unwinding works (as is the invoke instruction)
+      withContext $ \context -> do
+        withPassManager (defaultPassSetSpec { transforms = [T.LowerInvoke] }) $ \passManager -> do
+          let astIn = 
+                Module "<string>" "<string>" Nothing Nothing [
+                  GlobalDefinition $ functionDefaults {
+                    G.returnType = i32,
+                    G.name = Name "foo",
+                    G.parameters = ([Parameter i32 (Name "x") []], False),
+                    G.basicBlocks = [
+                      BasicBlock (Name "here") [
+                      ] (
+                        Do $ Ret (Just (ConstantOperand (C.Int 32 0))) []
+                      )
+                     ]
+                   }
+                 ] 
+          astOut <- withModuleFromAST' context astIn $ \mIn -> do
+            runPassManager passManager mIn
+            moduleAST mIn
+          astOut @?= Module "<string>" "<string>" Nothing Nothing [
+                  GlobalDefinition $ functionDefaults {
+                    G.returnType = i32,
+                    G.name = Name "foo",
+                    G.parameters = ([Parameter i32 (Name "x") []], False),
+                    G.basicBlocks = [
+                      BasicBlock (Name "here") [
+                      ] (
+                        Do $ Ret (Just (ConstantOperand (C.Int 32 0))) []
+                      )
+                    ]
+                  }
+                 ]
+   ]
+ ]
diff --git a/test/LLVM/Test/OrcJIT.hs b/test/LLVM/Test/OrcJIT.hs
new file mode 100644
--- /dev/null
+++ b/test/LLVM/Test/OrcJIT.hs
@@ -0,0 +1,103 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+module LLVM.Test.OrcJIT where
+
+import Test.Tasty
+import Test.Tasty.HUnit
+
+import LLVM.Test.Support
+
+import Data.Foldable
+import Data.IORef
+import Data.Word
+import Foreign.Ptr
+
+import LLVM.Context
+import LLVM.Module
+import LLVM.OrcJIT
+import LLVM.OrcJIT.IRCompileLayer (IRCompileLayer, withIRCompileLayer)
+import qualified LLVM.OrcJIT.IRCompileLayer as IRCompileLayer
+import LLVM.OrcJIT.CompileOnDemandLayer (CompileOnDemandLayer, withIndirectStubsManagerBuilder, withJITCompileCallbackManager, withCompileOnDemandLayer)
+import qualified LLVM.OrcJIT.CompileOnDemandLayer as CODLayer
+import LLVM.Target
+
+testModule :: String
+testModule =
+  "; ModuleID = '<string>'\n\
+  \source_filename = \"<string>\"\n\
+  \\n\
+  \declare i32 @testFunc()\n\
+  \define i32 @main(i32, i8**) {\n\
+  \  %3 = call i32 @testFunc()\n\
+  \  ret i32 %3\n\
+  \}\n"
+
+withTestModule :: (Module -> IO a) -> IO a
+withTestModule f = withContext $ \context -> withModuleFromLLVMAssembly' context testModule f
+
+myTestFuncImpl :: IO Word32
+myTestFuncImpl = return 42
+
+foreign import ccall "wrapper"
+  wrapTestFunc :: IO Word32 -> IO (FunPtr (IO Word32))
+
+foreign import ccall "dynamic"
+  mkMain :: FunPtr (IO Word32) -> IO Word32
+
+nullResolver :: MangledSymbol -> IO JITSymbol
+nullResolver s = putStrLn "nullresolver" >> return (JITSymbol 0 (JITSymbolFlags False False))
+
+resolver :: MangledSymbol -> IRCompileLayer -> MangledSymbol -> IO JITSymbol
+resolver testFunc compileLayer symbol
+  | symbol == testFunc = do
+      funPtr <- wrapTestFunc myTestFuncImpl
+      let addr = ptrToWordPtr (castFunPtrToPtr funPtr)
+      return (JITSymbol addr (JITSymbolFlags False True))
+  | otherwise = IRCompileLayer.findSymbol compileLayer symbol True
+
+codResolver :: MangledSymbol -> CompileOnDemandLayer -> MangledSymbol -> IO JITSymbol
+codResolver testFunc compileLayer symbol
+  | symbol == testFunc = do
+      funPtr <- wrapTestFunc myTestFuncImpl
+      let addr = ptrToWordPtr (castFunPtrToPtr funPtr)
+      return (JITSymbol addr (JITSymbolFlags False True))
+  | otherwise = CODLayer.findSymbol compileLayer symbol True
+
+tests :: TestTree
+tests =
+  testGroup "OrcJit" [
+    testCase "eager compilation" $ do
+      withTestModule $ \mod ->
+        failInIO $ withHostTargetMachine $ \tm ->
+          withObjectLinkingLayer $ \objectLayer ->
+            withIRCompileLayer objectLayer tm $ \compileLayer -> do
+              testFunc <- IRCompileLayer.mangleSymbol compileLayer "testFunc"
+              IRCompileLayer.withModuleSet
+                compileLayer
+                [mod]
+                (SymbolResolver (resolver testFunc compileLayer) nullResolver) $
+                \moduleSet -> do
+                  mainSymbol <- IRCompileLayer.mangleSymbol compileLayer "main"
+                  JITSymbol mainFn _ <- IRCompileLayer.findSymbol compileLayer mainSymbol True
+                  result <- mkMain (castPtrToFunPtr (wordPtrToPtr mainFn))
+                  result @?= 42,
+
+    testCase "lazy compilation" $ do
+      withTestModule $ \mod ->
+        failInIO $ withHostTargetMachine $ \tm -> do
+          triple <- getTargetMachineTriple tm
+          withObjectLinkingLayer $ \objectLayer ->
+            withIRCompileLayer objectLayer tm $ \baseLayer ->
+              withIndirectStubsManagerBuilder triple $ \stubsMgr ->
+                withJITCompileCallbackManager triple Nothing $ \callbackMgr ->
+                  withCompileOnDemandLayer baseLayer (\x -> return [x]) callbackMgr stubsMgr False $ \compileLayer -> do
+                    testFunc <- CODLayer.mangleSymbol compileLayer "testFunc"
+                    CODLayer.withModuleSet
+                      compileLayer
+                      [mod]
+                      (SymbolResolver (codResolver testFunc compileLayer) nullResolver) $
+                      \moduleSet -> do
+                        mainSymbol <- CODLayer.mangleSymbol compileLayer "main"
+                        JITSymbol mainFn _ <- CODLayer.findSymbol compileLayer mainSymbol True
+                        result <- mkMain (castPtrToFunPtr (wordPtrToPtr mainFn))
+                        result @?= 42
+  ]
diff --git a/test/LLVM/Test/Support.hs b/test/LLVM/Test/Support.hs
new file mode 100644
--- /dev/null
+++ b/test/LLVM/Test/Support.hs
@@ -0,0 +1,42 @@
+module LLVM.Test.Support where
+
+import Test.Tasty
+import Test.Tasty.HUnit
+
+import Data.Functor
+import Control.Monad
+import Control.Monad.Trans.Except
+import Text.Show.Pretty
+
+import LLVM.Context
+import LLVM.Module
+import LLVM.Diagnostic
+
+class FailInIO f where
+  errorToString :: f -> String
+
+failInIO :: FailInIO f => ExceptT f IO a -> IO a
+failInIO = either (fail . errorToString) return <=< runExceptT
+
+instance FailInIO String where
+  errorToString = id
+
+instance FailInIO (Either String Diagnostic) where
+  errorToString = either id diagnosticDisplay
+
+withModuleFromLLVMAssembly' c s f  = failInIO $ withModuleFromLLVMAssembly c s f
+withModuleFromAST' c a f = failInIO $ withModuleFromAST c a f
+withModuleFromBitcode' c a f = failInIO $ withModuleFromBitcode c ("<string>", a) f
+
+assertEqPretty :: (Eq a, Show a) => a -> a -> Assertion
+assertEqPretty actual expected = do
+  assertBool
+   ("expected: " ++ ppShow expected ++ "\n" ++ "but got: " ++ ppShow actual ++ "\n")
+   (expected == actual)
+
+strCheckC mAST mStr mStrCanon = withContext $ \context -> do
+  a <- withModuleFromLLVMAssembly' context mStr moduleAST
+  s <- withModuleFromAST' context mAST moduleLLVMAssembly
+  (a,s) `assertEqPretty` (mAST, mStrCanon)
+
+strCheck mAST mStr = strCheckC mAST mStr mStr
diff --git a/test/LLVM/Test/Target.hs b/test/LLVM/Test/Target.hs
new file mode 100644
--- /dev/null
+++ b/test/LLVM/Test/Target.hs
@@ -0,0 +1,71 @@
+{-# LANGUAGE
+  RecordWildCards
+  #-}
+module LLVM.Test.Target where
+
+import Test.Tasty
+import Test.Tasty.HUnit
+import Test.Tasty.QuickCheck
+import Test.QuickCheck
+import Test.QuickCheck.Property
+
+import Control.Monad
+
+import LLVM.Target
+import LLVM.Target.Options
+import LLVM.Target.LibraryFunction
+
+instance Arbitrary FloatABI where
+  arbitrary = elements [minBound .. maxBound]
+
+instance Arbitrary FloatingPointOperationFusionMode where
+  arbitrary = elements [minBound .. maxBound]
+
+instance Arbitrary Options where
+  arbitrary = do
+    printMachineCode <- arbitrary
+    lessPreciseFloatingPointMultiplyAddOption <- arbitrary
+    unsafeFloatingPointMath <- arbitrary
+    noInfinitiesFloatingPointMath <- arbitrary
+    noNaNsFloatingPointMath <- arbitrary
+    honorSignDependentRoundingFloatingPointMathOption <- arbitrary
+    noZerosInBSS <- arbitrary
+    guaranteedTailCallOptimization <- arbitrary
+    enableFastInstructionSelection <- arbitrary
+    useInitArray <- arbitrary
+    disableIntegratedAssembler <- arbitrary
+    compressDebugSections <- arbitrary
+    trapUnreachable <- arbitrary
+    stackAlignmentOverride <- arbitrary
+    floatABIType <- arbitrary
+    allowFloatingPointOperationFusion <- arbitrary
+    return Options { .. }
+
+tests = testGroup "Target" [
+  testGroup "Options" [
+     testProperty "basic" $ \options -> ioProperty $ do
+       withTargetOptions $ \to -> do
+         pokeTargetOptions options to
+         options' <- peekTargetOptions to
+         return $ options == options'
+   ],
+  testGroup "LibraryFunction" [
+    testGroup "set-get" [
+       testCase (show lf) $ do
+         triple <- getDefaultTargetTriple
+         withTargetLibraryInfo triple $ \tli -> do
+           setLibraryFunctionAvailableWithName tli lf "foo"
+           nm <- getLibraryFunctionName tli lf
+           nm @?= "foo"
+       | lf <- [minBound, maxBound]
+     ],
+    testCase "get" $ do
+      triple <- getDefaultTargetTriple
+      withTargetLibraryInfo triple $ \tli -> do
+        lf <- getLibraryFunction tli "printf"
+        lf @?= Just LF__printf
+   ],
+  testCase "Host" $ do
+    features <- getHostCPUFeatures
+    return ()
+ ]
diff --git a/test/LLVM/Test/Tests.hs b/test/LLVM/Test/Tests.hs
new file mode 100644
--- /dev/null
+++ b/test/LLVM/Test/Tests.hs
@@ -0,0 +1,39 @@
+module LLVM.Test.Tests where
+
+import Test.Tasty
+
+import qualified LLVM.Test.Analysis as Analysis
+import qualified LLVM.Test.CallingConvention as CallingConvention
+import qualified LLVM.Test.Constants as Constants
+import qualified LLVM.Test.DataLayout as DataLayout
+import qualified LLVM.Test.ExecutionEngine as ExecutionEngine
+import qualified LLVM.Test.Global as Global
+import qualified LLVM.Test.InlineAssembly as InlineAssembly
+import qualified LLVM.Test.Instructions as Instructions
+import qualified LLVM.Test.Instrumentation as Instrumentation
+import qualified LLVM.Test.Linking as Linking
+import qualified LLVM.Test.Metadata as Metadata
+import qualified LLVM.Test.Module as Module
+import qualified LLVM.Test.ObjectCode as ObjectCode
+import qualified LLVM.Test.Optimization as Optimization
+import qualified LLVM.Test.OrcJIT as OrcJIT
+import qualified LLVM.Test.Target as Target
+
+tests = testGroup "llvm-hs" [
+    CallingConvention.tests,
+    Constants.tests,
+    DataLayout.tests,
+    ExecutionEngine.tests,
+    Global.tests,
+    InlineAssembly.tests,
+    Instructions.tests,
+    Metadata.tests,
+    Module.tests,
+    OrcJIT.tests,
+    Optimization.tests,
+    Target.tests,
+    Analysis.tests,
+    Linking.tests,
+    Instrumentation.tests,
+    ObjectCode.tests
+  ]
diff --git a/test/Test.hs b/test/Test.hs
new file mode 100644
--- /dev/null
+++ b/test/Test.hs
@@ -0,0 +1,10 @@
+import Test.Tasty
+import qualified LLVM.Test.Tests as LLVM
+import LLVM.CommandLine
+
+main = do
+  parseCommandLineOptions [
+    "test",
+    "-bb-vectorize-ignore-target-info"
+   ] Nothing
+  defaultMain LLVM.tests
