diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2013, Benjamin S. Scarlet
+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,82 @@
+import Control.Monad
+import Data.List (isPrefixOf, (\\))
+import Data.Map (Map)
+import qualified Data.Map as Map
+import Distribution.Simple
+import Distribution.Simple.Program
+import Distribution.Simple.Setup
+import Distribution.Simple.LocalBuildInfo
+import Distribution.PackageDescription
+import System.Environment
+import System.SetEnv
+import Distribution.System
+
+
+
+-- define these selectively in C files (where _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"]
+
+llvmProgram = simpleProgram "llvm-config"
+
+main = do
+  let (ldLibraryPathVar, ldLibraryPathSep) = 
+        case buildOS of
+          OSX -> ("DYLD_LIBRARY_PATH",":")
+          _ -> ("LD_LIBRARY_PATH",":")
+      addToLdLibraryPath s = do
+         v <- lookupEnv ldLibraryPathVar
+         setEnv ldLibraryPathVar (s ++ maybe "" (ldLibraryPathSep ++) v)
+      getLLVMConfig configFlags = do
+         let verbosity = fromFlag $ configVerbosity configFlags
+         -- preconfigure the configuration-generating program "llvm-config"
+         programDb <- configureProgram verbosity llvmProgram
+                      . userSpecifyPaths (configProgramPaths configFlags)
+                      . userSpecifyArgss (configProgramArgs configFlags)
+                      $ configPrograms configFlags
+         let llvmConfig :: [String] -> IO String
+             llvmConfig = getDbProgramOutput verbosity llvmProgram programDb
+         return llvmConfig
+      addLLVMToLdLibraryPath configFlags = do
+        llvmConfig <- getLLVMConfig configFlags
+        [libDir] <- liftM lines $ llvmConfig ["--libdir"]
+        addToLdLibraryPath libDir
+         
+  defaultMainWithHooks simpleUserHooks {
+    hookedPrograms = [ llvmProgram ],
+    confHook = \(genericPackageDescription, hookedBuildInfo) configFlags -> do
+      llvmConfig <- getLLVMConfig configFlags
+
+      cppflags <- llvmConfig ["--cppflags"]
+      let useCppFlags = (filter ("-D" `isPrefixOf`) $ words cppflags) \\ (map ("-D"++) uncheckedHsFFIDefines)
+      includeDirs <- liftM lines $ llvmConfig ["--includedir"]
+      libDirs@[libDir] <- liftM lines $ llvmConfig ["--libdir"]
+
+      let genericPackageDescription' = genericPackageDescription {
+            condLibrary = do
+              libraryCondTree <- condLibrary genericPackageDescription
+              return libraryCondTree {
+                condTreeData = 
+                  let library = condTreeData libraryCondTree
+                  in library { 
+                    libBuildInfo = 
+                      let buildInfo = libBuildInfo library 
+                      in buildInfo { ccOptions = ccOptions buildInfo ++ useCppFlags }
+                    }
+              }
+           }
+          configFlags' = configFlags {
+            configExtraLibDirs = libDirs ++ configExtraLibDirs configFlags,
+            configExtraIncludeDirs = includeDirs ++ configExtraIncludeDirs configFlags
+           }
+      addLLVMToLdLibraryPath configFlags'
+      confHook simpleUserHooks (genericPackageDescription', hookedBuildInfo) configFlags',
+    buildHook = \packageDescription localBuildInfo userHooks buildFlags -> do
+      addLLVMToLdLibraryPath (configFlags localBuildInfo)
+      buildHook simpleUserHooks packageDescription localBuildInfo userHooks buildFlags,
+    testHook = \packageDescription localBuildInfo userHooks testFlags -> do
+      addLLVMToLdLibraryPath (configFlags localBuildInfo)
+      testHook simpleUserHooks packageDescription localBuildInfo userHooks testFlags
+   }
+
diff --git a/llvm-general.cabal b/llvm-general.cabal
new file mode 100644
--- /dev/null
+++ b/llvm-general.cabal
@@ -0,0 +1,186 @@
+name: llvm-general
+version: 0.1
+license: BSD3
+license-file: LICENSE
+author: Benjamin S.Scarlet <fgthb0@greynode.net>
+maintainer: Benjamin S. Scarlet <fgthb0@greynode.net>
+copyright: Benjamin S. Scarlet 2013
+build-type: Custom
+cabal-version: >= 1.8
+category: Compilers/Interpreters, Code Generation
+synopsis: General purpose LLVM bindings
+description:
+  llvm-general 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/General/Internal/FFI/Instruction.h
+  src/LLVM/General/Internal/FFI/Value.h
+  src/LLVM/General/Internal/FFI/SMDiagnostic.h
+  src/LLVM/General/Internal/FFI/InlineAssembly.h
+  src/LLVM/General/Internal/FFI/Target.h
+  src/LLVM/General/Internal/FFI/Function.h
+  src/LLVM/General/Internal/FFI/GlobalValue.h
+  src/LLVM/General/Internal/FFI/Type.h
+   
+source-repository head
+  type: git
+  location: git://github.com/bscarlet/llvm-general.git
+
+library
+  build-tools: llvm-config
+  ghc-options: -fwarn-unused-imports
+  build-depends: 
+    base >= 3 && < 5,
+    text >= 0.11.2.1,
+    bytestring >= 0.9.1.10,
+    transformers >= 0.3.0.0,
+    mtl >= 2.0.1.0,
+    template-haskell >= 2.5.0.0,
+    containers >= 0.5.0.0,
+    parsec >= 3.1.3,
+    array >= 0.4.0.1,
+    setenv >= 0.1.0
+  extra-libraries: LLVM-3.2svn stdc++
+  hs-source-dirs: src
+  exposed-modules:
+    LLVM.General
+    LLVM.General.AST
+    LLVM.General.AST.AddrSpace
+    LLVM.General.AST.InlineAssembly
+    LLVM.General.AST.Attribute
+    LLVM.General.AST.CallingConvention
+    LLVM.General.AST.Constant
+    LLVM.General.AST.DataLayout
+    LLVM.General.AST.Float
+    LLVM.General.AST.FloatingPointPredicate
+    LLVM.General.AST.Global
+    LLVM.General.AST.Instruction
+    LLVM.General.AST.IntegerPredicate
+    LLVM.General.AST.Linkage
+    LLVM.General.AST.Name
+    LLVM.General.AST.Operand
+    LLVM.General.AST.RMWOperation
+    LLVM.General.AST.Type
+    LLVM.General.AST.Visibility
+    LLVM.General.CodeGenOpt
+    LLVM.General.CodeModel
+    LLVM.General.Context
+    LLVM.General.Diagnostic
+    LLVM.General.ExecutionEngine
+    LLVM.General.Module
+    LLVM.General.PassManager
+    LLVM.General.Relocation
+    LLVM.General.Target
+    LLVM.General.Target.Options
+    LLVM.General.Transforms
+
+  other-modules:
+    Control.Monad.Phased
+    Control.Monad.Phased.Class
+    Control.Monad.Trans.Phased
+    Control.Monad.AnyCont
+    Control.Monad.AnyCont.Class
+    Control.Monad.Trans.AnyCont
+    LLVM.General.Internal.Atomicity
+    LLVM.General.Internal.Attribute
+    LLVM.General.Internal.BasicBlock
+    LLVM.General.Internal.CallingConvention
+    LLVM.General.Internal.Coding
+    LLVM.General.Internal.Constant
+    LLVM.General.Internal.Context
+    LLVM.General.Internal.DataLayout
+    LLVM.General.Internal.DecodeAST
+    LLVM.General.Internal.Diagnostic
+    LLVM.General.Internal.EncodeAST
+    LLVM.General.Internal.ExecutionEngine
+    LLVM.General.Internal.FloatingPointPredicate
+    LLVM.General.Internal.Function
+    LLVM.General.Internal.Global
+    LLVM.General.Internal.InlineAssembly
+    LLVM.General.Internal.Instruction
+    LLVM.General.Internal.InstructionDefs
+    LLVM.General.Internal.IntegerPredicate
+    LLVM.General.Internal.Metadata
+    LLVM.General.Internal.Module
+    LLVM.General.Internal.Operand
+    LLVM.General.Internal.PassManager
+    LLVM.General.Internal.RMWOperation
+    LLVM.General.Internal.String
+    LLVM.General.Internal.Target
+    LLVM.General.Internal.Type
+    LLVM.General.Internal.Value
+    LLVM.General.Internal.FFI.Assembly
+    LLVM.General.Internal.FFI.BasicBlock
+    LLVM.General.Internal.FFI.BinaryOperator
+    LLVM.General.Internal.FFI.Builder
+    LLVM.General.Internal.FFI.Cleanup
+    LLVM.General.Internal.FFI.Constant
+    LLVM.General.Internal.FFI.Context
+    LLVM.General.Internal.FFI.ExecutionEngine
+    LLVM.General.Internal.FFI.Function
+    LLVM.General.Internal.FFI.GlobalAlias
+    LLVM.General.Internal.FFI.GlobalValue
+    LLVM.General.Internal.FFI.GlobalVariable
+    LLVM.General.Internal.FFI.InlineAssembly
+    LLVM.General.Internal.FFI.Instruction
+    LLVM.General.Internal.FFI.InstructionDefs
+    LLVM.General.Internal.FFI.Iterate
+    LLVM.General.Internal.FFI.LLVMCTypes
+    LLVM.General.Internal.FFI.Metadata
+    LLVM.General.Internal.FFI.Module
+    LLVM.General.Internal.FFI.PassManager
+    LLVM.General.Internal.FFI.PtrHierarchy
+    LLVM.General.Internal.FFI.SMDiagnostic
+    LLVM.General.Internal.FFI.Target
+    LLVM.General.Internal.FFI.Transforms
+    LLVM.General.Internal.FFI.Type
+    LLVM.General.Internal.FFI.User
+    LLVM.General.Internal.FFI.Value
+
+  include-dirs: src
+  c-sources: 
+    src/LLVM/General/Internal/FFI/AssemblyC.cpp
+    src/LLVM/General/Internal/FFI/BuilderC.cpp
+    src/LLVM/General/Internal/FFI/ConstantC.cpp
+    src/LLVM/General/Internal/FFI/FunctionC.cpp
+    src/LLVM/General/Internal/FFI/GlobalAliasC.cpp
+    src/LLVM/General/Internal/FFI/GlobalValueC.cpp
+    src/LLVM/General/Internal/FFI/InlineAssemblyC.cpp
+    src/LLVM/General/Internal/FFI/InstructionC.cpp
+    src/LLVM/General/Internal/FFI/MetadataC.cpp
+    src/LLVM/General/Internal/FFI/ModuleC.cpp
+    src/LLVM/General/Internal/FFI/PassManagerC.cpp
+    src/LLVM/General/Internal/FFI/SMDiagnosticC.cpp
+    src/LLVM/General/Internal/FFI/TargetC.cpp
+    src/LLVM/General/Internal/FFI/TypeC.cpp
+    src/LLVM/General/Internal/FFI/ValueC.cpp
+
+test-suite test
+  type: exitcode-stdio-1.0
+  build-depends:  
+    base >= 3 && < 5,
+    test-framework >= 0.5,
+    test-framework-hunit >= 0.2.7,
+    HUnit >= 1.2.4.2,
+    test-framework-quickcheck2 >= 0.3.0.1,
+    QuickCheck >= 2.5.1.1,
+    llvm-general >= 0.1,
+    containers >= 0.5.0.0
+  hs-source-dirs: test
+  main-is: Test.hs
+  other-modules:
+    LLVM.General.Test.Constants
+    LLVM.General.Test.DataLayout
+    LLVM.General.Test.ExecutionEngine
+    LLVM.General.Test.Global
+    LLVM.General.Test.InlineAssembly
+    LLVM.General.Test.Instructions
+    LLVM.General.Test.Metadata
+    LLVM.General.Test.Module
+    LLVM.General.Test.Optimization
+    LLVM.General.Test.Support
+    LLVM.General.Test.Target
+    LLVM.General.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,29 @@
+{-# LANGUAGE
+  FlexibleInstances,
+  MultiParamTypeClasses,
+  UndecidableInstances
+  #-}
+module Control.Monad.AnyCont (
+    MonadAnyCont(..),
+    AnyContT(..),
+    runAnyContT,
+    withAnyContT,
+    mapAnyContT,
+    anyContIOToM,
+    anyContT
+  ) where
+
+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,39 @@
+{-# LANGUAGE
+  RankNTypes,
+  MultiParamTypeClasses,
+  FunctionalDependencies,
+  FlexibleInstances,
+  UndecidableInstances
+  #-}
+module Control.Monad.AnyCont.Class where
+
+import Control.Monad.Trans.Class
+import Control.Monad.Trans.AnyCont (AnyContT)
+import qualified Control.Monad.Trans.AnyCont as AnyCont
+import Control.Monad.Trans.Error as Error
+import Control.Monad.Trans.State as State
+import Control.Monad.Trans.Phased as Phased
+import Control.Monad.IO.Class
+
+class MonadAnyCont b m | m -> b where
+  anyContToM :: (forall r . (a -> b r) -> b r) -> m a
+  scopeAnyCont :: m a -> m a
+
+instance Monad m => MonadAnyCont m (AnyContT m) where
+  anyContToM = AnyCont.anyContT
+  scopeAnyCont = lift . flip AnyCont.runAnyContT return
+                                     
+instance (Error e, Monad m, MonadAnyCont b m) => MonadAnyCont b (ErrorT e m) where
+  anyContToM = lift . anyContToM
+  scopeAnyCont = mapErrorT scopeAnyCont
+
+instance (Monad m, MonadAnyCont b m) => MonadAnyCont b (PhasedT m) where
+  anyContToM = lift . anyContToM
+  scopeAnyCont = mapPhasedT scopeAnyCont
+
+instance (Monad m, MonadAnyCont b m) => MonadAnyCont b (StateT s m) where
+  anyContToM = lift . anyContToM
+  scopeAnyCont = StateT . (scopeAnyCont .) . runStateT
+
+anyContIOToM :: MonadIO m => (forall r . (a -> IO r) -> IO r) -> AnyContT m a
+anyContIOToM ioac = AnyCont.anyContT (\c -> liftIO (ioac return) >>= c)
diff --git a/src/Control/Monad/Phased.hs b/src/Control/Monad/Phased.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Monad/Phased.hs
@@ -0,0 +1,36 @@
+{-# LANGUAGE
+  FlexibleInstances,
+  MultiParamTypeClasses,
+  UndecidableInstances
+  #-}
+module Control.Monad.Phased (
+  MonadPhased(..),
+  PhasedT(..),
+  runPhasedT,
+  forInterleavedM,
+  iap,
+  defer,
+  runInterleaved,
+  mapPhasedT
+  ) where
+
+import Control.Monad
+import Control.Monad.Trans.Phased
+
+import Control.Monad.Trans.Class
+import Control.Monad.State.Class
+import Control.Monad.Reader.Class
+import Control.Monad.Error.Class
+import Control.Monad.Phased.Class
+
+instance MonadState s m => MonadState s (PhasedT m) where
+  state = lift . state
+
+instance MonadReader r m => MonadReader r (PhasedT m) where
+  ask = lift ask
+  local f = PhasedT . local f . liftM (either (Left . local f) Right) . unPhasedT
+
+instance (MonadError e m) => MonadError e (PhasedT m) where
+  throwError = lift . throwError
+  catchError pa ph = mapPhasedT (`catchError` (unPhasedT . ph)) pa
+  
diff --git a/src/Control/Monad/Phased/Class.hs b/src/Control/Monad/Phased/Class.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Monad/Phased/Class.hs
@@ -0,0 +1,46 @@
+{-# LANGUAGE
+  MultiParamTypeClasses,
+  UndecidableInstances,
+  FlexibleInstances,
+  TupleSections
+  #-}
+module Control.Monad.Phased.Class where
+
+import Control.Monad.Trans.Class
+import Control.Monad.Trans.Phased
+import Control.Monad.Trans.AnyCont
+
+class Monad m => MonadPhased m where
+  later :: m a -> m a
+  interleavePhasesWith :: (a -> b -> c) -> m a -> m b -> m c
+  mergePhases :: m a -> m a
+
+iap :: MonadPhased m => m (a -> b) -> m a -> m b
+iap = interleavePhasesWith ($)
+
+runInterleaved :: (MonadPhased m) => [m a] -> m [a]
+runInterleaved = foldr (interleavePhasesWith (:)) (return [])
+
+forInterleavedM x = runInterleaved . flip map x
+
+defer :: MonadPhased m => m ()
+defer = later (return ())
+
+instance Monad m => MonadPhased (PhasedT m) where
+  later = PhasedT . return . Left
+  interleavePhasesWith p (PhasedT mx) (PhasedT my) = PhasedT $ do
+    x <- mx
+    y <- my
+    return $ case (x,y) of
+      (Right a, Right b) -> Right (p a b)
+      _ -> Left $ interleavePhasesWith p (stall x) (stall y)
+           where stall = either id return
+  mergePhases = lift . runPhasedT
+
+instance MonadPhased m => MonadPhased (AnyContT m) where
+  later = lift . later . flip runAnyContT return
+  interleavePhasesWith p mx my = 
+    anyContT $ (>>=) $ interleavePhasesWith p (runAnyContT mx return) (runAnyContT my return)
+  mergePhases ma = anyContT (mergePhases (runAnyContT ma return) >>= )
+
+
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,39 @@
+{-# LANGUAGE
+  RankNTypes
+  #-}
+module Control.Monad.Trans.AnyCont where
+
+import Control.Applicative
+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/Control/Monad/Trans/Phased.hs b/src/Control/Monad/Trans/Phased.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Monad/Trans/Phased.hs
@@ -0,0 +1,33 @@
+module Control.Monad.Trans.Phased where
+
+import Control.Monad
+import Control.Applicative
+import Control.Monad.Trans.Class
+import Control.Monad.IO.Class
+
+newtype PhasedT m a = PhasedT { unPhasedT :: m (Either (PhasedT m a) a) }
+
+instance Functor m => Functor (PhasedT m) where
+  fmap f = PhasedT . fmap (either (Left . fmap f) (Right . f)) . unPhasedT
+
+instance (Functor m, Monad m) => Applicative (PhasedT m) where
+  pure = return
+  (<*>) = ap
+
+instance Monad m => Monad (PhasedT m) where
+  k >>= f = PhasedT $ unPhasedT k >>= either (return . Left . (>>= f)) (unPhasedT . f)
+  return = PhasedT . return . Right
+  fail = PhasedT . fail
+
+instance MonadTrans PhasedT where
+  lift = PhasedT . liftM Right
+
+runPhasedT :: Monad m => PhasedT m a -> m a
+runPhasedT = either runPhasedT return <=< unPhasedT
+
+instance MonadIO m => MonadIO (PhasedT m) where
+  liftIO = PhasedT . liftM Right . liftIO
+
+mapPhasedT :: Monad n => (m (Either (PhasedT m a) a) -> n (Either (PhasedT m a) b)) -> PhasedT m a -> PhasedT n b
+mapPhasedT f (PhasedT x) = PhasedT $ return (either (Left . (mapPhasedT f)) Right) `ap` f x
+
diff --git a/src/LLVM/General.hs b/src/LLVM/General.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General.hs
@@ -0,0 +1,7 @@
+-- | An interface to use LLVM in all capacities
+module LLVM.General (
+  module LLVM.General.Module
+  ) where
+
+import LLVM.General.Module
+
diff --git a/src/LLVM/General/AST.hs b/src/LLVM/General/AST.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/AST.hs
@@ -0,0 +1,53 @@
+-- | This module and descendants define AST data types to represent LLVM code.
+-- Note that these types are designed for fidelity rather than convenience - if the truth
+-- of what LLVM supports is less than pretty, so be it.
+module LLVM.General.AST (
+  Module(..), defaultModule,
+  Definition(..),
+  Global(GlobalVariable, GlobalAlias, Function), 
+        globalVariableDefaults,
+        globalAliasDefaults,
+        functionDefaults,
+  Parameter(..),
+  BasicBlock(..),
+  module LLVM.General.AST.Instruction,
+  module LLVM.General.AST.Name,
+  module LLVM.General.AST.Operand,
+  module LLVM.General.AST.Type
+  ) where
+
+import LLVM.General.AST.Name
+import LLVM.General.AST.Type
+import LLVM.General.AST.Global
+import LLVM.General.AST.Operand
+import LLVM.General.AST.Instruction
+import LLVM.General.AST.DataLayout
+
+-- | Any thing which can be at the top level of a 'Module'
+data Definition 
+  = GlobalDefinition Global
+  | TypeDefinition Name (Maybe Type)
+  | MetadataNodeDefinition MetadataNodeID [Operand]
+  | NamedMetadataDefinition String [MetadataNodeID]
+  | ModuleInlineAssembly String
+    deriving (Eq, Read, Show)
+
+-- | <http://llvm.org/docs/LangRef.html#modulestructure>
+data Module = 
+  Module {
+    moduleName :: String,
+    -- | a 'DataLayout', if specified, must match that of the eventual code generator
+    moduleDataLayout :: Maybe DataLayout, 
+    moduleTargetTriple :: Maybe String,
+    moduleDefinitions :: [Definition]
+  } 
+  deriving (Eq, Read, Show)
+
+-- | helper for making 'Module's
+defaultModule = 
+  Module {
+    moduleName = "<string>",
+    moduleDataLayout = Nothing,
+    moduleTargetTriple = Nothing,
+    moduleDefinitions = []
+  }
diff --git a/src/LLVM/General/AST/AddrSpace.hs b/src/LLVM/General/AST/AddrSpace.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/AST/AddrSpace.hs
@@ -0,0 +1,8 @@
+-- | Pointers exist in Address Spaces 
+module LLVM.General.AST.AddrSpace where
+
+import Data.Word
+
+-- | See <http://llvm.org/docs/LangRef.html#pointer-type>
+data AddrSpace = AddrSpace Word32
+   deriving (Eq, Ord, Read, Show)
diff --git a/src/LLVM/General/AST/Attribute.hs b/src/LLVM/General/AST/Attribute.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/AST/Attribute.hs
@@ -0,0 +1,38 @@
+-- | Module to allow importing 'Attribute' distinctly qualified.
+module LLVM.General.AST.Attribute where
+
+import Data.Word
+
+-- | <http://llvm.org/docs/LangRef.html#parameter-attributes>
+data ParameterAttribute
+    = ZeroExt
+    | SignExt
+    | InReg
+    | SRet
+    | NoAlias
+    | ByVal
+    | NoCapture
+    | Nest
+  deriving (Eq, Read, Show)
+
+-- | <http://llvm.org/docs/LangRef.html#function-attributes>
+data FunctionAttribute
+    = NoReturn
+    | NoUnwind
+    | ReadNone
+    | ReadOnly
+    | NoInline
+    | AlwaysInline
+    | OptimizeForSize
+    | StackProtect
+    | StackProtectReq
+    | Alignment Word32
+    | NoRedZone
+    | NoImplicitFloat
+    | Naked
+    | InlineHint
+    | StackAlignment Word32
+    | ReturnsTwice
+    | UWTable
+    | NonLazyBind
+  deriving (Eq, Read, Show)
diff --git a/src/LLVM/General/AST/CallingConvention.hs b/src/LLVM/General/AST/CallingConvention.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/AST/CallingConvention.hs
@@ -0,0 +1,13 @@
+{-# LANGUAGE
+  DeriveDataTypeable
+  #-}
+-- | Module to allow importing 'CallingConvention' distinctly qualified.
+module LLVM.General.AST.CallingConvention where
+
+import Data.Data
+import Data.Word
+
+-- |  <http://llvm.org/docs/LangRef.html#callingconv>
+data CallingConvention = C | Fast | Cold | GHC | Numbered Word32
+  deriving (Eq, Read, Show, Typeable, Data)
+
diff --git a/src/LLVM/General/AST/Constant.hs b/src/LLVM/General/AST/Constant.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/AST/Constant.hs
@@ -0,0 +1,218 @@
+-- | A representation of LLVM constants
+module LLVM.General.AST.Constant where
+
+import Data.Word (Word32)
+import Data.Bits ((.|.), (.&.), complement, testBit, shiftL)
+
+import LLVM.General.AST.Type
+import LLVM.General.AST.Name
+import LLVM.General.AST.FloatingPointPredicate (FloatingPointPredicate)
+import LLVM.General.AST.IntegerPredicate (IntegerPredicate)
+import qualified LLVM.General.AST.Float as F
+
+{- |
+<http://llvm.org/docs/LangRef.html#constants>
+
+N.B. - <http://llvm.org/docs/LangRef.html#constant-expressions>
+
+Although constant expressions and instructions have many similarites, there are important
+differences - so they're represented using different types in this AST. At the cost of making it
+harder to move an code back and forth between being constant and not, this approach embeds more of
+the rules of what IR is legal into the Haskell types.
+-} 
+data Constant
+    = Int { integerBits :: Word32, integerValue :: Integer }
+    | Float { floatValue :: F.SomeFloat }
+    | Null { constantType :: Type }
+    | Struct { isPacked :: Bool, memberValues :: [ Constant ] }
+    | Array { memberType :: Type, memberValues :: [ Constant ] }
+    | Vector { memberValues :: [ Constant ] }
+    | Undef { constantType :: Type }
+    | BlockAddress { blockAddressFunction :: Name, blockAddressBlock :: Name }
+    | GlobalReference Name 
+    | Add { 
+        operand0 :: Constant,
+        operand1 :: Constant
+      }
+    | FAdd {
+        operand0 :: Constant,
+        operand1 :: Constant
+      }
+    | Sub {
+        operand0 :: Constant,
+        operand1 :: Constant
+      }
+    | FSub { 
+        operand0 :: Constant, 
+        operand1 :: Constant
+      }
+    | Mul { 
+        operand0 :: Constant, 
+        operand1 :: Constant
+      }
+    | FMul { 
+        operand0 :: Constant, 
+        operand1 :: Constant
+      }
+    | UDiv { 
+        operand0 :: Constant, 
+        operand1 :: Constant
+      }
+    | SDiv { 
+        operand0 :: Constant, 
+        operand1 :: Constant
+      }
+    | FDiv { 
+        operand0 :: Constant, 
+        operand1 :: Constant
+      }
+    | URem { 
+        operand0 :: Constant, 
+        operand1 :: Constant
+      }
+    | SRem { 
+        operand0 :: Constant, 
+        operand1 :: Constant
+      }
+    | FRem { 
+        operand0 :: Constant, 
+        operand1 :: Constant
+      }
+    | Shl { 
+        operand0 :: Constant, 
+        operand1 :: Constant
+      }
+    | LShr { 
+        operand0 :: Constant, 
+        operand1 :: Constant
+      }
+    | AShr { 
+        operand0 :: Constant, 
+        operand1 :: Constant
+      }
+    | And { 
+        operand0 :: Constant, 
+        operand1 :: Constant
+      }
+    | Or { 
+        operand0 :: Constant, 
+        operand1 :: Constant
+      }
+    | Xor { 
+        operand0 :: Constant, 
+        operand1 :: Constant
+      }
+    | GetElementPtr { 
+        inBounds :: Bool,
+        address :: Constant,
+        indices :: [Constant]
+      }
+    | Trunc { 
+        operand0 :: Constant,
+        type' :: Type
+      }
+    | ZExt {
+        operand0 :: Constant,
+        type' :: Type
+      }
+    | SExt {
+        operand0 :: Constant,
+        type' :: Type
+      }
+    | FPToUI {
+        operand0 :: Constant,
+        type' :: Type
+      }
+    | FPToSI {
+        operand0 :: Constant,
+        type' :: Type
+      }
+    | UIToFP {
+        operand0 :: Constant,
+        type' :: Type
+      }
+    | SIToFP {
+        operand0 :: Constant,
+        type' :: Type
+      }
+    | FPTrunc {
+        operand0 :: Constant,
+        type' :: Type
+      }
+    | FPExt {
+        operand0 :: Constant,
+        type' :: Type
+      }
+    | PtrToInt {
+        operand0 :: Constant,
+        type' :: Type
+      }
+    | IntToPtr {
+        operand0 :: Constant,
+        type' :: Type
+      }
+    | BitCast {
+        operand0 :: Constant,
+        type' :: Type
+      }
+    | ICmp {
+        iPredicate :: IntegerPredicate,
+        operand0 :: Constant,
+        operand1 :: Constant
+      }
+    | FCmp {
+        fpPredicate :: FloatingPointPredicate,
+        operand0 :: Constant,
+        operand1 :: Constant
+      }
+    | Select { 
+        condition' :: Constant,
+        trueValue :: Constant,
+        falseValue :: Constant
+      }
+    | ExtractElement { 
+        vector :: Constant,
+        index :: Constant
+      }
+    | InsertElement { 
+        vector :: Constant,
+        element :: Constant,
+        index :: Constant
+      }
+    | ShuffleVector { 
+        operand0 :: Constant,
+        operand1 :: Constant,
+        mask :: Constant
+      }
+    | ExtractValue { 
+        aggregate :: Constant,
+        indices' :: [Word32]
+      }
+    | InsertValue { 
+        aggregate :: Constant,
+        element :: Constant,
+        indices' :: [Word32]
+      }
+    deriving (Eq, Ord, Read, Show)
+
+
+-- | Since LLVM types don't include signedness, there's ambiguity in interpreting
+-- an constant as an Integer. The LLVM assembly printer prints integers as signed, but
+-- cheats for 1-bit integers and prints them as 'true' or 'false'. That way it circuments the
+-- otherwise awkward fact that a twos complement 1-bit number only has the values -1 and 0.
+signedIntegerValue :: Constant -> Integer
+signedIntegerValue (Int nBits' bits) =
+  let nBits = fromIntegral nBits'
+  in
+    if bits `testBit` (nBits - 1) then bits .|. (-1 `shiftL` nBits) else bits
+
+-- | This library's conversion from LLVM C++ objects will always produce integer constants
+-- as unsigned, so this function in many cases is not necessary. However, nothing's to keep
+-- stop direct construction of an 'Int' with a negative 'integerValue'. There's nothing in principle
+-- wrong with such a value - it has perfectly good low order bits like any integer, and will be used
+-- as such, likely producing the intended result if lowered to C++. If, however one wishes to interpret
+-- an 'Int' of unknown provenance as unsigned, then this function will serve.
+unsignedIntegerValue :: Constant -> Integer
+unsignedIntegerValue (Int nBits bits) =
+  bits .&. (complement (-1 `shiftL` (fromIntegral nBits)))
+
diff --git a/src/LLVM/General/AST/DataLayout.hs b/src/LLVM/General/AST/DataLayout.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/AST/DataLayout.hs
@@ -0,0 +1,51 @@
+-- | <http://llvm.org/docs/LangRef.html#data-layout>
+module LLVM.General.AST.DataLayout where
+
+import Data.Word
+
+import Data.Map (Map)
+import qualified Data.Map as Map
+import Data.Set (Set)
+
+import LLVM.General.AST.AddrSpace
+
+-- | Little Endian is the one true way :-). Sadly, we must support the infidels.
+data Endianness = LittleEndian | BigEndian
+  deriving (Eq, Ord, Read, Show)
+
+-- | An AlignmentInfo describes how a given type must and would best be aligned
+data AlignmentInfo = AlignmentInfo {
+    abiAlignment :: Word32,
+    preferredAlignment :: Word32
+  }
+  deriving (Eq, Ord, Read, Show)
+
+-- | A type of type for which 'AlignmentInfo' may be specified
+data AlignType
+  = IntegerAlign
+  | VectorAlign
+  | FloatAlign
+  | AggregateAlign
+  | StackAlign
+  deriving (Eq, Ord, Read, Show)
+
+-- | a description of the various data layout properties which may be used during
+-- optimization
+data DataLayout = DataLayout {
+    endianness :: Maybe Endianness,
+    stackAlignment :: Maybe Word32,
+    pointerLayouts :: Map AddrSpace (Word32, AlignmentInfo),
+    typeLayouts :: Map (AlignType, Word32) AlignmentInfo,
+    nativeSizes :: Maybe (Set Word32)
+  }
+  deriving (Eq, Ord, Read, Show)
+
+-- | a 'DataLayout' which specifies nothing
+defaultDataLayout = DataLayout {
+  endianness = Nothing,
+  stackAlignment = Nothing,
+  pointerLayouts = Map.empty,
+  typeLayouts = Map.empty,
+  nativeSizes = Nothing
+ }
+
diff --git a/src/LLVM/General/AST/Float.hs b/src/LLVM/General/AST/Float.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/AST/Float.hs
@@ -0,0 +1,19 @@
+-- | This module provides a sub-namespace for a type to support the various sizes of floating point
+-- numbers LLVM supports. It is most definitely intended to be imported qualified.
+module LLVM.General.AST.Float where
+
+import Prelude as P
+import Data.Word (Word16, Word64)
+
+-- | A type summing up the various float types.
+-- N.B. Note that in the constructors with multiple fields, the lower significance bits are on the right
+-- - e.g. Quadruple highbits lowbits
+data SomeFloat
+  = Half Word16 
+  | Single Float
+  | Double P.Double
+  | Quadruple Word64 Word64
+  | X86_FP80 Word16 Word64
+  | PPC_FP128 Word64 Word64
+  deriving (Eq, Ord, Read, Show)
+
diff --git a/src/LLVM/General/AST/FloatingPointPredicate.hs b/src/LLVM/General/AST/FloatingPointPredicate.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/AST/FloatingPointPredicate.hs
@@ -0,0 +1,30 @@
+{-# LANGUAGE
+  DeriveDataTypeable 
+  #-}  
+-- | Predicates for the 'LLVM.General.AST.Instruction.FCmp' instruction
+module LLVM.General.AST.FloatingPointPredicate where
+
+import Data.Data
+
+-- | <http://llvm.org/docs/LangRef.html#fcmp-instruction>
+data FloatingPointPredicate
+  = False
+  | OEQ
+  | OGT
+  | OGE
+  | OLT
+  | OLE
+  | ONE
+  | ORD
+  | UNO
+  | UEQ
+  | UGT
+  | UGE
+  | ULT
+  | ULE
+  | UNE
+  | True
+  deriving (Eq, Ord, Read, Show, Data, Typeable)
+
+
+
diff --git a/src/LLVM/General/AST/Global.hs b/src/LLVM/General/AST/Global.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/AST/Global.hs
@@ -0,0 +1,109 @@
+-- | 'Global's - top-level values in 'Module's - and supporting structures.
+module LLVM.General.AST.Global where
+
+import Data.Word
+
+import LLVM.General.AST.Name
+import LLVM.General.AST.Type
+import LLVM.General.AST.Constant (Constant)
+import LLVM.General.AST.AddrSpace
+import LLVM.General.AST.Instruction (Named, Instruction, Terminator)
+import qualified LLVM.General.AST.Linkage as L
+import qualified LLVM.General.AST.Visibility as V
+import qualified LLVM.General.AST.CallingConvention as CC
+import qualified LLVM.General.AST.Attribute as A
+
+-- | <http://llvm.org/doxygen/classllvm_1_1GlobalValue.html>
+data Global
+    -- | <http://llvm.org/docs/LangRef.html#global-variables>
+    = GlobalVariable {
+        name :: Name,
+        linkage :: L.Linkage,
+        visibility :: V.Visibility,
+        isThreadLocal :: Bool,
+        addrSpace :: AddrSpace,
+        hasUnnamedAddr :: Bool,
+        isConstant :: Bool,
+        type' :: Type,
+        initializer :: Maybe Constant,
+        section :: Maybe String,
+        alignment :: Word32
+      }
+    -- | <http://llvm.org/docs/LangRef.html#aliases>
+    | GlobalAlias {
+        name :: Name,
+        linkage :: L.Linkage,
+        visibility :: V.Visibility,
+        type' :: Type,
+        aliasee :: Constant
+      }
+    -- | <http://llvm.org/docs/LangRef.html#functions>
+    | Function {
+        linkage :: L.Linkage,
+        visibility :: V.Visibility,
+        callingConvention :: CC.CallingConvention,
+        returnAttributes :: [A.ParameterAttribute],
+        returnType :: Type,
+        name :: Name,
+        parameters :: ([Parameter],Bool),
+        functionAttributes :: [A.FunctionAttribute],
+        section :: Maybe String,
+        alignment :: Word32,
+        basicBlocks :: [BasicBlock]
+      }
+  deriving (Eq, Read, Show)
+
+-- | 'Parameter's for 'Function's
+data Parameter = Parameter Type Name [A.ParameterAttribute]
+  deriving (Eq, Read, Show)
+
+-- | <http://llvm.org/doxygen/classllvm_1_1BasicBlock.html>
+-- LLVM code in a function is a sequence of 'BasicBlock's each with a label,
+-- some instructions, and a terminator.
+data BasicBlock = BasicBlock Name [Named Instruction] (Named Terminator)
+  deriving (Eq, Read, Show)
+
+-- | helper for making 'GlobalVariable's
+globalVariableDefaults :: Global
+globalVariableDefaults = 
+  GlobalVariable {
+  name = undefined,
+  linkage = L.External,
+  visibility = V.Default,
+  isThreadLocal = False,
+  addrSpace = AddrSpace 0,
+  hasUnnamedAddr = False,
+  isConstant = False,
+  type' = undefined,
+  initializer = Nothing,
+  section = Nothing,
+  alignment = 0
+  }
+
+-- | helper for making 'GlobalAlias's
+globalAliasDefaults :: Global
+globalAliasDefaults =
+  GlobalAlias {
+    name = undefined,
+    linkage = L.External,
+    visibility = V.Default,
+    type' = undefined,
+    aliasee = undefined
+  }
+
+-- | helper for making 'Function's
+functionDefaults :: Global
+functionDefaults = 
+  Function {
+    linkage = L.External,
+    visibility = V.Default,
+    callingConvention = CC.C,
+    returnAttributes = [],
+    returnType = undefined,
+    name = undefined,
+    parameters = undefined,
+    functionAttributes = [],
+    section = Nothing,
+    alignment = 0,
+    basicBlocks = []
+  }
diff --git a/src/LLVM/General/AST/InlineAssembly.hs b/src/LLVM/General/AST/InlineAssembly.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/AST/InlineAssembly.hs
@@ -0,0 +1,30 @@
+{-# LANGUAGE
+  DeriveDataTypeable
+  #-}
+-- | A representation of an LLVM inline assembly
+module LLVM.General.AST.InlineAssembly where
+
+import Data.Data
+
+import LLVM.General.AST.Type
+
+-- | the dialect of assembly used in an inline assembly string
+-- <http://en.wikipedia.org/wiki/X86_assembly_language#Syntax>
+data Dialect
+  = ATTDialect
+  | IntelDialect
+  deriving (Eq, Read, Show, Typeable, Data)
+
+-- | <http://llvm.org/docs/LangRef.html#inline-assembler-expressions>
+-- to be used through 'LLVM.General.AST.Operand.CallableOperand' with a
+-- 'LLVM.General.AST.Instruction.Call' instruction
+data InlineAssembly
+  = InlineAssembly {
+      type' :: Type,
+      assembly :: String,
+      constraints :: String,
+      hasSideEffects :: Bool,
+      alignStack :: Bool,
+      dialect :: Dialect
+    }
+  deriving (Eq, Read, Show)
diff --git a/src/LLVM/General/AST/Instruction.hs b/src/LLVM/General/AST/Instruction.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/AST/Instruction.hs
@@ -0,0 +1,389 @@
+{-# LANGUAGE
+  DeriveDataTypeable
+  #-}
+-- | LLVM instructions 
+-- <http://llvm.org/docs/LangRef.html#instruction-reference>
+module LLVM.General.AST.Instruction where
+
+import Data.Data
+import Data.Word
+
+import LLVM.General.AST.Type
+import LLVM.General.AST.Name
+import LLVM.General.AST.Constant
+import LLVM.General.AST.Operand
+import LLVM.General.AST.IntegerPredicate (IntegerPredicate)
+import LLVM.General.AST.FloatingPointPredicate (FloatingPointPredicate)
+import LLVM.General.AST.RMWOperation (RMWOperation)
+import LLVM.General.AST.CallingConvention (CallingConvention)
+import LLVM.General.AST.Attribute (ParameterAttribute, FunctionAttribute)
+
+-- | <http://llvm.org/docs/LangRef.html#metadata-nodes-and-metadata-strings>
+-- Metadata can be attached to an instruction
+type InstructionMetadata = [(String, MetadataNode)]
+
+-- | <http://llvm.org/docs/LangRef.html#terminators>
+data Terminator 
+  = Ret { 
+      returnOperand :: Maybe Operand,
+      metadata' :: InstructionMetadata
+    }
+  | CondBr { 
+      condition :: Operand, 
+      trueDest :: Name, 
+      falseDest :: Name,
+      metadata' :: InstructionMetadata
+    }
+  | Br { 
+      dest :: Name,
+      metadata' :: InstructionMetadata
+    }
+  | Switch {
+      operand0' :: Operand,
+      defaultDest :: Name,
+      dests :: [(Constant, Name)],
+      metadata' :: InstructionMetadata
+    }
+  | IndirectBr {
+      operand0' :: Operand,
+      possibleDests :: [Name],
+      metadata' :: InstructionMetadata
+    }
+  | Invoke {
+      callingConvention' :: CallingConvention,
+      returnAttributes' :: [ParameterAttribute],
+      function' :: CallableOperand,
+      arguments' :: [(Operand, [ParameterAttribute])],
+      functionAttributes' :: [FunctionAttribute],
+      returnDest :: Name,
+      exceptionDest :: Name,
+      metadata' :: InstructionMetadata
+    }
+  | Resume {
+      operand0' :: Operand,
+      metadata' :: InstructionMetadata
+    }
+  | Unreachable {
+      metadata' :: InstructionMetadata
+    }
+  deriving (Eq, Read, Show)
+
+-- | <http://llvm.org/docs/LangRef.html#atomic-memory-ordering-constraints>
+-- <http://llvm.org/docs/Atomics.html>
+data MemoryOrdering
+  = Unordered
+  | Monotonic
+  | Acquire
+  | Release
+  | AcquireRelease
+  | SequentiallyConsistent
+  deriving (Eq, Ord, Read, Show, Data, Typeable)
+
+-- | An 'Atomicity' describes constraints on the visibility of effects of an atomic instruction
+data Atomicity = Atomicity { 
+  crossThread :: Bool, -- ^ <http://llvm.org/docs/LangRef.html#singlethread>
+  memoryOrdering :: MemoryOrdering
+ }
+ deriving (Eq, Ord, Read, Show)
+
+-- | For the redoubtably complex 'LandingPad' instruction
+data LandingPadClause
+    = Catch Constant
+    | Filter Constant
+    deriving (Eq, Ord, Read, Show)
+
+-- | non-terminator instructions:
+-- <http://llvm.org/docs/LangRef.html#binaryops>
+-- <http://llvm.org/docs/LangRef.html#bitwiseops>
+-- <http://llvm.org/docs/LangRef.html#memoryops>
+-- <http://llvm.org/docs/LangRef.html#otherops>
+data Instruction
+  = Add { 
+      nsw :: Bool,
+      nuw :: Bool,
+      operand0 :: Operand,
+      operand1 :: Operand,
+      metadata :: InstructionMetadata
+    }
+  | FAdd {
+      operand0 :: Operand,
+      operand1 :: Operand,
+      metadata :: InstructionMetadata
+    }
+  | Sub {
+      nsw :: Bool,
+      nuw :: Bool,
+      operand0 :: Operand,
+      operand1 :: Operand,
+      metadata :: InstructionMetadata
+    }
+  | FSub { 
+      operand0 :: Operand, 
+      operand1 :: Operand, 
+      metadata :: InstructionMetadata
+    }
+  | Mul { 
+      nsw :: Bool, 
+      nuw :: Bool, 
+      operand0 :: Operand, 
+      operand1 :: Operand, 
+      metadata :: InstructionMetadata 
+    }
+  | FMul { 
+      operand0 :: Operand, 
+      operand1 :: Operand, 
+      metadata :: InstructionMetadata
+    }
+  | UDiv { 
+      exact :: Bool, 
+      operand0 :: Operand, 
+      operand1 :: Operand, 
+      metadata :: InstructionMetadata
+    }
+  | SDiv { 
+      exact :: Bool, 
+      operand0 :: Operand, 
+      operand1 :: Operand, 
+      metadata :: InstructionMetadata
+    }
+  | FDiv { 
+      operand0 :: Operand, 
+      operand1 :: Operand, 
+      metadata :: InstructionMetadata
+    }
+  | URem { 
+      operand0 :: Operand, 
+      operand1 :: Operand, 
+      metadata :: InstructionMetadata
+    }
+  | SRem { 
+      operand0 :: Operand, 
+      operand1 :: Operand, 
+      metadata :: InstructionMetadata
+    }
+  | FRem { 
+      operand0 :: Operand, 
+      operand1 :: Operand, 
+      metadata :: InstructionMetadata
+    }
+  | Shl { 
+      nsw :: Bool, 
+      nuw :: Bool, 
+      operand0 :: Operand, 
+      operand1 :: Operand, 
+      metadata :: InstructionMetadata
+    }
+  | LShr { 
+      exact :: Bool, 
+      operand0 :: Operand, 
+      operand1 :: Operand, 
+      metadata :: InstructionMetadata
+    }
+  | AShr { 
+      exact :: Bool, 
+      operand0 :: Operand, 
+      operand1 :: Operand, 
+      metadata :: InstructionMetadata
+    }
+  | And { 
+      operand0 :: Operand, 
+      operand1 :: Operand, 
+      metadata :: InstructionMetadata
+    }
+  | Or { 
+      operand0 :: Operand, 
+      operand1 :: Operand, 
+      metadata :: InstructionMetadata
+    }
+  | Xor { 
+      operand0 :: Operand, 
+      operand1 :: Operand, 
+      metadata :: InstructionMetadata
+    }
+  | Alloca { 
+      allocatedType :: Type,
+      numElements :: Maybe Operand,
+      alignment :: Word32,
+      metadata :: InstructionMetadata
+    }
+  | Load {
+      volatile :: Bool, 
+      address :: Operand,
+      maybeAtomicity :: Maybe Atomicity,
+      alignment :: Word32,
+      metadata :: InstructionMetadata
+    }
+  | Store {
+      volatile :: Bool, 
+      address :: Operand,
+      value :: Operand,
+      maybeAtomicity :: Maybe Atomicity,
+      alignment :: Word32,
+      metadata :: InstructionMetadata
+    }
+  | GetElementPtr { 
+      inBounds :: Bool,
+      address :: Operand,
+      indices :: [Operand],
+      metadata :: InstructionMetadata
+    }
+  | Fence { 
+      atomicity :: Atomicity,
+      metadata :: InstructionMetadata 
+    }
+  | CmpXchg { 
+      volatile :: Bool,
+      address :: Operand,
+      expected :: Operand,
+      replacement :: Operand,
+      atomicity :: Atomicity,
+      metadata :: InstructionMetadata 
+    }
+  | AtomicRMW { 
+      volatile :: Bool,
+      rmwOperation :: RMWOperation,
+      address :: Operand,
+      value :: Operand,
+      atomicity :: Atomicity,
+      metadata :: InstructionMetadata 
+    }
+  | Trunc { 
+      operand0 :: Operand,
+      type' :: Type,
+      metadata :: InstructionMetadata 
+    }
+  | ZExt {
+      operand0 :: Operand,
+      type' :: Type,
+      metadata :: InstructionMetadata 
+    }
+  | SExt {
+      operand0 :: Operand,
+      type' :: Type,
+      metadata :: InstructionMetadata
+    }
+  | FPToUI {
+      operand0 :: Operand,
+      type' :: Type,
+      metadata :: InstructionMetadata
+    }
+  | FPToSI {
+      operand0 :: Operand,
+      type' :: Type,
+      metadata :: InstructionMetadata
+    }
+  | UIToFP {
+      operand0 :: Operand,
+      type' :: Type,
+      metadata :: InstructionMetadata
+    }
+  | SIToFP {
+      operand0 :: Operand,
+      type' :: Type,
+      metadata :: InstructionMetadata
+    }
+  | FPTrunc {
+      operand0 :: Operand,
+      type' :: Type,
+      metadata :: InstructionMetadata
+    }
+  | FPExt {
+      operand0 :: Operand,
+      type' :: Type,
+      metadata :: InstructionMetadata
+    }
+  | PtrToInt {
+      operand0 :: Operand,
+      type' :: Type,
+      metadata :: InstructionMetadata
+    }
+  | IntToPtr {
+      operand0 :: Operand,
+      type' :: Type,
+      metadata :: InstructionMetadata
+    }
+  | BitCast {
+      operand0 :: Operand,
+      type' :: Type,
+      metadata :: InstructionMetadata
+    }
+  | ICmp {
+      iPredicate :: IntegerPredicate,
+      operand0 :: Operand,
+      operand1 :: Operand,
+      metadata :: InstructionMetadata
+    }
+  | FCmp {
+      fpPredicate :: FloatingPointPredicate,
+      operand0 :: Operand,
+      operand1 :: Operand,
+      metadata :: InstructionMetadata
+    }
+  | Phi {
+      type' :: Type,
+      incomingValues :: [ (Operand, Name) ],
+      metadata :: InstructionMetadata
+  } 
+  | Call {
+      isTailCall :: Bool,
+      callingConvention :: CallingConvention,
+      returnAttributes :: [ParameterAttribute],
+      function :: CallableOperand,
+      arguments :: [(Operand, [ParameterAttribute])],
+      functionAttributes :: [FunctionAttribute],
+      metadata :: InstructionMetadata
+  }
+  | Select { 
+      condition' :: Operand,
+      trueValue :: Operand,
+      falseValue :: Operand,
+      metadata :: InstructionMetadata
+    }
+  | VAArg { 
+      argList :: Operand,
+      type' :: Type,
+      metadata :: InstructionMetadata 
+    }
+  | ExtractElement { 
+      vector :: Operand,
+      index :: Operand,
+      metadata :: InstructionMetadata 
+    }
+  | InsertElement { 
+      vector :: Operand,
+      element :: Operand,
+      index :: Operand,
+      metadata :: InstructionMetadata
+    }
+  | ShuffleVector { 
+      operand0 :: Operand,
+      operand1 :: Operand,
+      mask :: Constant,
+      metadata :: InstructionMetadata
+    }
+  | ExtractValue { 
+      aggregate :: Operand,
+      indices' :: [Word32],
+      metadata :: InstructionMetadata
+    }
+  | InsertValue { 
+      aggregate :: Operand,
+      element :: Operand,
+      indices' :: [Word32],
+      metadata :: InstructionMetadata
+    }
+  | LandingPad { 
+      type' :: Type,
+      personalityFunction :: Operand,
+      cleanup :: Bool,
+      clauses :: [LandingPadClause],
+      metadata :: InstructionMetadata 
+    }
+  deriving (Eq, Read, Show)
+
+-- | Instances of instructions may be given a name, allowing their results to be referenced as 'Operand's.
+-- Sometimes instructions - e.g. a call to a function returning void - don't need names.
+data Named a 
+  = Name := a
+  | Do a
+  deriving (Eq, Read, Show)
diff --git a/src/LLVM/General/AST/IntegerPredicate.hs b/src/LLVM/General/AST/IntegerPredicate.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/AST/IntegerPredicate.hs
@@ -0,0 +1,24 @@
+{-# LANGUAGE
+  DeriveDataTypeable 
+  #-}  
+-- | Predicates for the 'LLVM.General.AST.Instruction.ICmp' instruction
+module LLVM.General.AST.IntegerPredicate where
+
+import Data.Data
+
+-- | <http://llvm.org/docs/LangRef.html#icmp-instruction>
+data IntegerPredicate
+  = EQ
+  | NE
+  | UGT
+  | UGE
+  | ULT
+  | ULE
+  | SGT
+  | SGE
+  | SLT
+  | SLE
+  deriving (Eq, Ord, Read, Show, Data, Typeable)
+
+
+
diff --git a/src/LLVM/General/AST/Linkage.hs b/src/LLVM/General/AST/Linkage.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/AST/Linkage.hs
@@ -0,0 +1,27 @@
+{-# LANGUAGE
+  DeriveDataTypeable
+  #-}
+  
+-- | Module to allow importing 'Linkage' distinctly qualified.
+module LLVM.General.AST.Linkage where
+
+import Data.Data
+
+-- | <http://llvm.org/docs/LangRef.html#linkage>
+data Linkage
+    = Private
+    | LinkerPrivate
+    | LinkerPrivateWeak
+    | Internal
+    | AvailableExternally
+    | LinkOnce
+    | Weak
+    | Common
+    | Appending
+    | ExternWeak
+    | LinkOnceODR
+    | WeakODR
+    | External
+    | DLLImport
+    | DLLExport
+  deriving (Eq, Read, Show, Typeable, Data)
diff --git a/src/LLVM/General/AST/Name.hs b/src/LLVM/General/AST/Name.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/AST/Name.hs
@@ -0,0 +1,31 @@
+-- | Names as used in LLVM IR
+module LLVM.General.AST.Name where
+
+import Data.Word
+
+{- |
+Objects of various sorts in LLVM IR are identified by address in the LLVM C++ API, and
+may be given a string name. When printed to (resp. read from) human-readable LLVM assembly, objects without
+string names are numbered sequentially (resp. must be numbered sequentially). String names may be quoted, and
+are quoted when printed if they would otherwise be misread - e.g. when containing special characters. 
+
+> 7
+
+means the seventh unnamed object, while
+
+> "7"
+
+means the object named with the string "7".
+
+This libraries handling of 'UnName's during translation of the AST down into C++ IR is somewhat more
+forgiving than the LLVM assembly parser: it does not require that unnamed values be numbered sequentially;
+however, the numbers of 'UnName's passed into C++ cannot be preserved in the C++ objects. If the C++ IR is
+printed as assembly or translated into a Haskell AST, unnamed nodes will be renumbered sequentially. Thus
+unnamed node numbers should be thought of as having any scope limited to the 'LLVM.General.AST.Module' in
+which they are used.
+-}
+data Name 
+    = Name String -- ^ a string name 
+    | UnName Word -- ^ a number for a nameless thing
+   deriving (Eq, Ord, Read, Show)
+
diff --git a/src/LLVM/General/AST/Operand.hs b/src/LLVM/General/AST/Operand.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/AST/Operand.hs
@@ -0,0 +1,33 @@
+-- | A type to represent operands to LLVM 'LLVM.General.AST.Instruction.Instruction's
+module LLVM.General.AST.Operand where
+  
+import Data.Word
+
+import LLVM.General.AST.Name
+import LLVM.General.AST.Constant
+import LLVM.General.AST.InlineAssembly
+
+-- | A 'MetadataNodeID' is a number for identifying a metadata node.
+-- Note this is different from "named metadata", which are represented with
+-- 'LLVM.General.AST.NamedMetadataDefinition'.
+newtype MetadataNodeID = MetadataNodeID Word
+  deriving (Eq, Ord, Read, Show)
+
+-- | <http://llvm.org/docs/LangRef.html#metadata>
+data MetadataNode 
+  = MetadataNode [Operand]
+  | MetadataNodeReference MetadataNodeID
+  deriving (Eq, Ord, Read, Show)
+
+-- | An 'Operand' is roughly that which is an argument to an 'LLVM.General.AST.Instruction.Instruction'
+data Operand 
+  -- | %foo
+  = LocalReference Name
+  -- | 'Constant's include 'LLVM.General.AST.Constant.GlobalReference', for \@foo
+  | ConstantOperand Constant
+  | MetadataStringOperand String
+  | MetadataNodeOperand MetadataNode
+  deriving (Eq, Ord, Read, Show)
+
+-- | The 'LLVM.General.AST.Instruction.Call' instruction is special: the callee can be inline assembly
+type CallableOperand  = Either InlineAssembly Operand
diff --git a/src/LLVM/General/AST/RMWOperation.hs b/src/LLVM/General/AST/RMWOperation.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/AST/RMWOperation.hs
@@ -0,0 +1,25 @@
+{-# LANGUAGE
+  DeriveDataTypeable 
+  #-}  
+-- | Operations for the 'LLVM.General.AST.Instruction.AtomicRMW' instruction
+module LLVM.General.AST.RMWOperation where
+
+import Data.Data
+
+-- | <http://llvm.org/docs/LangRef.html#atomicrmw-instruction>
+data RMWOperation
+  = Xchg
+  | Add
+  | Sub
+  | And
+  | Nand
+  | Or
+  | Xor
+  | Max
+  | Min
+  | UMax
+  | UMin
+  deriving (Eq, Ord, Read, Show, Data, Typeable)
+
+
+
diff --git a/src/LLVM/General/AST/Type.hs b/src/LLVM/General/AST/Type.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/AST/Type.hs
@@ -0,0 +1,40 @@
+-- | A representation of an LLVM type
+module LLVM.General.AST.Type where
+
+import LLVM.General.AST.AddrSpace
+import LLVM.General.AST.Name
+
+import Data.Word (Word32, Word64)
+
+-- | LLVM supports some special formats floating point format. This type is to distinguish those format.
+-- I believe it's treated as a format for "a" float, as opposed to a vector of two floats, because
+-- its intended usage is to represent a single number with a combined significand.
+data FloatingPointFormat
+  = IEEE
+  | DoubleExtended
+  | PairOfFloats
+  deriving (Eq, Ord, Read, Show)
+
+-- | <http://llvm.org/docs/LangRef.html#type-system>
+data Type
+  -- | <http://llvm.org/docs/LangRef.html#void-type>
+  = VoidType
+  -- | <http://llvm.org/docs/LangRef.html#integer-type>
+  | IntegerType { typeBits :: Word32 }
+  -- | <http://llvm.org/docs/LangRef.html#pointer-type>
+  | PointerType { pointerReferent :: Type, pointerAddrSpace :: AddrSpace }
+  -- | <http://llvm.org/docs/LangRef.html#floating-point-types>
+  | FloatingPointType { typeBits :: Word32, floatingPointFormat :: FloatingPointFormat }
+  -- | <http://llvm.org/docs/LangRef.html#function-type>
+  | FunctionType { resultType :: Type, argumentTypes :: [Type], isVarArg :: Bool }
+  -- | <http://llvm.org/docs/LangRef.html#vector-type>
+  | VectorType { nVectorElements :: Word32, elementType :: Type }
+  -- | <http://llvm.org/docs/LangRef.html#structure-type>
+  | StructureType { isPacked :: Bool, elementTypes :: [Type] }
+  -- | <http://llvm.org/docs/LangRef.html#array-type>
+  | ArrayType { nArrayElements :: Word64, elementType :: Type }
+  -- | <http://llvm.org/docs/LangRef.html#opaque-structure-types>
+  | NamedTypeReference Name
+  -- | <http://llvm.org/docs/LangRef.html#metadata-type>
+  | MetadataType -- only to be used as a parameter type for a few intrinsics
+  deriving (Eq, Ord, Read, Show)
diff --git a/src/LLVM/General/AST/Visibility.hs b/src/LLVM/General/AST/Visibility.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/AST/Visibility.hs
@@ -0,0 +1,11 @@
+{-# LANGUAGE
+  DeriveDataTypeable
+  #-}
+-- | Module to allow importing 'Visibility' distinctly qualified.
+module LLVM.General.AST.Visibility where
+
+import Data.Data
+
+-- | <http://llvm.org/docs/LangRef.html#visibility>
+data Visibility = Default | Hidden | Protected
+  deriving (Eq, Read, Show, Typeable, Data)
diff --git a/src/LLVM/General/CodeGenOpt.hs b/src/LLVM/General/CodeGenOpt.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/CodeGenOpt.hs
@@ -0,0 +1,15 @@
+{-# LANGUAGE
+  DeriveDataTypeable
+  #-}
+-- | Code generation options, used in specifying TargetMachine
+module LLVM.General.CodeGenOpt where
+
+import Data.Data
+
+-- | <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/General/CodeModel.hs b/src/LLVM/General/CodeModel.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/CodeModel.hs
@@ -0,0 +1,17 @@
+{-# LANGUAGE
+  DeriveDataTypeable
+  #-}
+-- | Relocations, used in specifying TargetMachine
+module LLVM.General.CodeModel where
+
+import Data.Data
+
+-- | <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/General/Context.hs b/src/LLVM/General/Context.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Context.hs
@@ -0,0 +1,7 @@
+-- | functions for the LLVM Context object which holds thread-scope state
+module LLVM.General.Context (
+  Context,
+  withContext
+  ) where
+
+import LLVM.General.Internal.Context
diff --git a/src/LLVM/General/Diagnostic.hs b/src/LLVM/General/Diagnostic.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Diagnostic.hs
@@ -0,0 +1,36 @@
+{-# LANGUAGE
+  DeriveDataTypeable
+  #-}
+-- | Diagnostics describe parse errors
+module LLVM.General.Diagnostic (
+  DiagnosticKind(..),
+  Diagnostic(..),
+  diagnosticDisplay
+ ) where
+
+import Data.Data
+
+-- | 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/General/ExecutionEngine.hs b/src/LLVM/General/ExecutionEngine.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/ExecutionEngine.hs
@@ -0,0 +1,9 @@
+-- | Tools for JIT execution
+module LLVM.General.ExecutionEngine (
+  ExecutionEngine,
+  withExecutionEngine,
+  withModuleInEngine,
+  findFunction
+  ) where
+
+import LLVM.General.Internal.ExecutionEngine
diff --git a/src/LLVM/General/Internal/Atomicity.hs b/src/LLVM/General/Internal/Atomicity.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/Atomicity.hs
@@ -0,0 +1,41 @@
+{-# LANGUAGE
+  TemplateHaskell,
+  MultiParamTypeClasses,
+  FlexibleInstances
+  #-}
+
+module LLVM.General.Internal.Atomicity where
+
+import Control.Monad
+
+import Data.Maybe
+
+import qualified LLVM.General.Internal.FFI.LLVMCTypes as FFI
+
+import LLVM.General.Internal.Coding
+
+import qualified LLVM.General.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)
+ ]
+
+instance Monad m => EncodeM m (Maybe A.Atomicity) (FFI.LLVMBool, FFI.MemoryOrdering) where
+  encodeM a =
+    return (,) `ap` encodeM (maybe False A.crossThread a) `ap` encodeM (liftM A.memoryOrdering a)
+
+instance Monad m => DecodeM m (Maybe A.Atomicity) (FFI.LLVMBool, FFI.MemoryOrdering) where
+  decodeM (ss, ao) = return (liftM . A.Atomicity) `ap` decodeM ss `ap` decodeM ao
+
+instance Monad m => EncodeM m A.Atomicity (FFI.LLVMBool, FFI.MemoryOrdering) where
+  encodeM = encodeM . Just
+
+instance Monad m => DecodeM m A.Atomicity (FFI.LLVMBool, FFI.MemoryOrdering) where
+  decodeM = liftM fromJust . decodeM
+
diff --git a/src/LLVM/General/Internal/Attribute.hs b/src/LLVM/General/Internal/Attribute.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/Attribute.hs
@@ -0,0 +1,152 @@
+{-# LANGUAGE
+  TemplateHaskell,
+  MultiParamTypeClasses,
+  FlexibleInstances
+  #-}
+
+module LLVM.General.Internal.Attribute where
+
+import Language.Haskell.TH
+import Language.Haskell.TH.Quote
+
+import Data.Data
+import Data.List (genericSplitAt)
+
+import Data.Bits
+
+import qualified LLVM.General.Internal.FFI.LLVMCTypes as FFI
+
+import qualified LLVM.General.AST.Attribute as A.A
+
+import LLVM.General.Internal.Coding
+
+$(do
+  let
+    -- build an instance of the Coding class for lists of some kind of attribute
+    genAttributeListCoding :: (Data a, Bits a) => TypeQ -> Name -> [(a, String)] -> DecsQ
+    genAttributeListCoding type' ctn attributeData = do
+      let
+         lowZeroes :: Bits b => b -> Int
+         lowZeroes b | testBit b 0 = 0
+         lowZeroes b = 1 + lowZeroes (shiftR b 1)
+         field :: Bits b => b -> (Int, Int)
+         field b = 
+             let s = lowZeroes b
+                 w = lowZeroes (complement (shiftR b s))
+             in 
+               (s,w)
+         attributeData' = [(mkName n, b, s,w) | (b,n) <- attributeData, let (s,w) = field b ]
+      let m = varT . mkName $ "m"
+      TyConI (NewtypeD _ _ _ (NormalC ctcn _) _) <- reify ctn
+      let zero = [| $(conE ctcn) 0 |]
+      sequence [
+        instanceD (sequence [classP ''Monad [m]]) [t| EncodeM $(m) [$(type')] $(conT ctn) |] [
+          funD (mkName "encodeM") [
+            clause [] (normalB [| return . (
+              let 
+                 encodeAttribute a = $(
+                  caseE [| a |] $ flip map attributeData' $ \(n, b, s, w) ->
+                    let bQ = (dataToExpQ (const Nothing) b)
+                    in
+                      case w of
+                        1 -> match (conP n []) (normalB bQ) []
+                        _ -> do 
+                          a <- newName "a"
+                          match 
+                           (conP n [varP a])
+                           (normalB [| ($(conE ctcn) (fromIntegral $(varE a) `shiftL` s)) .&. $(bQ) |])
+                           []
+                  )
+              in
+                foldl (.|.) $(zero) . map encodeAttribute
+             ) |]) []
+           ]
+         ],
+ 
+        -- build a decoder which uses bit masking for multiple fields at once
+        -- to eliminate multiple absent attributes in fewer tests
+        instanceD (sequence [classP ''Monad [m]]) [t| DecodeM $(m) [$(type')] $(conT ctn) |] [
+          funD (mkName "decodeM") [
+            do
+              bits <- newName "bits"
+              clause [varP bits] (normalB 
+                (let
+                    code :: (Data a, Bits a) => [ (Name, a, Int, Int) ] -- attrs to handle
+                         -> Int -- length (attrs), passed in to avoid recomputation
+                         -> (a, ExpQ) -- (<bitmask for all the given attrs>, <code to decode the given attrs>)
+                    code [(n, b, s, w)] _ = (
+                       b, 
+                       case w of
+                         1 -> [| [ $(conE n) | testBit $(varE bits) s ] |]
+                         _-> [| 
+                               [ 
+                                 $(do
+                                    i' <- newName "i'"
+                                    letE 
+                                     [valD (conP ctcn [varP i']) (normalB [| i |]) []]
+                                     [| $(conE n) (fromIntegral $(varE i')) |])
+                                 | let i = ($(varE bits) .&. $(dataToExpQ (const Nothing) b)) `shiftR` s, 
+                                   i /= $(zero)
+                               ]
+                             |]
+                      )
+                    code attrs nAttrs =
+                      let (nEarlyAttrs, nLateAttrs) = (nAttrs `div` 2, nAttrs - nEarlyAttrs)
+                          (earlyAttrs, lateAttrs) = genericSplitAt nEarlyAttrs attrs
+                          (earlyAttrBits, earlyAttrCode) = code earlyAttrs nEarlyAttrs
+                          (lateAttrBits, lateAttrCode) = code lateAttrs nLateAttrs
+                          attrBits = earlyAttrBits .|. lateAttrBits
+                      in
+                        (
+                         attrBits, 
+                         [| 
+                            if ($(varE bits) .&. $(dataToExpQ (const Nothing) attrBits) /= $(zero)) then
+                              ($earlyAttrCode ++ $lateAttrCode) 
+                            else
+                              []
+                          |]
+                        )
+                in
+                  [| return $(snd $ code attributeData' (length attributeData')) |]
+                )
+               ) []
+            ]
+          ]
+        ]
+
+  pi <- genAttributeListCoding [t| A.A.ParameterAttribute |] ''FFI.ParamAttr [
+    (FFI.paramAttrZExt, "A.A.ZeroExt"),
+    (FFI.paramAttrSExt, "A.A.SignExt"),
+    (FFI.paramAttrInReg, "A.A.InReg"),
+    (FFI.paramAttrStructRet, "A.A.SRet"),
+    (FFI.paramAttrNoAlias, "A.A.NoAlias"),
+    (FFI.paramAttrByVal, "A.A.ByVal"),
+    (FFI.paramAttrNoCapture, "A.A.NoCapture"),
+    (FFI.paramAttrNest, "A.A.Nest")
+   ]
+
+  fi <- genAttributeListCoding [t| A.A.FunctionAttribute |] ''FFI.FunctionAttr [
+    (FFI.functionAttrNoReturn, "A.A.NoReturn"),
+    (FFI.functionAttrNoUnwind, "A.A.NoUnwind"),
+    (FFI.functionAttrReadNone, "A.A.ReadNone"),
+    (FFI.functionAttrReadOnly, "A.A.ReadOnly"),
+    (FFI.functionAttrNoInline, "A.A.NoInline"),
+    (FFI.functionAttrAlwaysInline, "A.A.AlwaysInline"),
+    (FFI.functionAttrOptimizeForSize, "A.A.OptimizeForSize"),
+    (FFI.functionAttrStackProtect, "A.A.StackProtect"),
+    (FFI.functionAttrStackProtectReq, "A.A.StackProtectReq"),
+    (FFI.functionAttrAlignment, "A.A.Alignment"),
+    (FFI.functionAttrNoRedZone, "A.A.NoRedZone"),
+    (FFI.functionAttrNoImplicitFloat, "A.A.NoImplicitFloat"),
+    (FFI.functionAttrNaked, "A.A.Naked"),
+    (FFI.functionAttrInlineHint, "A.A.InlineHint"),
+    (FFI.functionAttrStackAlignment, "A.A.StackAlignment"),
+    (FFI.functionAttrReturnsTwice, "A.A.ReturnsTwice"),
+    (FFI.functionAttrUWTable, "A.A.UWTable"),
+    (FFI.functionAttrNonLazyBind, "A.A.NonLazyBind")
+   ]
+  return (pi ++ fi)
+ )
+
+
+
diff --git a/src/LLVM/General/Internal/BasicBlock.hs b/src/LLVM/General/Internal/BasicBlock.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/BasicBlock.hs
@@ -0,0 +1,27 @@
+module LLVM.General.Internal.BasicBlock where
+
+import Control.Monad
+import Control.Monad.Trans
+import Control.Monad.Phased
+import Foreign.Ptr
+
+import qualified LLVM.General.Internal.FFI.PtrHierarchy as FFI
+import qualified LLVM.General.Internal.FFI.BasicBlock as FFI
+import qualified LLVM.General.Internal.FFI.Iterate as FFI
+
+import LLVM.General.Internal.DecodeAST
+import LLVM.General.Internal.Coding
+import LLVM.General.Internal.Instruction ()
+
+import qualified LLVM.General.AST.Instruction as A
+
+getBasicBlockTerminator :: Ptr FFI.BasicBlock -> DecodeAST (A.Named A.Terminator)
+getBasicBlockTerminator = decodeM <=< (liftIO . FFI.getBasicBlockTerminator)
+
+getNamedInstructions :: Ptr FFI.BasicBlock -> DecodeAST [A.Named A.Instruction]
+getNamedInstructions b = do
+  ffiInstructions <- liftIO $ FFI.getXs (FFI.getFirstInstruction b) FFI.getNextInstruction
+  let n = length ffiInstructions
+  forInterleavedM (take (n-1) ffiInstructions) $ decodeM
+
+  
diff --git a/src/LLVM/General/Internal/CallingConvention.hs b/src/LLVM/General/Internal/CallingConvention.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/CallingConvention.hs
@@ -0,0 +1,30 @@
+{-# LANGUAGE
+  MultiParamTypeClasses,
+  TemplateHaskell,
+  QuasiQuotes,
+  FlexibleInstances
+  #-}
+module LLVM.General.Internal.CallingConvention where
+
+import LLVM.General.Internal.Coding
+
+import qualified LLVM.General.Internal.FFI.LLVMCTypes as FFI
+
+import qualified LLVM.General.AST.CallingConvention as A.CC
+
+instance Monad m => EncodeM m A.CC.CallingConvention FFI.CallConv where
+  encodeM cc = return $ 
+        case cc of
+          A.CC.C -> FFI.callConvC
+          A.CC.Fast -> FFI.callConvFast
+          A.CC.Cold -> FFI.callConvCold
+          A.CC.GHC ->  FFI.CallConv 10
+          A.CC.Numbered cc' -> FFI.CallConv (fromIntegral cc')
+
+instance Monad m => DecodeM m A.CC.CallingConvention FFI.CallConv where
+  decodeM cc = return $ case cc of
+    [FFI.callConvP|C|] -> A.CC.C
+    [FFI.callConvP|Fast|] -> A.CC.Fast
+    [FFI.callConvP|Cold|] -> A.CC.Cold
+    FFI.CallConv 10 -> A.CC.GHC
+    FFI.CallConv ci | ci >= 64 -> A.CC.Numbered (fromIntegral ci)
diff --git a/src/LLVM/General/Internal/Coding.hs b/src/LLVM/General/Internal/Coding.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/Coding.hs
@@ -0,0 +1,125 @@
+{-# LANGUAGE
+  TemplateHaskell,
+  FlexibleInstances,
+  FlexibleContexts,
+  MultiParamTypeClasses,
+  FunctionalDependencies,
+  UndecidableInstances
+  #-}
+module LLVM.General.Internal.Coding where
+
+import Language.Haskell.TH
+import Language.Haskell.TH.Quote
+
+import Control.Monad.AnyCont
+import Control.Monad.IO.Class
+
+import Data.Data (Data)
+import Data.Word (Word32, Word64)
+import Data.Int (Int32)
+
+import Foreign.C
+import Foreign.Ptr
+import Foreign.Storable
+import Foreign.Marshal.Alloc
+import Foreign.Marshal.Array
+
+import qualified LLVM.General.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 h => TypeQ -> Name -> [(Integer, h)] -> Q [Dec]
+genCodingInstance ht ctn ihs = do
+  let n = const Nothing
+  TyConI (NewtypeD _ _ _ (NormalC ctcn _) _) <- reify ctn
+  [d| 
+    instance Monad m => EncodeM m $(ht) $(conT ctn) where
+      encodeM h = return $ $(
+        caseE [| h |] [ match (dataToPatQ n h) (normalB (appE (conE ctcn) (litE (integerL i)))) [] | (i,h) <- ihs ] 
+       )
+
+    instance Monad m => DecodeM m $(ht) $(conT ctn) where
+      decodeM i = return $ $(
+        caseE [| i |] [ match (conP ctcn [litP (integerL i)]) (normalB (dataToExpQ n h)) [] | (i,h) <- ihs ]
+       )
+   |]
+
+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 1) = return $ True
+
+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 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
+
diff --git a/src/LLVM/General/Internal/Constant.hs b/src/LLVM/General/Internal/Constant.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/Constant.hs
@@ -0,0 +1,220 @@
+{-# LANGUAGE
+  TemplateHaskell,
+  QuasiQuotes,
+  TupleSections,
+  MultiParamTypeClasses,
+  FlexibleInstances,
+  FlexibleContexts,
+  ScopedTypeVariables
+  #-}
+module LLVM.General.Internal.Constant where
+
+import qualified Language.Haskell.TH as TH
+import qualified Language.Haskell.TH.Quote as TH
+import qualified LLVM.General.Internal.InstructionDefs as ID
+
+import Control.Applicative
+import Data.Word (Word32, Word64)
+import Data.Bits
+import Control.Monad.State
+import Control.Monad.AnyCont
+
+import qualified Data.Map as Map
+import Foreign.Ptr
+import Foreign.Storable (Storable, sizeOf)
+
+import qualified LLVM.General.Internal.FFI.Constant as FFI
+import qualified LLVM.General.Internal.FFI.GlobalValue as FFI
+import qualified LLVM.General.Internal.FFI.Instruction as FFI
+import qualified LLVM.General.Internal.FFI.LLVMCTypes as FFI
+import qualified LLVM.General.Internal.FFI.PtrHierarchy as FFI
+import qualified LLVM.General.Internal.FFI.User as FFI
+import qualified LLVM.General.Internal.FFI.Value as FFI
+
+import qualified LLVM.General.AST.Constant as A (Constant)
+import qualified LLVM.General.AST.Constant as A.C hiding (Constant)
+import qualified LLVM.General.AST.Type as A
+import qualified LLVM.General.AST.IntegerPredicate as A (IntegerPredicate)
+import qualified LLVM.General.AST.FloatingPointPredicate as A (FloatingPointPredicate)
+import qualified LLVM.General.AST.Float as A.F
+
+import LLVM.General.Internal.Coding
+import LLVM.General.Internal.DecodeAST
+import LLVM.General.Internal.EncodeAST
+import LLVM.General.Internal.Context
+import LLVM.General.Internal.Type ()
+import LLVM.General.Internal.IntegerPredicate ()
+import LLVM.General.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
+      notPairOfFloats <- encodeM $ case v of A.F.PPC_FP128 _ _ -> False; _ -> True
+      nBits <- encodeM nBits
+      liftIO $ FFI.constantFloatOfArbitraryPrecision context nBits words notPairOfFloats
+    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 p ms -> do
+      Context context <- gets encodeStateContext
+      p <- encodeM p
+      ms <- encodeM ms
+      liftIO $ FFI.constStructInContext context ms p
+    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)) |] ]
+        core <- case instrInfo of
+          Just (_, iDef) -> do
+            let opcode = TH.dataToExpQ (const Nothing) (ID.cppOpcode iDef)
+            case ID.instructionKind iDef of
+              ID.Binary -> return [| $(coreCall "BinaryOperator") $(opcode) |]
+              ID.Cast -> return [| $(coreCall "Cast") $(opcode) |]
+              _ -> return $ coreCall name
+          Nothing -> if (name `elem` ["Vector", "Null", "Array"]) 
+                      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
+    t <- decodeM =<< liftIO (FFI.typeOf v)
+    valueSubclassId <- liftIO $ FFI.getValueSubclassId v
+    nOps <- liftIO $ FFI.getNumOperands u
+    let globalRef = return A.C.GlobalReference `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
+      [FFI.valueSubclassIdP|Function|] -> globalRef
+      [FFI.valueSubclassIdP|GlobalAlias|] -> globalRef
+      [FFI.valueSubclassIdP|GlobalVariable|] -> globalRef
+      [FFI.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 :: Word64)) 0 words)
+      [FFI.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
+          )
+      [FFI.valueSubclassIdP|ConstantPointerNull|] -> return $ A.C.Null t
+      [FFI.valueSubclassIdP|ConstantAggregateZero|] -> return $ A.C.Null t
+      [FFI.valueSubclassIdP|UndefValue|] -> return $ A.C.Undef t
+      [FFI.valueSubclassIdP|BlockAddress|] -> 
+            return A.C.BlockAddress 
+               `ap` (getGlobalName =<< do liftIO $ FFI.isAGlobalValue =<< FFI.getBlockAddressFunction c)
+               `ap` (getLocalName =<< do liftIO $ FFI.getBlockAddressBlock c)
+      [FFI.valueSubclassIdP|ConstantStruct|] -> 
+            return A.C.Struct `ap` (return $ A.isPacked t) `ap` getConstantOperands
+      [FFI.valueSubclassIdP|ConstantDataArray|] -> 
+            return A.C.Array `ap` (return $ A.elementType t) `ap` getConstantData
+      [FFI.valueSubclassIdP|ConstantArray|] -> 
+            return A.C.Array `ap` (return $ A.elementType t) `ap` getConstantOperands
+      [FFI.valueSubclassIdP|ConstantDataVector|] -> 
+            return A.C.Vector `ap` getConstantData
+      [FFI.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 |]
+                             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))
+                          []
+             )
+      _ -> error $ "unhandled constant valueSubclassId: " ++ show valueSubclassId
+
+
+  
+  
diff --git a/src/LLVM/General/Internal/Context.hs b/src/LLVM/General/Internal/Context.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/Context.hs
@@ -0,0 +1,16 @@
+module LLVM.General.Internal.Context where
+
+import Control.Exception
+
+import Foreign.Ptr
+
+import qualified LLVM.General.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 = bracket FFI.contextCreate FFI.contextDispose . (. Context)
diff --git a/src/LLVM/General/Internal/DataLayout.hs b/src/LLVM/General/Internal/DataLayout.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/DataLayout.hs
@@ -0,0 +1,88 @@
+module LLVM.General.Internal.DataLayout where
+
+import Text.ParserCombinators.Parsec
+
+import Data.Word
+import Data.Functor
+
+import qualified Data.List as List
+import qualified Data.Map as Map
+import qualified Data.Set as Set
+
+import LLVM.General.AST.DataLayout
+import LLVM.General.AST.AddrSpace
+
+dataLayoutToString :: DataLayout -> String
+dataLayoutToString dl = 
+  let sTriple :: (Word32, AlignmentInfo) -> String
+      sTriple (s, ai) = show s ++ ":" ++ show (abiAlignment ai) ++ ":" ++ show (preferredAlignment ai)
+      atChar at = case at of
+                    IntegerAlign -> "i"
+                    VectorAlign -> "v"
+                    FloatAlign -> "f"
+                    AggregateAlign -> "a"
+                    StackAlign -> "s"
+  in
+  List.intercalate "-" (
+    (case endianness dl of Just BigEndian -> ["E"]; Just LittleEndian -> ["e"]; _ -> [])
+    ++
+    (maybe [] (\s -> ["S" ++ show s]) (stackAlignment dl))
+    ++
+    [ "p" ++ show a ++ ":" ++ sTriple t | (AddrSpace a, t) <- Map.toList . pointerLayouts $ dl]
+    ++
+    [ atChar at ++ sTriple (s, ai) | ((at, s), ai) <- Map.toList . typeLayouts $ dl ]
+    ++ 
+    (maybe [] (\ns -> ["n" ++ (List.intercalate ":" (map show . Set.toList $ ns))]) (nativeSizes dl))
+  )
+
+parseDataLayout :: String -> Maybe DataLayout
+parseDataLayout "" = Nothing
+parseDataLayout s = 
+  let
+    num :: Parser Word32
+    num = read <$> many digit
+    triple :: Parser (Word32, AlignmentInfo)
+    triple = do
+      s <- num
+      char ':'
+      abi <- num
+      char ':'
+      pref <- num
+      return (s, (AlignmentInfo abi pref))
+    parseSpec :: Parser (DataLayout -> DataLayout)
+    parseSpec = choice [
+      do
+        char 'e'
+        return $ \dl -> dl { endianness = Just LittleEndian },
+      do
+        char 'E' 
+        return $ \dl -> dl { endianness = Just BigEndian },
+      do
+        char 'S'
+        n <- num
+        return $ \dl -> dl { stackAlignment = Just n },
+      do
+        char 'p'
+        a <- AddrSpace . read <$> many digit
+        char ':'
+        t <- triple
+        return $ \dl -> dl { pointerLayouts = Map.insert a t (pointerLayouts dl) },
+      do
+        at <- choice [
+               char 'i' >> return IntegerAlign,
+               char 'v' >> return VectorAlign,
+               char 'f' >> return FloatAlign,
+               char 'a' >> return AggregateAlign,
+               char 's' >> return StackAlign
+              ]
+        (sz,ai) <- triple
+        return $ \dl -> dl { typeLayouts = Map.insert (at,sz) ai (typeLayouts dl) },
+      do 
+        char 'n'
+        ns <- num `sepBy` (char ':')
+        return $ \dl -> dl { nativeSizes = Just (Set.fromList ns) }
+     ]
+  in 
+    case parse (parseSpec `sepBy` (char '-')) "" s of
+      Left _ -> error $ "ill formed data layout: " ++ show s
+      Right fs -> Just $ foldr ($) defaultDataLayout fs
diff --git a/src/LLVM/General/Internal/DecodeAST.hs b/src/LLVM/General/Internal/DecodeAST.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/DecodeAST.hs
@@ -0,0 +1,171 @@
+{-# LANGUAGE
+  GeneralizedNewtypeDeriving,
+  FlexibleContexts,
+  FlexibleInstances,
+  TypeFamilies,
+  MultiParamTypeClasses,
+  UndecidableInstances
+  #-}
+module LLVM.General.Internal.DecodeAST where
+
+import Control.Applicative
+import Control.Monad.State
+import Control.Monad.Phased
+import Control.Monad.AnyCont
+
+import Foreign.Ptr
+import Foreign.C
+import Data.Word
+
+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.General.Internal.FFI.PtrHierarchy as FFI
+import qualified LLVM.General.Internal.FFI.Value as FFI
+import qualified LLVM.General.Internal.FFI.Type as FFI
+
+import qualified LLVM.General.AST.Name as A
+import qualified LLVM.General.AST.Operand as A (MetadataNodeID(..))
+
+import LLVM.General.Internal.Coding
+import LLVM.General.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
+  }
+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) []
+  }
+newtype DecodeAST a = DecodeAST { unDecodeAST :: AnyContT (PhasedT (StateT DecodeState IO)) a }
+  deriving (
+    Applicative,
+    Functor,
+    Monad,
+    MonadIO,
+    MonadState DecodeState,
+    MonadPhased
+  )
+
+instance MonadAnyCont IO DecodeAST where
+  anyContToM = DecodeAST . anyContIOToM
+  scopeAnyCont = DecodeAST . scopeAnyCont . unDecodeAST
+
+runDecodeAST :: DecodeAST a -> IO a
+runDecodeAST d = flip evalStateT initialDecode . runPhasedT . flip runAnyContT return . unDecodeAST $ d
+
+localScope :: DecodeAST a -> DecodeAST a
+localScope (DecodeAST x) = DecodeAST (mapAnyContT pScope (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
+        pScope (PhasedT x) = PhasedT $ do
+          let s0 `withLocalsFrom` s1 = s0 { 
+                localNameCounter = localNameCounter s1
+               }
+          state <- get -- save the state
+          a <- x
+          state' <- get -- get the modified state
+          put $ state' `withLocalsFrom` state -- revert the local part
+          -- Finally here's the fun bit - in the Left case where we're coming back to a deferment point,
+          -- prepend an action which reinstates the local state, but re-wrap with pScope to continue
+          -- containment.
+          return $ either (Left . pScope . (modify (`withLocalsFrom` state') >>)) Right a
+
+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
diff --git a/src/LLVM/General/Internal/Diagnostic.hs b/src/LLVM/General/Internal/Diagnostic.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/Diagnostic.hs
@@ -0,0 +1,40 @@
+{-# LANGUAGE
+  TemplateHaskell,
+  DeriveDataTypeable,
+  MultiParamTypeClasses,
+  FlexibleInstances
+  #-}  
+module LLVM.General.Internal.Diagnostic where
+
+import qualified LLVM.General.Internal.FFI.LLVMCTypes as FFI
+import qualified LLVM.General.Internal.FFI.SMDiagnostic as FFI
+
+import Control.Exception
+
+import Foreign.Ptr
+
+import LLVM.General.Diagnostic
+import LLVM.General.Internal.Coding
+import LLVM.General.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/General/Internal/EncodeAST.hs b/src/LLVM/General/Internal/EncodeAST.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/EncodeAST.hs
@@ -0,0 +1,146 @@
+{-# LANGUAGE
+  GeneralizedNewtypeDeriving,
+  FlexibleContexts,
+  FlexibleInstances,
+  MultiParamTypeClasses,
+  UndecidableInstances
+  #-}
+
+module LLVM.General.Internal.EncodeAST where
+
+import Control.Exception
+import Control.Monad.State
+import Control.Monad.Phased
+import Control.Monad.Error
+import Control.Monad.AnyCont
+
+import Foreign.Ptr
+import Foreign.C
+
+import Data.Map (Map)
+import qualified Data.Map as Map
+
+import qualified LLVM.General.Internal.FFI.PtrHierarchy as FFI
+import qualified LLVM.General.Internal.FFI.Builder as FFI
+import qualified LLVM.General.Internal.FFI.Type as FFI
+
+import qualified LLVM.General.AST as A
+
+import LLVM.General.Internal.Context
+import LLVM.General.Internal.Coding
+import LLVM.General.Internal.String ()
+
+data EncodeState = EncodeState { 
+      encodeStateBuilder :: Ptr FFI.Builder,
+      encodeStateContext :: Context,
+      encodeStateLocals :: Map A.Name (Ptr FFI.Value),
+      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)
+    }
+
+newtype EncodeAST a = EncodeAST { unEncodeAST :: AnyContT (PhasedT (ErrorT String (StateT EncodeState IO))) a }
+    deriving (
+       Functor,
+       Monad,
+       MonadIO,
+       MonadState EncodeState,
+       MonadPhased,
+       MonadError String
+     )
+
+instance MonadAnyCont IO EncodeAST where
+  anyContToM = EncodeAST . anyContIOToM
+  scopeAnyCont = EncodeAST . scopeAnyCont . unEncodeAST
+
+lookupNamedType :: A.Name -> EncodeAST (Ptr FFI.Type)
+lookupNamedType n = do
+  t <- gets $ Map.lookup n . encodeStateNamedTypes
+  maybe (fail $ "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 -> IO (Either String a)
+runEncodeAST context@(Context ctx) (EncodeAST a) = 
+    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
+            }
+      flip evalStateT initEncodeState . runErrorT . runPhasedT . 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 ""
+
+-- contain modifications to the local part of the encode state - in this case all those except
+-- those to encodeStateAllBlocks
+encodeScope :: EncodeAST a -> EncodeAST a
+encodeScope (EncodeAST x) = 
+  EncodeAST . mapAnyContT pScope $ x -- get inside the boring wrappers down to the phasing
+  where pScope (PhasedT x) = PhasedT $ do
+          let s0 `withLocalsFrom` s1 = s0 { 
+                 encodeStateLocals = encodeStateLocals s1,
+                 encodeStateBlocks = encodeStateBlocks s1
+                }
+          state <- get -- save the state
+          a <- x
+          state' <- get -- get the modified state
+          put $ state' `withLocalsFrom` state -- revert the local part
+          -- Finally here's the fun bit - in the Left case where we're coming back to a deferment point,
+          -- prepend an action which reinstates the local state, but re-wrap with pScope to continue
+          -- containment.
+          return $ either (Left . pScope . (modify (`withLocalsFrom` state') >>)) Right a
+
+
+define :: (Ord n, FFI.DescendentOf p v) => 
+          (EncodeState -> Map n (Ptr p))
+          -> (Map n (Ptr p) -> EncodeState -> EncodeState)
+          -> n
+          -> Ptr v
+          -> EncodeAST ()
+define r w n v = modify $ \b -> w (Map.insert n (FFI.upCast v) (r b)) b
+
+defineLocal :: FFI.DescendentOf FFI.Value v => A.Name -> Ptr v -> EncodeAST ()
+defineLocal = define encodeStateLocals (\m b -> b { encodeStateLocals = m })
+
+defineGlobal :: FFI.DescendentOf FFI.GlobalValue v => A.Name -> Ptr v -> EncodeAST ()
+defineGlobal = define encodeStateGlobals (\m b -> b { encodeStateGlobals = m })
+
+defineMDNode :: A.MetadataNodeID -> Ptr FFI.MDNode -> EncodeAST ()
+defineMDNode = define encodeStateMDNodes (\m b -> b { encodeStateMDNodes = m })
+
+refer :: (Show n, Ord n) => (EncodeState -> Map n (Ptr p)) -> String -> n -> EncodeAST (Ptr p)
+refer r m n = do
+  mop <- gets $ Map.lookup n . r
+  maybe (fail $ "reference to undefined " ++ m ++ ": " ++ show n) return mop
+
+referLocal = refer encodeStateLocals "local"
+referGlobal = refer encodeStateGlobals "global"
+referMDNode = refer encodeStateMDNodes "metadata node"
+
+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 = refer encodeStateBlocks "block"
+
+getBlockForAddress :: A.Name -> A.Name -> EncodeAST (Ptr FFI.BasicBlock)
+getBlockForAddress fn n = refer encodeStateAllBlocks "blockaddress" (fn, n)
+
diff --git a/src/LLVM/General/Internal/ExecutionEngine.hs b/src/LLVM/General/Internal/ExecutionEngine.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/ExecutionEngine.hs
@@ -0,0 +1,66 @@
+module LLVM.General.Internal.ExecutionEngine where
+
+import Control.Exception
+import Control.Monad
+import Control.Monad.IO.Class
+import Control.Monad.AnyCont
+import Data.Functor
+
+import Foreign.Ptr
+import Foreign.Marshal.Alloc (free)
+
+import qualified LLVM.General.Internal.FFI.PtrHierarchy as FFI
+import qualified LLVM.General.Internal.FFI.ExecutionEngine as FFI
+import qualified LLVM.General.Internal.FFI.Target as FFI
+import qualified LLVM.General.Internal.FFI.Module as FFI
+
+import LLVM.General.Internal.Module
+import LLVM.General.Internal.Context
+import LLVM.General.Internal.Coding
+
+import qualified LLVM.General.AST as A
+
+-- | <http://llvm.org/doxygen/classllvm_1_1ExecutionEngine.html>
+newtype ExecutionEngine = ExecutionEngine (Ptr FFI.ExecutionEngine)
+
+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"
+
+
+-- | bracket the creation and destruction of an 'ExecutionEngine'
+withExecutionEngine :: Context -> (ExecutionEngine -> IO a) -> IO a
+withExecutionEngine c f = flip runAnyContT return $ do
+  liftIO $ FFI.initializeNativeTarget
+  outExecutionEngine <- alloca
+  outErrorCStringPtr <- alloca
+  Module dummyModule <- anyContT $ liftM (either undefined id) . withModuleFromAST c (A.Module "" Nothing Nothing [])
+  r <- liftIO $ FFI.createExecutionEngineForModule outExecutionEngine dummyModule outErrorCStringPtr
+  when (r /= 0) $ do
+    s <- anyContT $ bracket (peek outErrorCStringPtr) free
+    fail =<< decodeM s
+  executionEngine <- anyContT $ bracket (peek outExecutionEngine) FFI.disposeExecutionEngine
+  liftIO $ removeModule executionEngine dummyModule
+  liftIO $ f (ExecutionEngine executionEngine)
+          
+      
+-- | bracket the availability of machine code for a given 'Module' in an 'ExecutionEngine'.
+-- See 'findFunction'.
+withModuleInEngine :: ExecutionEngine -> Module -> IO a -> IO a
+withModuleInEngine (ExecutionEngine e) (Module m) = bracket_ (FFI.addModule e m) (removeModule e m)
+
+-- | While a 'Module' is in an 'ExecutionEngine', use 'findFunction' to lookup functions in the module.
+-- To run them from Haskell, treat them as any other function pointer: cast them to an appropriate and
+-- foreign type, then wrap them with a dynamic FFI stub.
+findFunction :: ExecutionEngine -> A.Name -> IO (Maybe (Ptr ()))
+findFunction (ExecutionEngine e) (A.Name fName) = flip runAnyContT return $ do
+  out <- alloca
+  fName <- encodeM fName
+  r <- liftIO $ FFI.findFunction e fName out
+  if (r /= 0) then 
+      return Nothing
+   else 
+      Just <$> (liftIO $ FFI.getPointerToGlobal e . FFI.upCast =<< peek out)
diff --git a/src/LLVM/General/Internal/FFI/Assembly.hs b/src/LLVM/General/Internal/FFI/Assembly.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/Assembly.hs
@@ -0,0 +1,24 @@
+{-# LANGUAGE
+  ForeignFunctionInterface
+  #-}
+
+-- | Functions to read and write textual LLVM assembly
+module LLVM.General.Internal.FFI.Assembly where
+
+import LLVM.General.Internal.FFI.Context 
+import LLVM.General.Internal.FFI.Module
+import LLVM.General.Internal.FFI.SMDiagnostic
+
+import Foreign.C
+import Foreign.Ptr
+
+-- | Use LLVM's parser to parse a string of llvm assembly to get a module
+foreign import ccall unsafe "LLVM_General_GetModuleFromAssemblyInContext" getModuleFromAssemblyInContext ::
+    Ptr Context -> CString -> Ptr SMDiagnostic -> IO (Ptr Module)
+
+-- | LLVM's serializer to generate a string of llvm assembly from a module
+foreign import ccall unsafe "LLVM_General_GetModuleAssembly" getModuleAssembly ::
+    Ptr Module -> IO CString
+
+
+
diff --git a/src/LLVM/General/Internal/FFI/AssemblyC.cpp b/src/LLVM/General/Internal/FFI/AssemblyC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/AssemblyC.cpp
@@ -0,0 +1,33 @@
+#define __STDC_LIMIT_MACROS
+#include "llvm/LLVMContext.h"
+#include "llvm/Assembly/Parser.h"
+#include "llvm/Assembly/PrintModulePass.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include "llvm-c/Core.h"
+
+using namespace llvm;
+
+extern "C" {
+
+LLVMModuleRef LLVM_General_GetModuleFromAssemblyInContext(
+	LLVMContextRef context,
+	const char *assembly,
+	SMDiagnostic *error
+) {
+	wrap(ParseAssemblyString(assembly, NULL, *error, *unwrap(context))); 
+}
+
+char *LLVM_General_GetModuleAssembly(LLVMModuleRef module) {
+	std::string s;
+	raw_string_ostream buf(s);
+	ModulePass *printPass = createPrintModulePass(&buf);
+	printPass->runOnModule(*unwrap(module));
+	delete printPass;
+	return strdup(buf.str().c_str());
+}
+
+}
+
diff --git a/src/LLVM/General/Internal/FFI/BasicBlock.hs b/src/LLVM/General/Internal/FFI/BasicBlock.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/BasicBlock.hs
@@ -0,0 +1,33 @@
+{-# LANGUAGE
+  ForeignFunctionInterface,
+  EmptyDataDecls,
+  MultiParamTypeClasses
+  #-}
+
+-- | <http://llvm.org/doxygen/classllvm_1_1BasicBlock.html>
+module LLVM.General.Internal.FFI.BasicBlock where
+
+import Foreign.Ptr
+
+import LLVM.General.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/General/Internal/FFI/BinaryOperator.hs b/src/LLVM/General/Internal/FFI/BinaryOperator.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/BinaryOperator.hs
@@ -0,0 +1,35 @@
+{-# LANGUAGE
+  ForeignFunctionInterface,
+  EmptyDataDecls,
+  MultiParamTypeClasses,
+  FlexibleInstances,
+  UndecidableInstances,
+  OverlappingInstances
+  #-}
+
+-- | FFI functions for handling the LLVM BinaryOperator class
+module LLVM.General.Internal.FFI.BinaryOperator where
+
+import Foreign.Ptr
+import Foreign.C
+
+import LLVM.General.Internal.FFI.PtrHierarchy
+import LLVM.General.Internal.FFI.LLVMCTypes
+
+-- | a blind type to correspond to llvm::BinaryOperator
+data BinaryOperator
+
+instance ChildOf Instruction BinaryOperator
+
+foreign import ccall unsafe "LLVMIsABinaryOperator" isABinaryOperator ::
+    Ptr Value -> IO (Ptr BinaryOperator)
+
+foreign import ccall unsafe "LLVM_General_HasNoSignedWrap" hasNoSignedWrap ::
+    Ptr BinaryOperator -> IO LLVMBool
+
+foreign import ccall unsafe "LLVM_General_HasNoUnsignedWrap" hasNoUnsignedWrap ::
+    Ptr BinaryOperator -> IO LLVMBool
+
+foreign import ccall unsafe "LLVM_General_IsExact" isExact ::
+    Ptr BinaryOperator -> IO LLVMBool
+
diff --git a/src/LLVM/General/Internal/FFI/Builder.hs b/src/LLVM/General/Internal/FFI/Builder.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/Builder.hs
@@ -0,0 +1,152 @@
+{-#
+  LANGUAGE
+  ForeignFunctionInterface,
+  EmptyDataDecls,
+  TemplateHaskell
+  #-}
+
+-- | FFI glue for llvm::IRBuilder - llvm's IR construction state object
+module LLVM.General.Internal.FFI.Builder where
+
+import qualified Language.Haskell.TH as TH
+
+import qualified LLVM.General.AST.Instruction as A
+import qualified LLVM.General.AST.Operand as A
+import qualified LLVM.General.AST.Type as A
+
+import LLVM.General.Internal.InstructionDefs as ID
+
+import Foreign.Ptr
+import Foreign.C
+
+import qualified Data.Map as Map
+
+import LLVM.General.Internal.FFI.LLVMCTypes
+import LLVM.General.Internal.FFI.Context
+import LLVM.General.Internal.FFI.BinaryOperator
+import LLVM.General.Internal.FFI.Type
+import LLVM.General.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)
+
+
+$(do
+  sequence [
+     TH.forImpD TH.cCall TH.unsafe cName (TH.mkName ("build" ++ a)) [t| 
+       Ptr Builder -> $(foldr (\at rt -> [t| $(at) -> $(rt) |]) [t| CString -> IO (Ptr $(rt)) |] ats)
+       |]
+     | (lrn, ID.InstructionDef { ID.cAPIName = a, ID.instructionKind = k }) <- Map.toList ID.instructionDefs,
+       let fieldTypes = 
+             (\(TH.RecC _ fs) -> [ t | (_,_,t) <- fs]) $
+             Map.findWithDefault 
+                  (error $ "LLVM instruction not found in AST: " ++ show lrn)
+                  lrn ID.astInstructionRecs
+           ats = flip concatMap fieldTypes $ \ft ->
+                 case ft of
+                   TH.ConT h | h == ''Bool -> [[t| LLVMBool |]]
+                             | h == ''A.Operand -> [[t| Ptr Value |]]
+                             | h == ''A.Type -> [[t| Ptr Type |]]
+                             | h == ''A.InstructionMetadata -> []
+                   _ -> error $ "show ft = " ++ show ft
+           cName = (if TH.ConT ''Bool `elem` fieldTypes then "LLVM_General_" else "LLVM") ++ "Build" ++ a,
+       rt <- case k of
+               ID.Binary -> [[t| BinaryOperator |]]
+               ID.Cast -> [[t| Instruction |]]
+               _ -> []
+    ]
+ )
+
+foreign import ccall unsafe "LLVMBuildArrayAlloca" buildAlloca ::
+  Ptr Builder -> Ptr Type -> Ptr Value -> CString -> IO (Ptr Instruction)
+
+foreign import ccall unsafe "LLVM_General_BuildLoad" buildLoad ::
+  Ptr Builder -> Ptr Value -> CUInt -> LLVMBool -> MemoryOrdering -> LLVMBool -> CString -> IO (Ptr Instruction)
+
+foreign import ccall unsafe "LLVM_General_BuildStore" buildStore ::
+  Ptr Builder -> Ptr Value -> Ptr Value -> CUInt -> LLVMBool -> MemoryOrdering -> LLVMBool -> CString -> IO (Ptr Instruction)
+
+foreign import ccall unsafe "LLVMBuildGEP" buildGetElementPtr ::
+  Ptr Builder -> Ptr Value -> Ptr (Ptr Value) -> CUInt -> CString -> IO (Ptr Instruction)
+
+foreign import ccall unsafe "LLVMBuildFence" buildFence ::
+  Ptr Builder -> MemoryOrdering -> LLVMBool -> CString -> IO (Ptr Instruction)
+
+foreign import ccall unsafe "LLVM_General_BuildAtomicCmpXchg" buildCmpXchg ::
+  Ptr Builder -> Ptr Value -> Ptr Value -> Ptr Value -> LLVMBool -> MemoryOrdering -> LLVMBool -> CString -> IO (Ptr Instruction)
+
+foreign import ccall unsafe "LLVM_General_BuildAtomicRMW" buildAtomicRMW ::
+  Ptr Builder -> RMWOperation -> Ptr Value -> Ptr Value -> LLVMBool -> MemoryOrdering -> LLVMBool -> CString -> IO (Ptr Instruction)
+
+
+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_General_BuildExtractValue" buildExtractValue ::
+  Ptr Builder -> Ptr Value -> Ptr CUInt -> CUInt -> CString -> IO (Ptr Instruction)
+
+foreign import ccall unsafe "LLVM_General_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)
+
+
diff --git a/src/LLVM/General/Internal/FFI/BuilderC.cpp b/src/LLVM/General/Internal/FFI/BuilderC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/BuilderC.cpp
@@ -0,0 +1,181 @@
+#define __STDC_LIMIT_MACROS
+#include "llvm/LLVMContext.h"
+#include "llvm/IRBuilder.h"
+
+#include "llvm-c/Core.h"
+
+#include "LLVM/General/Internal/FFI/Instruction.h"
+
+using namespace llvm;
+
+namespace llvm {
+static AtomicOrdering unwrap(LLVMAtomicOrdering l) {
+	switch(l) {
+#define ENUM_CASE(x) case LLVM ## x ## AtomicOrdering: return x;
+LLVM_GENERAL_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_GENERAL_FOR_EACH_SYNCRONIZATION_SCOPE(ENUM_CASE)
+#undef ENUM_CASE
+	default: return SynchronizationScope(0);
+	}
+}
+
+
+static AtomicRMWInst::BinOp unwrap(LLVMAtomicRMWOperation l) {
+	switch(l) {
+#define ENUM_CASE(x) case LLVM ## x ## RMWOperation: return AtomicRMWInst::x;
+LLVM_GENERAL_FOR_EACH_RMW_OPERATION(ENUM_CASE)
+#undef ENUM_CASE
+	default: return AtomicRMWInst::BinOp(0);
+	}
+}
+
+}
+
+extern "C" {
+
+#define LLVM_GENERAL_FOR_ALL_OVERFLOWING_BINARY_OPERATORS(macro) \
+	macro(Add) \
+	macro(Mul) \
+	macro(Shl) \
+	macro(Sub) \
+
+#define ENUM_CASE(Op)																										\
+LLVMValueRef LLVM_General_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_GENERAL_FOR_ALL_OVERFLOWING_BINARY_OPERATORS(ENUM_CASE)
+#undef ENUM_CASE
+
+#define LLVM_GENERAL_FOR_ALL_POSSIBLY_EXACT_OPERATORS(macro) \
+	macro(AShr) \
+	macro(LShr) \
+	macro(SDiv) \
+	macro(UDiv) \
+
+#define ENUM_CASE(Op)																										\
+LLVMValueRef LLVM_General_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_GENERAL_FOR_ALL_POSSIBLY_EXACT_OPERATORS(ENUM_CASE)
+#undef ENUM_CASE
+
+
+LLVMValueRef LLVM_General_BuildLoad(
+	LLVMBuilderRef b,
+	LLVMValueRef p,
+	unsigned align,
+	LLVMBool isVolatile,
+	LLVMAtomicOrdering atomicOrdering,
+	LLVMSynchronizationScope synchScope,
+	const char *name
+) {
+	LoadInst *i = unwrap(b)->CreateAlignedLoad(unwrap(p), align, isVolatile, name);
+	i->setOrdering(unwrap(atomicOrdering));
+	i->setSynchScope(unwrap(synchScope));
+	return wrap(i);
+}
+
+LLVMValueRef LLVM_General_BuildStore(
+	LLVMBuilderRef b,
+	LLVMValueRef v,
+	LLVMValueRef p,
+	unsigned align,
+	LLVMBool isVolatile,
+	LLVMAtomicOrdering atomicOrdering,
+	LLVMSynchronizationScope synchScope,
+	const char *name
+) {
+	StoreInst *i = unwrap(b)->CreateAlignedStore(unwrap(v), unwrap(p), align, isVolatile);
+	i->setName(name);
+	i->setOrdering(unwrap(atomicOrdering));
+	i->setSynchScope(unwrap(synchScope));
+	return wrap(i);
+}
+
+LLVMValueRef LLVMBuildFence(
+	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_General_BuildAtomicCmpXchg(
+	LLVMBuilderRef b,
+	LLVMValueRef ptr, 
+	LLVMValueRef cmp, 
+	LLVMValueRef n, 
+	LLVMBool v,
+	LLVMAtomicOrdering lao,
+	LLVMSynchronizationScope lss,
+	const char *name
+) {
+	AtomicCmpXchgInst *a = unwrap(b)->CreateAtomicCmpXchg(
+		unwrap(ptr), unwrap(cmp), unwrap(n), unwrap(lao), unwrap(lss)
+	);
+	a->setVolatile(v);
+	a->setName(name);
+	return wrap(a);
+}
+
+LLVMValueRef LLVM_General_BuildAtomicRMW(
+	LLVMBuilderRef b,
+	LLVMAtomicRMWOperation rmwOp,
+	LLVMValueRef ptr, 
+	LLVMValueRef val, 
+	LLVMBool v,
+	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_General_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_General_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));
+}
+
+}
diff --git a/src/LLVM/General/Internal/FFI/Cleanup.hs b/src/LLVM/General/Internal/FFI/Cleanup.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/Cleanup.hs
@@ -0,0 +1,73 @@
+{-# LANGUAGE
+  TemplateHaskell
+  #-}
+module LLVM.General.Internal.FFI.Cleanup where
+
+import Language.Haskell.TH
+import Control.Monad
+import Data.Sequence as Seq
+import Data.Foldable (toList)
+
+import Data.Function
+
+import LLVM.General.Internal.FFI.LLVMCTypes
+import Data.Word
+import Data.Int
+import Foreign.C
+import Foreign.Ptr
+
+import qualified LLVM.General.AST.IntegerPredicate as A (IntegerPredicate) 
+import qualified LLVM.General.AST.FloatingPointPredicate as A (FloatingPointPredicate) 
+
+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))) []
+    ]
+   ]
+
+typeMappingU :: (Type -> TypeQ) -> Type -> TypeQ
+typeMappingU 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.FloatingPointPredicate -> [t| FCmpPredicate |]
+         | h == ''A.IntegerPredicate -> [t| ICmpPredicate |]
+  AppT ListT x -> foldl1 appT [tupleT 2, [t| CUInt |], appT [t| Ptr |] (typeMapping x)]
+
+typeMapping :: Type -> TypeQ
+typeMapping = fix typeMappingU
diff --git a/src/LLVM/General/Internal/FFI/Constant.hs b/src/LLVM/General/Internal/FFI/Constant.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/Constant.hs
@@ -0,0 +1,136 @@
+{-# LANGUAGE
+  TemplateHaskell,
+  ForeignFunctionInterface,
+  EmptyDataDecls,
+  MultiParamTypeClasses,
+  FlexibleInstances,
+  UndecidableInstances,
+  OverlappingInstances
+  #-}
+
+-- | FFI functions for handling the LLVM Constant class
+module LLVM.General.Internal.FFI.Constant where
+
+import qualified Language.Haskell.TH as TH
+import qualified LLVM.General.Internal.InstructionDefs as ID
+import qualified LLVM.General.AST.Constant as A.C
+
+import Control.Monad
+import Data.Function
+import qualified Data.Map as Map
+
+import Foreign.Ptr
+import Foreign.C
+
+import LLVM.General.Internal.FFI.PtrHierarchy
+import LLVM.General.Internal.FFI.Context
+import LLVM.General.Internal.FFI.Cleanup
+import LLVM.General.Internal.FFI.Type
+import LLVM.General.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_General_GetConstantIntWords" getConstantIntWords ::
+  Ptr Constant -> Ptr CUInt -> IO (Ptr CULong)
+
+foreign import ccall unsafe "LLVM_General_ConstFloatDoubleValue" constFloatDoubleValue ::
+  Ptr Constant -> IO CDouble
+
+foreign import ccall unsafe "LLVM_General_ConstFloatFloatValue" constFloatFloatValue ::
+  Ptr Constant -> IO CFloat
+
+foreign import ccall unsafe "LLVMConstStructInContext" constStructInContext' ::
+  Ptr Context -> Ptr (Ptr Constant) -> CUInt -> LLVMBool -> IO (Ptr Constant)
+
+constStructInContext ctx (n, cs) p = constStructInContext' ctx cs n p
+
+foreign import ccall unsafe "LLVM_General_GetConstantDataSequentialElementAsConstant" getConstantDataSequentialElementAsConstant ::
+  Ptr Constant -> CUInt -> IO (Ptr Constant)
+
+foreign import ccall unsafe "LLVMConstIntOfArbitraryPrecision" constantIntOfArbitraryPrecision' ::
+  Ptr Type -> CUInt -> Ptr CULong -> IO (Ptr Constant)
+
+constantIntOfArbitraryPrecision t = uncurry (constantIntOfArbitraryPrecision' t)
+
+foreign import ccall unsafe "LLVM_General_ConstFloatOfArbitraryPrecision" constantFloatOfArbitraryPrecision ::
+  Ptr Context -> CUInt -> Ptr CULong -> LLVMBool -> IO (Ptr Constant)
+
+foreign import ccall unsafe "LLVM_General_GetConstantFloatWords" getConstantFloatWords ::
+  Ptr Constant -> Ptr CULong -> IO ()
+
+foreign import ccall unsafe "LLVMConstVector" constantVector' ::
+  Ptr (Ptr Constant) -> CUInt -> 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 t (n, cs) = constantArray' t cs n
+
+foreign import ccall unsafe "LLVM_General_ConstCast" constantCast ::
+  CPPOpcode -> Ptr Constant -> Ptr Type -> IO (Ptr Constant)
+
+foreign import ccall unsafe "LLVM_General_ConstBinaryOperator" constantBinaryOperator ::
+  CPPOpcode -> Ptr Constant -> Ptr Constant -> IO (Ptr Constant)
+
+$(do
+   let constExprInfo = ID.innerJoin (ID.innerJoin ID.astConstantRecs ID.astInstructionRecs) ID.instructionDefs
+   let tm = fix tm' 
+         where tm' _ (TH.ConT h) | h == ''A.C.Constant = [t| Ptr Constant |]
+               tm' x t = typeMappingU x t
+               
+   liftM concat $ sequence [      
+     foreignDecl ("LLVMConst" ++ name) ("constant" ++ name) [tm t | (_, _, t) <- fs ] [t| Ptr Constant |]
+     | (name, ((TH.RecC _ fs,_), ID.InstructionDef { ID.instructionKind = ID.Other })) <- Map.toList constExprInfo
+    ]
+  )
+
+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 ib) a (n, is) = 
+  (case ib of { 0 -> constantGetElementPtr'; 1 -> constantInBoundsGetElementPtr' }) a is n
+
+foreign import ccall unsafe "LLVM_General_GetConstCPPOpcode" getConstantCPPOpcode ::
+  Ptr Constant -> IO CPPOpcode
+
+foreign import ccall unsafe "LLVM_General_GetConstPredicate" getConstantICmpPredicate ::
+  Ptr Constant -> IO ICmpPredicate
+
+foreign import ccall unsafe "LLVM_General_GetConstPredicate" getConstantFCmpPredicate ::
+  Ptr Constant -> IO FCmpPredicate
+
+foreign import ccall unsafe "LLVM_General_GetConstIndices" getConstantIndices ::
+  Ptr Constant -> Ptr CUInt -> IO (Ptr CUInt)
+
+foreign import ccall unsafe "LLVMGetUndef" getUndef ::
+  Ptr Type -> IO (Ptr Constant)
+
+foreign import ccall unsafe "LLVMBlockAddress" blockAddress ::
+  Ptr Value -> Ptr BasicBlock -> IO (Ptr Constant)
+
+foreign import ccall unsafe "LLVM_General_GetBlockAddressFunction" getBlockAddressFunction ::
+  Ptr Constant -> IO (Ptr Value)
+
+foreign import ccall unsafe "LLVM_General_GetBlockAddressBlock" getBlockAddressBlock ::
+  Ptr Constant -> IO (Ptr BasicBlock)
diff --git a/src/LLVM/General/Internal/FFI/ConstantC.cpp b/src/LLVM/General/Internal/FFI/ConstantC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/ConstantC.cpp
@@ -0,0 +1,78 @@
+#define __STDC_LIMIT_MACROS
+#include "llvm-c/Core.h"
+#include "llvm/LLVMContext.h"
+#include "llvm/Constants.h"
+#include "LLVM/General/Internal/FFI/Value.h"
+
+using namespace llvm;
+
+extern "C" {
+
+LLVMValueRef LLVM_General_GetConstantDataSequentialElementAsConstant(LLVMValueRef v, unsigned i) {
+	wrap(unwrap<ConstantDataSequential>(v)->getElementAsConstant(i));
+}
+
+LLVMValueRef LLVM_General_GetBlockAddressFunction(LLVMValueRef v) {
+	return wrap(unwrap<BlockAddress>(v)->getFunction());
+}
+
+LLVMBasicBlockRef LLVM_General_GetBlockAddressBlock(LLVMValueRef v) {
+	return wrap(unwrap<BlockAddress>(v)->getBasicBlock());
+}
+
+double LLVM_General_ConstFloatDoubleValue(LLVMValueRef v) {
+	return unwrap<ConstantFP>(v)->getValueAPF().convertToDouble();
+}
+
+float LLVM_General_ConstFloatFloatValue(LLVMValueRef v) {
+	return unwrap<ConstantFP>(v)->getValueAPF().convertToFloat();
+}
+
+LLVMValueRef LLVM_General_ConstCast(unsigned opcode, LLVMValueRef v, LLVMTypeRef t) {
+	return wrap(ConstantExpr::getCast(opcode, unwrap<Constant>(v), unwrap(t)));
+}
+
+LLVMValueRef LLVM_General_ConstBinaryOperator(unsigned opcode, LLVMValueRef o0, LLVMValueRef o1) {
+	return wrap(ConstantExpr::get(opcode, unwrap<Constant>(o0), unwrap<Constant>(o1)));
+}
+
+unsigned LLVM_General_GetConstCPPOpcode(LLVMValueRef v) {
+	return unwrap<ConstantExpr>(v)->getOpcode();
+}
+
+unsigned LLVM_General_GetConstPredicate(LLVMValueRef v) {
+	return unwrap<ConstantExpr>(v)->getPredicate();
+}
+
+const unsigned *LLVM_General_GetConstIndices(LLVMValueRef v, unsigned *n) {
+	ArrayRef<unsigned> r = unwrap<ConstantExpr>(v)->getIndices();
+	*n = r.size();
+	return r.data();
+}
+
+const uint64_t *LLVM_General_GetConstantIntWords(LLVMValueRef v, unsigned *n) {
+	const APInt &i = unwrap<ConstantInt>(v)->getValue();
+	*n = i.getNumWords();
+	return i.getRawData();
+}
+
+LLVMValueRef LLVM_General_ConstFloatOfArbitraryPrecision(
+	LLVMContextRef c,
+	unsigned bits,
+	const uint64_t *words,
+	unsigned notPairOfFloats
+) {
+	return wrap(
+		ConstantFP::get(
+			*unwrap(c),
+			APFloat(APInt(bits, ArrayRef<uint64_t>(words, (bits-1)/64 + 1)), notPairOfFloats)
+		)
+	);
+}
+
+void LLVM_General_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];
+}
+
+}
diff --git a/src/LLVM/General/Internal/FFI/Context.hs b/src/LLVM/General/Internal/FFI/Context.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/Context.hs
@@ -0,0 +1,27 @@
+{-# LANGUAGE
+  ForeignFunctionInterface,
+  EmptyDataDecls
+  #-}
+
+-- | 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.General.Internal.FFI.Context where
+
+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/General/Internal/FFI/ExecutionEngine.hs b/src/LLVM/General/Internal/FFI/ExecutionEngine.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/ExecutionEngine.hs
@@ -0,0 +1,32 @@
+{-# LANGUAGE
+  ForeignFunctionInterface,
+  EmptyDataDecls
+  #-}
+
+module LLVM.General.Internal.FFI.ExecutionEngine where
+
+import Foreign.Ptr
+import Foreign.C
+
+import LLVM.General.Internal.FFI.PtrHierarchy
+import LLVM.General.Internal.FFI.Module
+
+data ExecutionEngine
+
+foreign import ccall unsafe "LLVMCreateExecutionEngineForModule" createExecutionEngineForModule ::
+    Ptr (Ptr ExecutionEngine) -> Ptr Module -> Ptr 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 ())
diff --git a/src/LLVM/General/Internal/FFI/Function.h b/src/LLVM/General/Internal/FFI/Function.h
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/Function.h
@@ -0,0 +1,39 @@
+#ifndef __LLVM_GENERAL_INTERNAL_FFI__FUNCTION__H__
+#define __LLVM_GENERAL_INTERNAL_FFI__FUNCTION__H__
+
+#define LLVM_GENERAL_FOR_EACH_CALLCONV(macro) \
+	macro(C)																		\
+	macro(Fast)																	\
+	macro(Cold)																	\
+
+#define LLVM_GENERAL_FOR_EACH_PARAM_ATTR(macro)				 \
+	macro(ZExt)																					 \
+	macro(SExt)																					 \
+	macro(InReg)																				 \
+	macro(StructRet)																		 \
+	macro(NoAlias)																			 \
+	macro(ByVal)																				 \
+	macro(NoCapture)																		 \
+	macro(Nest)																					 \
+
+#define LLVM_GENERAL_FOR_EACH_FUNCTION_ATTR(macro)	\
+	macro(NoReturn,Attribute)													\
+	macro(NoUnwind,Attribute)													\
+	macro(ReadNone,Attribute)													\
+	macro(ReadOnly,Attribute)													\
+	macro(NoInline,Attribute)													\
+	macro(AlwaysInline,Attribute)											\
+	macro(OptimizeForSize,Attribute)									\
+	macro(StackProtect,Attribute)											\
+	macro(StackProtectReq,Attribute)									\
+	macro(Alignment,)																	\
+	macro(NoRedZone,Attribute)												\
+	macro(NoImplicitFloat,Attribute)									\
+	macro(Naked,Attribute)														\
+	macro(InlineHint,Attribute)												\
+	macro(StackAlignment,)														\
+	macro(ReturnsTwice,)															\
+	macro(UWTable,)																		\
+	macro(NonLazyBind,)																\
+
+#endif
diff --git a/src/LLVM/General/Internal/FFI/Function.hs b/src/LLVM/General/Internal/FFI/Function.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/Function.hs
@@ -0,0 +1,60 @@
+{-# LANGUAGE
+  ForeignFunctionInterface,
+  EmptyDataDecls,
+  MultiParamTypeClasses
+  #-}
+
+module LLVM.General.Internal.FFI.Function where
+
+import Foreign.Ptr
+import Foreign.C
+
+import LLVM.General.Internal.FFI.Context
+import LLVM.General.Internal.FFI.LLVMCTypes
+import LLVM.General.Internal.FFI.PtrHierarchy
+
+foreign import ccall unsafe "LLVMGetFunctionCallConv" getFunctionCallConv ::
+    Ptr Function -> IO CallConv
+
+foreign import ccall unsafe "LLVMSetFunctionCallConv" setFunctionCallConv ::
+    Ptr Function -> CallConv -> IO ()
+
+foreign import ccall unsafe "LLVMAddFunctionAttr" addFunctionAttr ::
+    Ptr Function -> FunctionAttr -> IO ()
+
+foreign import ccall unsafe "LLVMGetFunctionAttr" getFunctionAttr ::
+    Ptr Function -> IO FunctionAttr
+
+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 "LLVMGetAttribute" getAttribute ::
+    Ptr Parameter -> IO ParamAttr
+
+foreign import ccall unsafe "LLVMAddAttribute" addAttribute ::
+    Ptr Parameter -> ParamAttr -> IO ()
+
+foreign import ccall unsafe "LLVM_General_GetFunctionRetAttr" getFunctionRetAttr ::
+    Ptr Function -> IO ParamAttr
+
+foreign import ccall unsafe "LLVM_General_AddFunctionRetAttr" addFunctionRetAttr ::
+    Ptr Function -> ParamAttr -> IO ()
+
+
+
diff --git a/src/LLVM/General/Internal/FFI/FunctionC.cpp b/src/LLVM/General/Internal/FFI/FunctionC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/FunctionC.cpp
@@ -0,0 +1,25 @@
+#define __STDC_LIMIT_MACROS
+#include "llvm/LLVMContext.h"
+#include "llvm/Attributes.h"
+
+#include "llvm-c/Core.h"
+
+using namespace llvm;
+
+extern "C" {
+
+LLVMAttribute LLVM_General_GetFunctionRetAttr(LLVMValueRef f) {
+	return (LLVMAttribute)unwrap<Function>(f)->getRetAttributes().Raw();
+}
+
+void LLVM_General_AddFunctionRetAttr(LLVMValueRef v, LLVMAttribute attr) {
+	Function &f = *unwrap<Function>(v);
+	LLVMContext &context = f.getContext();
+	AttrBuilder attrBuilder(attr);
+	f.setAttributes(
+		f.getAttributes().addAttr(context, AttrListPtr::ReturnIndex, Attributes::get(context, attrBuilder))
+	);
+}
+
+
+}
diff --git a/src/LLVM/General/Internal/FFI/GlobalAlias.hs b/src/LLVM/General/Internal/FFI/GlobalAlias.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/GlobalAlias.hs
@@ -0,0 +1,28 @@
+{-# LANGUAGE
+  ForeignFunctionInterface,
+  EmptyDataDecls,
+  MultiParamTypeClasses,
+  FlexibleInstances,
+  UndecidableInstances,
+  OverlappingInstances
+  #-}
+
+-- | FFI functions for handling the LLVM GlobalAlias class
+module LLVM.General.Internal.FFI.GlobalAlias where
+
+import Foreign.Ptr
+
+import LLVM.General.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_General_GetAliasee" getAliasee ::
+    Ptr GlobalAlias -> IO (Ptr Constant)
+
+-- | set the constant aliased by this alias
+foreign import ccall unsafe "LLVM_General_SetAliasee" setAliasee ::
+    Ptr GlobalAlias -> Ptr Constant -> IO ()
+
diff --git a/src/LLVM/General/Internal/FFI/GlobalAliasC.cpp b/src/LLVM/General/Internal/FFI/GlobalAliasC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/GlobalAliasC.cpp
@@ -0,0 +1,21 @@
+#define __STDC_LIMIT_MACROS
+#include "llvm/LLVMContext.h"
+#include "llvm/GlobalAlias.h"
+
+#include "llvm-c/Core.h"
+
+#include <iostream>
+
+using namespace llvm;
+
+extern "C" {
+
+LLVMValueRef LLVM_General_GetAliasee(LLVMValueRef g) {
+	return wrap(unwrap<GlobalAlias>(g)->getAliasee());
+}
+
+void LLVM_General_SetAliasee(LLVMValueRef g, LLVMValueRef c) {
+	unwrap<GlobalAlias>(g)->setAliasee(unwrap<Constant>(c));
+}
+
+}
diff --git a/src/LLVM/General/Internal/FFI/GlobalValue.h b/src/LLVM/General/Internal/FFI/GlobalValue.h
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/GlobalValue.h
@@ -0,0 +1,29 @@
+#ifndef __LLVM_GENERAL_INTERNAL_FFI__GLOBAL_VALUE__H__
+#define __LLVM_GENERAL_INTERNAL_FFI__GLOBAL_VALUE__H__
+
+#define LLVM_GENERAL_FOR_EACH_LINKAGE(macro)		\
+	macro(External)																\
+	macro(AvailableExternally)										\
+	macro(LinkOnceAny)														\
+	macro(LinkOnceODR)														\
+	macro(LinkOnceODRAutoHide)										\
+	macro(WeakAny)																\
+	macro(WeakODR)																\
+	macro(Appending)															\
+	macro(Internal)																\
+	macro(Private)																\
+	macro(DLLImport)															\
+	macro(DLLExport)															\
+	macro(ExternalWeak)														\
+	macro(Ghost)																	\
+	macro(Common)																	\
+	macro(LinkerPrivate)													\
+	macro(LinkerPrivateWeak)											\
+
+
+#define LLVM_GENERAL_FOR_EACH_VISIBILITY(macro)	\
+	macro(Default)																\
+	macro(Hidden)																	\
+	macro(Protected)															\
+
+#endif
diff --git a/src/LLVM/General/Internal/FFI/GlobalValue.hs b/src/LLVM/General/Internal/FFI/GlobalValue.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/GlobalValue.hs
@@ -0,0 +1,53 @@
+{-# LANGUAGE
+  ForeignFunctionInterface,
+  EmptyDataDecls,
+  MultiParamTypeClasses,
+  FlexibleInstances,
+  UndecidableInstances,
+  OverlappingInstances
+  #-}
+
+-- | FFI functions for handling the LLVM GlobalValue class
+module LLVM.General.Internal.FFI.GlobalValue where
+
+import Foreign.Ptr
+import Foreign.C
+
+import LLVM.General.Internal.FFI.PtrHierarchy
+import LLVM.General.Internal.FFI.LLVMCTypes
+
+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 "LLVMGetSection" getSection ::
+    Ptr GlobalValue -> IO CString
+
+foreign import ccall unsafe "LLVMSetSection" setSection ::
+    Ptr GlobalValue -> CString -> 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 "LLVMGetAlignment" getAlignment ::
+    Ptr GlobalValue -> IO CUInt
+
+foreign import ccall unsafe "LLVMSetAlignment" setAlignment ::
+    Ptr GlobalValue -> CUInt -> IO ()
+
+foreign import ccall unsafe "LLVM_General_HasUnnamedAddr" hasUnnamedAddr ::
+    Ptr GlobalValue -> IO LLVMBool
+
+foreign import ccall unsafe "LLVM_General_SetUnnamedAddr" setUnnamedAddr ::
+    Ptr GlobalValue -> LLVMBool -> IO ()
+
+
+
diff --git a/src/LLVM/General/Internal/FFI/GlobalValueC.cpp b/src/LLVM/General/Internal/FFI/GlobalValueC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/GlobalValueC.cpp
@@ -0,0 +1,17 @@
+#define __STDC_LIMIT_MACROS
+
+#include "llvm-c/Core.h"
+
+using namespace llvm;
+
+extern "C" {
+
+LLVMBool LLVM_General_HasUnnamedAddr(LLVMValueRef globalVal) {
+	return unwrap<GlobalValue>(globalVal)->hasUnnamedAddr();
+}
+
+void LLVM_General_SetUnnamedAddr(LLVMValueRef globalVal, LLVMBool isUnnamedAddr) {
+	unwrap<GlobalValue>(globalVal)->setUnnamedAddr(isUnnamedAddr);
+}
+
+}
diff --git a/src/LLVM/General/Internal/FFI/GlobalVariable.hs b/src/LLVM/General/Internal/FFI/GlobalVariable.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/GlobalVariable.hs
@@ -0,0 +1,39 @@
+{-# LANGUAGE
+  ForeignFunctionInterface,
+  EmptyDataDecls,
+  MultiParamTypeClasses,
+  FlexibleInstances,
+  UndecidableInstances,
+  OverlappingInstances
+  #-}
+
+-- | FFI functions for handling the LLVM GlobalVariable class
+module LLVM.General.Internal.FFI.GlobalVariable where
+
+import Foreign.Ptr
+import Foreign.C
+
+import LLVM.General.Internal.FFI.PtrHierarchy
+import LLVM.General.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 ()
+
+foreign import ccall unsafe "LLVMIsThreadLocal" isThreadLocal ::
+    Ptr GlobalVariable -> IO LLVMBool
+
+foreign import ccall unsafe "LLVMSetThreadLocal" setThreadLocal ::
+    Ptr GlobalVariable -> LLVMBool -> IO ()
+
diff --git a/src/LLVM/General/Internal/FFI/InlineAssembly.h b/src/LLVM/General/Internal/FFI/InlineAssembly.h
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/InlineAssembly.h
@@ -0,0 +1,14 @@
+#ifndef __LLVM_GENERAL_INTERNAL_FFI__INLINE_ASSEMBLY__H__
+#define __LLVM_GENERAL_INTERNAL_FFI__INLINE_ASSEMBLY__H__
+
+#define LLVM_GENERAL_FOR_EACH_ASM_DIALECT(macro) \
+	macro(ATT) \
+	macro(Intel) \
+
+typedef enum {
+#define ENUM_CASE(d) LLVMAsmDialect_ ## d,
+	LLVM_GENERAL_FOR_EACH_ASM_DIALECT(ENUM_CASE)
+#undef ENUM_CASE
+} LLVMAsmDialect;
+
+#endif
diff --git a/src/LLVM/General/Internal/FFI/InlineAssembly.hs b/src/LLVM/General/Internal/FFI/InlineAssembly.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/InlineAssembly.hs
@@ -0,0 +1,34 @@
+{-# LANGUAGE
+  ForeignFunctionInterface
+  #-}
+
+module LLVM.General.Internal.FFI.InlineAssembly where
+
+import Foreign.C
+import Foreign.Ptr
+
+import LLVM.General.Internal.FFI.LLVMCTypes
+import LLVM.General.Internal.FFI.PtrHierarchy
+import LLVM.General.Internal.FFI.Type
+
+foreign import ccall unsafe "LLVMIsAInlineAsm" isAInlineAsm ::
+  Ptr Value -> IO (Ptr InlineAsm)
+
+foreign import ccall unsafe "LLVM_General_CreateInlineAsm" createInlineAsm ::
+  Ptr Type -> CString -> CString -> LLVMBool -> LLVMBool -> AsmDialect -> IO (Ptr InlineAsm)
+
+foreign import ccall unsafe "LLVM_General_GetInlineAsmAsmString" getInlineAsmAssemblyString ::
+  Ptr InlineAsm -> IO CString
+
+foreign import ccall unsafe "LLVM_General_GetInlineAsmConstraintString" getInlineAsmConstraintString ::
+  Ptr InlineAsm -> IO CString
+
+foreign import ccall unsafe "LLVM_General_InlineAsmHasSideEffects" inlineAsmHasSideEffects ::
+  Ptr InlineAsm -> IO LLVMBool
+
+foreign import ccall unsafe "LLVM_General_InlineAsmIsAlignStack" inlineAsmIsAlignStack ::
+  Ptr InlineAsm -> IO LLVMBool
+
+foreign import ccall unsafe "LLVM_General_GetInlineAsmDialect" getInlineAsmDialect ::
+  Ptr InlineAsm -> IO AsmDialect
+
diff --git a/src/LLVM/General/Internal/FFI/InlineAssemblyC.cpp b/src/LLVM/General/Internal/FFI/InlineAssemblyC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/InlineAssemblyC.cpp
@@ -0,0 +1,72 @@
+#define __STDC_LIMIT_MACROS
+#include "llvm/InlineAsm.h"
+#include "llvm-c/Core.h"
+
+#include "LLVM/General/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_GENERAL_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_GENERAL_FOR_EACH_ASM_DIALECT(ENUM_CASE)
+#undef ENUM_CASE
+	default: return InlineAsm::AsmDialect(0);
+	}
+}
+}
+
+extern "C" {
+
+LLVMValueRef LLVM_General_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_General_GetInlineAsmAsmString(LLVMValueRef v) {
+	return unwrap<InlineAsm>(v)->getAsmString().c_str();
+}
+
+const char *LLVM_General_GetInlineAsmConstraintString(LLVMValueRef v) {
+	return unwrap<InlineAsm>(v)->getConstraintString().c_str();
+}
+
+LLVMBool LLVM_General_InlineAsmHasSideEffects(LLVMValueRef v) {
+	return unwrap<InlineAsm>(v)->hasSideEffects();
+}
+
+LLVMBool LLVM_General_InlineAsmIsAlignStack(LLVMValueRef v) {
+	return unwrap<InlineAsm>(v)->isAlignStack();
+}
+
+LLVMAsmDialect LLVM_General_GetInlineAsmDialect(LLVMValueRef v) {
+	return wrap(unwrap<InlineAsm>(v)->getDialect());
+}
+
+}
+
diff --git a/src/LLVM/General/Internal/FFI/Instruction.h b/src/LLVM/General/Internal/FFI/Instruction.h
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/Instruction.h
@@ -0,0 +1,48 @@
+#ifndef __LLVM_GENERAL_INTERNAL_FFI__INSTRUCTION__H__
+#define __LLVM_GENERAL_INTERNAL_FFI__INSTRUCTION__H__
+
+#define LLVM_GENERAL_FOR_EACH_ATOMIC_ORDERING(macro) \
+	macro(NotAtomic) \
+	macro(Unordered) \
+	macro(Monotonic) \
+	macro(Acquire) \
+	macro(Release) \
+	macro(AcquireRelease) \
+	macro(SequentiallyConsistent)
+
+typedef enum {
+#define ENUM_CASE(x) LLVM ## x ## AtomicOrdering,
+LLVM_GENERAL_FOR_EACH_ATOMIC_ORDERING(ENUM_CASE)
+#undef ENUM_CASE
+} LLVMAtomicOrdering;
+
+#define LLVM_GENERAL_FOR_EACH_SYNCRONIZATION_SCOPE(macro) \
+	macro(SingleThread) \
+	macro(CrossThread)
+
+typedef enum {
+#define ENUM_CASE(x) LLVM ## x ## SynchronizationScope,
+LLVM_GENERAL_FOR_EACH_SYNCRONIZATION_SCOPE(ENUM_CASE)
+#undef ENUM_CASE
+} LLVMSynchronizationScope;
+
+#define LLVM_GENERAL_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)
+
+typedef enum {
+#define ENUM_CASE(x) LLVM ## x ## RMWOperation,
+LLVM_GENERAL_FOR_EACH_RMW_OPERATION(ENUM_CASE)
+#undef ENUM_CASE
+} LLVMAtomicRMWOperation;
+
+#endif
diff --git a/src/LLVM/General/Internal/FFI/Instruction.hs b/src/LLVM/General/Internal/FFI/Instruction.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/Instruction.hs
@@ -0,0 +1,147 @@
+{-# LANGUAGE
+  ForeignFunctionInterface,
+  EmptyDataDecls,
+  MultiParamTypeClasses,
+  FlexibleInstances,
+  UndecidableInstances,
+  OverlappingInstances,
+  TemplateHaskell
+  #-}
+
+module LLVM.General.Internal.FFI.Instruction where
+
+import Control.Monad
+import Foreign.Ptr
+import Foreign.C
+
+import LLVM.General.Internal.FFI.PtrHierarchy
+import LLVM.General.Internal.FFI.Type
+import LLVM.General.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_General_GetInstructionDefOpcode" getInstructionDefOpcode :: 
+  Ptr Instruction -> IO CPPOpcode
+
+foreign import ccall unsafe "LLVMGetICmpPredicate" getICmpPredicate ::
+  Ptr Instruction -> IO ICmpPredicate
+
+foreign import ccall unsafe "LLVM_General_GetFCmpPredicate" getFCmpPredicate ::
+  Ptr Instruction -> IO FCmpPredicate
+
+foreign import ccall unsafe "LLVMGetInstructionCallConv" getInstructionCallConv ::
+  Ptr Instruction -> IO CallConv
+
+foreign import ccall unsafe "LLVMSetInstructionCallConv" setInstructionCallConv ::
+  Ptr Instruction -> CallConv -> IO ()
+
+foreign import ccall unsafe "LLVMIsTailCall" isTailCall ::
+  Ptr Instruction -> IO LLVMBool
+
+foreign import ccall unsafe "LLVMSetTailCall" setTailCall ::
+  Ptr Instruction -> LLVMBool -> IO ()
+
+foreign import ccall unsafe "LLVM_General_GetCallInstCalledValue" getCallInstCalledValue ::
+  Ptr Instruction -> IO (Ptr Value)
+
+foreign import ccall unsafe "LLVM_General_GetCallInstFunctionAttr" getCallInstFunctionAttr ::
+  Ptr Instruction -> IO FunctionAttr
+
+foreign import ccall unsafe "LLVM_General_AddCallInstFunctionAttr" addCallInstFunctionAttr ::
+  Ptr Instruction -> FunctionAttr -> IO ()
+
+foreign import ccall unsafe "LLVM_General_GetCallInstAttr" getCallInstAttr ::
+  Ptr Instruction -> CUInt -> IO ParamAttr
+
+foreign import ccall unsafe "LLVM_General_AddCallInstAttr" addCallInstAttr ::
+  Ptr Instruction -> CUInt -> ParamAttr -> 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_General_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_General_GetIndirectBrDests" getIndirectBrDests ::
+  Ptr Instruction -> Ptr (Ptr BasicBlock) -> IO ()
+
+
+foreign import ccall unsafe "LLVM_General_GetInstrAlignment" getInstrAlignment ::
+  Ptr Instruction -> IO CUInt
+
+foreign import ccall unsafe "LLVM_General_SetInstrAlignment" setInstrAlignment ::
+  Ptr Instruction -> CUInt -> IO ()
+
+foreign import ccall unsafe "LLVM_General_GetAllocaNumElements" getAllocaNumElements ::
+  Ptr Instruction -> IO (Ptr Value)
+
+foreign import ccall unsafe "LLVM_General_GetAllocatedType" getAllocatedType ::
+  Ptr Instruction -> IO (Ptr Type)
+
+foreign import ccall unsafe "LLVM_General_GetAtomicOrdering" getAtomicOrdering ::
+  Ptr Instruction -> IO MemoryOrdering
+
+foreign import ccall unsafe "LLVM_General_GetSynchronizationScope" getSynchronizationScope ::
+  Ptr Instruction -> IO LLVMBool
+
+getAtomicity i = return (,) `ap` getSynchronizationScope i `ap` getAtomicOrdering i
+
+foreign import ccall unsafe "LLVM_General_GetVolatile" getVolatile ::
+  Ptr Instruction -> IO LLVMBool
+
+foreign import ccall unsafe "LLVM_General_GetInBounds" getInBounds ::
+  Ptr Value -> IO LLVMBool
+
+foreign import ccall unsafe "LLVM_General_SetInBounds" setInBounds ::
+  Ptr Instruction -> LLVMBool -> IO ()
+
+foreign import ccall unsafe "LLVM_General_GetAtomicRMWOperation" getAtomicRMWOperation ::
+  Ptr Instruction -> IO RMWOperation
+
+foreign import ccall unsafe "LLVM_General_CountInstStructureIndices" countInstStructureIndices ::
+  Ptr Instruction -> IO CUInt
+
+foreign import ccall unsafe "LLVM_General_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 "LLVM_General_IsCleanup" isCleanup ::
+  Ptr Instruction -> IO LLVMBool
+
+foreign import ccall unsafe "LLVMSetMetadata" setMetadata ::
+  Ptr Instruction -> MDKindID -> Ptr MDNode -> IO ()
+
+foreign import ccall unsafe "LLVM_General_GetMetadata" getMetadata ::
+  Ptr Instruction -> Ptr MDKindID -> Ptr (Ptr MDNode) -> CUInt -> IO CUInt
diff --git a/src/LLVM/General/Internal/FFI/InstructionC.cpp b/src/LLVM/General/Internal/FFI/InstructionC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/InstructionC.cpp
@@ -0,0 +1,241 @@
+#define __STDC_LIMIT_MACROS
+#include "llvm/LLVMContext.h"
+#include "llvm/InstrTypes.h"
+#include "llvm/Support/CallSite.h"
+#include "llvm/Attributes.h"
+#include "llvm/Operator.h"
+
+#include "llvm-c/Core.h"
+
+#include "LLVM/General/Internal/FFI/Instruction.h"
+
+using namespace llvm;
+
+namespace llvm {
+
+static LLVMAtomicOrdering wrap(AtomicOrdering l) {
+	switch(l) {
+#define ENUM_CASE(x) case x: return LLVM ## x ## AtomicOrdering;
+LLVM_GENERAL_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_GENERAL_FOR_EACH_SYNCRONIZATION_SCOPE(ENUM_CASE)
+#undef ENUM_CASE
+	default: return LLVMSynchronizationScope(0);
+	}
+}
+
+static LLVMAtomicRMWOperation wrap(AtomicRMWInst::BinOp l) {
+	switch(l) {
+#define ENUM_CASE(x) case AtomicRMWInst::x: return LLVM ## x ## RMWOperation;
+LLVM_GENERAL_FOR_EACH_RMW_OPERATION(ENUM_CASE)
+#undef ENUM_CASE
+	default: return LLVMAtomicRMWOperation(0);
+	}
+}
+
+}
+
+extern "C" {
+
+unsigned LLVM_General_GetInstructionDefOpcode(LLVMValueRef val) {
+	return unwrap<Instruction>(val)->getOpcode();
+}
+
+unsigned LLVM_General_HasNoSignedWrap(LLVMValueRef val) {
+	return unwrap<OverflowingBinaryOperator>(val)->hasNoSignedWrap();
+}
+
+unsigned LLVM_General_HasNoUnsignedWrap(LLVMValueRef val) {
+	return unwrap<OverflowingBinaryOperator>(val)->hasNoUnsignedWrap();
+}
+
+int LLVM_General_IsExact(LLVMValueRef val) {
+	return unwrap<PossiblyExactOperator>(val)->isExact();
+}
+
+LLVMValueRef LLVM_General_GetCallInstCalledValue(
+	LLVMValueRef callInst
+) {
+	return wrap(CallSite(unwrap<Instruction>(callInst)).getCalledValue());
+}
+
+LLVMAttribute LLVM_General_GetCallInstAttr(LLVMValueRef callInst, unsigned i) {
+	return (LLVMAttribute)CallSite(unwrap<Instruction>(callInst)).getAttributes().getParamAttributes(i).Raw();
+}
+
+void LLVM_General_AddCallInstAttr(LLVMValueRef callInst, unsigned i, LLVMAttribute attr) {
+	CallSite callSite(unwrap<Instruction>(callInst));
+	LLVMContext &context = callSite->getContext();
+	AttrBuilder attrBuilder(attr);
+	callSite.setAttributes(callSite.getAttributes().addAttr(context, i, Attributes::get(context, attrBuilder)));
+}
+
+LLVMAttribute LLVM_General_GetCallInstFunctionAttr(LLVMValueRef callInst) {
+	return LLVM_General_GetCallInstAttr(callInst, AttrListPtr::FunctionIndex);
+}
+
+void LLVM_General_AddCallInstFunctionAttr(LLVMValueRef callInst, LLVMAttribute attr) {
+	LLVM_General_AddCallInstAttr(callInst, AttrListPtr::FunctionIndex, attr);
+}
+
+LLVMValueRef LLVM_General_GetAllocaNumElements(LLVMValueRef a) {
+	return wrap(unwrap<AllocaInst>(a)->getArraySize());
+}
+
+LLVMTypeRef LLVM_General_GetAllocatedType(LLVMValueRef a) {
+	return wrap(unwrap<AllocaInst>(a)->getAllocatedType());
+}
+
+// ------------------------------------------------------------
+
+#define LLVM_GENERAL_FOR_EACH_ALIGNMENT_INST(macro) \
+	macro(Alloca) \
+	macro(Load) \
+	macro(Store)
+
+unsigned LLVM_General_GetInstrAlignment(LLVMValueRef l) {
+	switch(unwrap<Instruction>(l)->getOpcode()) {
+#define ENUM_CASE(n) case Instruction::n: return unwrap<n ## Inst>(l)->getAlignment();
+		LLVM_GENERAL_FOR_EACH_ALIGNMENT_INST(ENUM_CASE)
+#undef ENUM_CASE
+	default: return 0;
+	}
+}
+
+void LLVM_General_SetInstrAlignment(LLVMValueRef l, unsigned a) {
+	switch(unwrap<Instruction>(l)->getOpcode()) {
+#define ENUM_CASE(n) case Instruction::n: unwrap<n ## Inst>(l)->setAlignment(a);
+		LLVM_GENERAL_FOR_EACH_ALIGNMENT_INST(ENUM_CASE)
+#undef ENUM_CASE
+	}
+}
+
+// ------------------------------------------------------------
+
+#define LLVM_GENERAL_FOR_EACH_ATOMIC_INST(macro) \
+	macro(Load) \
+	macro(Store) \
+	macro(Fence) \
+	macro(AtomicCmpXchg) \
+	macro(AtomicRMW)
+
+LLVMAtomicOrdering LLVM_General_GetAtomicOrdering(LLVMValueRef i) {
+	switch(unwrap<Instruction>(i)->getOpcode()) {
+#define ENUM_CASE(n) case Instruction::n: return wrap(unwrap<n ## Inst>(i)->getOrdering());
+		LLVM_GENERAL_FOR_EACH_ATOMIC_INST(ENUM_CASE)
+#undef ENUM_CASE
+	default: return LLVMAtomicOrdering(0);
+	}
+}
+
+LLVMSynchronizationScope LLVM_General_GetSynchronizationScope(LLVMValueRef i) {
+	switch(unwrap<Instruction>(i)->getOpcode()) {
+#define ENUM_CASE(n) case Instruction::n: return wrap(unwrap<n ## Inst>(i)->getSynchScope());
+		LLVM_GENERAL_FOR_EACH_ATOMIC_INST(ENUM_CASE)
+#undef ENUM_CASE
+	default: return LLVMSynchronizationScope(0);
+	}
+}
+
+#define LLVM_GENERAL_FOR_EACH_VOLATILITY_INST(macro) \
+	macro(Load) \
+	macro(Store) \
+	macro(AtomicCmpXchg) \
+	macro(AtomicRMW)
+
+LLVMBool LLVM_General_GetVolatile(LLVMValueRef i) {
+	switch(unwrap<Instruction>(i)->getOpcode()) {
+#define ENUM_CASE(n) case Instruction::n: return unwrap<n ## Inst>(i)->isVolatile();
+		LLVM_GENERAL_FOR_EACH_VOLATILITY_INST(ENUM_CASE)
+#undef ENUM_CASE
+	default: return LLVMBool(0);
+	}
+}
+
+// ------------------------------------------------------------
+
+LLVMBool LLVM_General_GetInBounds(LLVMValueRef i) {
+	return unwrap<GEPOperator>(i)->isInBounds();
+}
+
+void LLVM_General_SetInBounds(LLVMValueRef i, LLVMBool b) {
+	unwrap<GetElementPtrInst>(i)->setIsInBounds(b);
+}
+
+LLVMAtomicRMWOperation LLVM_General_GetAtomicRMWOperation(LLVMValueRef i) {
+	return wrap(unwrap<AtomicRMWInst>(i)->getOperation());
+}
+
+LLVMRealPredicate LLVM_General_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_General_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_General_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);
+}
+
+LLVMBool LLVM_General_IsCleanup(LLVMValueRef v) {
+	return unwrap<LandingPadInst>(v)->isCleanup();
+}
+
+void LLVM_General_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_General_GetIndirectBrDests(
+	LLVMValueRef v,
+	LLVMBasicBlockRef *dests
+) {
+	IndirectBrInst *ib = unwrap<IndirectBrInst>(v);
+	for(unsigned i=0; i != ib->getNumDestinations(); ++i, ++dests)
+		*dests = wrap(ib->getDestination(i));
+}
+
+unsigned LLVM_General_GetMetadata(
+	LLVMValueRef i,
+	unsigned *kinds,
+	LLVMValueRef *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();
+}
+
+}
+
diff --git a/src/LLVM/General/Internal/FFI/InstructionDefs.hsc b/src/LLVM/General/Internal/FFI/InstructionDefs.hsc
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/InstructionDefs.hsc
@@ -0,0 +1,56 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+-- 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.General.Internal.FFI.InstructionDefs where
+
+import LLVM.General.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 HANDLE_INST(num,opcode,class) { 0, num, #opcode, #class, },
+
+#define LAST_OTHER_INST(num) { 0, 0, 0, 0, } };
+
+#include "llvm/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(", "); } 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 | 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/General/Internal/FFI/Iterate.hs b/src/LLVM/General/Internal/FFI/Iterate.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/Iterate.hs
@@ -0,0 +1,13 @@
+-- | Functions to help handle LLVM iteration patterns
+module LLVM.General.Internal.FFI.Iterate where
+
+import Control.Monad
+import Data.Functor
+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/General/Internal/FFI/LLVMCTypes.hsc b/src/LLVM/General/Internal/FFI/LLVMCTypes.hsc
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/LLVMCTypes.hsc
@@ -0,0 +1,180 @@
+{-# LANGUAGE
+  DeriveDataTypeable,
+  StandaloneDeriving,
+  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.General.Internal.FFI.LLVMCTypes where
+
+#define __STDC_LIMIT_MACROS
+#include "llvm-c/Core.h"
+#include "llvm-c/Target.h"
+#include "llvm-c/TargetMachine.h"
+#include "LLVM/General/Internal/FFI/Instruction.h"
+#include "LLVM/General/Internal/FFI/Value.h"
+#include "LLVM/General/Internal/FFI/SMDiagnostic.h"
+#include "LLVM/General/Internal/FFI/InlineAssembly.h"
+#include "LLVM/General/Internal/FFI/Target.h"
+#include "LLVM/General/Internal/FFI/Function.h"
+#include "LLVM/General/Internal/FFI/GlobalValue.h"
+#include "LLVM/General/Internal/FFI/Type.h"
+
+import Language.Haskell.TH.Quote
+
+import Data.Data
+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_GENERAL_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" \
+             "  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 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 MemoryOrdering = MemoryOrdering CUInt
+  deriving (Eq, Typeable, Data)
+#define MO_Rec(n) { #n, LLVM ## n ## AtomicOrdering },
+#{inject ATOMIC_ORDERING, MemoryOrdering, MemoryOrdering, memoryOrdering, MO_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 CallConv = CallConv CUInt
+  deriving (Eq, Read, Show, Typeable, Data)
+#define CC_Rec(n) { #n, LLVM ## n ## CallConv },
+#{inject CALLCONV, CallConv, CallConv, callConv, CC_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, LLVM ## n ## RMWOperation },
+#{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 FloatABIType = FloatABIType CUInt
+  deriving (Eq, Read, Show, Typeable, Data)
+#define FAT_Rec(n) { #n, LLVM_General_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_General_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_General_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}
+
+newtype ParamAttr = ParamAttr CUInt
+  deriving (Eq, Read, Show, Bits, Typeable, Data)
+#define PA_Rec(n) { #n, LLVM ## n ## Attribute },
+#{inject PARAM_ATTR, ParamAttr, ParamAttr, paramAttr, PA_Rec}
+
+newtype FunctionAttr = FunctionAttr CUInt
+  deriving (Eq, Read, Show, Bits, Typeable, Data)
+#define FA_Rec(n,a) { #n, LLVM ## n ## a },
+#{inject FUNCTION_ATTR, FunctionAttr, FunctionAttr, functionAttr, FA_Rec}
diff --git a/src/LLVM/General/Internal/FFI/Metadata.hs b/src/LLVM/General/Internal/FFI/Metadata.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/Metadata.hs
@@ -0,0 +1,69 @@
+{-#
+  LANGUAGE
+  ForeignFunctionInterface
+  #-}
+
+module LLVM.General.Internal.FFI.Metadata where
+
+import Foreign.Ptr
+import Foreign.C
+
+import LLVM.General.Internal.FFI.Context
+import LLVM.General.Internal.FFI.PtrHierarchy
+import LLVM.General.Internal.FFI.LLVMCTypes
+
+foreign import ccall unsafe "LLVMIsAMDString" isAMDString ::
+  Ptr Value -> IO (Ptr MDString)
+
+foreign import ccall unsafe "LLVMIsAMDNode" isAMDNode ::
+  Ptr Value -> IO (Ptr MDNode)
+
+foreign import ccall unsafe "LLVMGetMDKindIDInContext" getMDKindIDInContext' ::
+  Ptr Context -> Ptr CChar -> CUInt -> IO MDKindID
+
+getMDKindIDInContext ctx (c, n) = getMDKindIDInContext' ctx c n
+
+foreign import ccall unsafe "LLVM_General_GetMDKindNames" getMDKindNames ::
+  Ptr Context -> Ptr (Ptr CChar) -> Ptr CUInt -> CUInt -> IO CUInt
+
+foreign import ccall unsafe "LLVMMDStringInContext" mdStringInContext' ::
+  Ptr Context -> CString -> CUInt -> IO (Ptr MDString)
+
+mdStringInContext ctx (p, n) = mdStringInContext' ctx p n
+
+foreign import ccall unsafe "LLVMGetMDString" getMDString ::
+  Ptr MDString -> Ptr CUInt -> IO CString
+
+foreign import ccall unsafe "LLVMMDNodeInContext" createMDNodeInContext' ::
+  Ptr Context -> Ptr (Ptr Value) -> CUInt -> IO (Ptr MDNode)
+
+createMDNodeInContext ctx (n, vs) = createMDNodeInContext' ctx vs n
+
+foreign import ccall unsafe "LLVM_General_CreateTemporaryMDNodeInContext" createTemporaryMDNodeInContext ::
+  Ptr Context -> IO (Ptr MDNode)
+
+foreign import ccall unsafe "LLVM_General_DestroyTemporaryMDNode" destroyTemporaryMDNode ::
+  Ptr MDNode -> IO ()
+
+foreign import ccall unsafe "LLVM_General_GetMDNodeNumOperands" getMDNodeNumOperands ::
+  Ptr MDNode -> IO CUInt
+
+foreign import ccall unsafe "LLVMGetMDNodeOperands" getMDNodeOperands ::
+  Ptr MDNode -> Ptr (Ptr Value) -> IO ()
+
+foreign import ccall unsafe "LLVM_General_MDNodeIsFunctionLocal" mdNodeIsFunctionLocal ::
+  Ptr MDNode -> IO LLVMBool
+
+foreign import ccall unsafe "LLVM_General_GetNamedMetadataName" getNamedMetadataName ::
+  Ptr NamedMetadata -> Ptr CUInt -> IO (Ptr CChar)
+
+foreign import ccall unsafe "LLVM_General_GetNamedMetadataNumOperands" getNamedMetadataNumOperands ::
+  Ptr NamedMetadata -> IO CUInt
+
+foreign import ccall unsafe "LLVM_General_GetNamedMetadataOperands" getNamedMetadataOperands ::
+  Ptr NamedMetadata -> Ptr (Ptr MDNode) -> IO ()
+
+foreign import ccall unsafe "LLVM_General_NamedMetadataAddOperands" namedMetadataAddOperands' ::
+  Ptr NamedMetadata -> Ptr (Ptr MDNode) -> CUInt -> IO ()
+
+namedMetadataAddOperands nm (n, vs) = namedMetadataAddOperands' nm vs n
diff --git a/src/LLVM/General/Internal/FFI/MetadataC.cpp b/src/LLVM/General/Internal/FFI/MetadataC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/MetadataC.cpp
@@ -0,0 +1,71 @@
+#define __STDC_LIMIT_MACROS
+#include "llvm/LLVMContext.h"
+#include "llvm/Metadata.h"
+
+#include "llvm-c/Core.h"
+
+using namespace llvm;
+
+extern "C" {
+
+unsigned LLVM_General_GetMDKindNames(
+	LLVMContextRef c,
+	const char **s,
+	unsigned *l,
+	unsigned n
+) {
+	SmallVector<StringRef, 16> 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_General_GetMDNodeNumOperands(LLVMValueRef v) {
+	return unwrap<MDNode>(v)->getNumOperands();
+}
+
+unsigned LLVM_General_MDNodeIsFunctionLocal(LLVMValueRef v) {
+	return unwrap<MDNode>(v)->isFunctionLocal();
+}
+
+void LLVM_General_NamedMetadataAddOperands(
+	NamedMDNode *n,
+	LLVMValueRef *ops,
+	unsigned nOps
+) {
+	for(unsigned i = 0; i != nOps; ++i) n->addOperand(unwrap<MDNode>(ops[i]));
+}
+
+const char *LLVM_General_GetNamedMetadataName(
+	NamedMDNode *n,
+	unsigned *len
+) {
+	StringRef s = n->getName();
+	*len = s.size();
+	return s.data();
+}
+
+unsigned LLVM_General_GetNamedMetadataNumOperands(NamedMDNode *n) {
+	return n->getNumOperands();
+}
+
+void LLVM_General_GetNamedMetadataOperands(NamedMDNode *n, LLVMValueRef *dest) {
+	for(unsigned i = 0; i != n->getNumOperands(); ++i)
+		dest[i] = wrap(n->getOperand(i));
+}
+
+LLVMValueRef LLVM_General_CreateTemporaryMDNodeInContext(LLVMContextRef c) {
+	return wrap(MDNode::getTemporary(*unwrap(c), ArrayRef<Value *>()));
+}
+
+void LLVM_General_DestroyTemporaryMDNode(LLVMValueRef v) {
+	MDNode::deleteTemporary(unwrap<MDNode>(v));
+}
+
+}
+
diff --git a/src/LLVM/General/Internal/FFI/Module.hs b/src/LLVM/General/Internal/FFI/Module.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/Module.hs
@@ -0,0 +1,86 @@
+{-# LANGUAGE
+  ForeignFunctionInterface,
+  EmptyDataDecls
+  #-}
+
+module LLVM.General.Internal.FFI.Module where
+
+import Foreign.Ptr
+import Foreign.C
+
+import LLVM.General.Internal.FFI.PtrHierarchy
+import LLVM.General.Internal.FFI.Context
+import LLVM.General.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_General_GetModuleIdentifier" getModuleIdentifier ::
+  Ptr Module -> IO CString
+
+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_General_GetFirstAlias" getFirstAlias ::
+  Ptr Module -> IO (Ptr GlobalAlias)
+
+foreign import ccall unsafe "LLVM_General_GetNextAlias" getNextAlias ::
+  Ptr GlobalAlias -> IO (Ptr GlobalAlias)
+
+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_General_GetFirstNamedMetadata" getFirstNamedMetadata ::
+  Ptr Module -> IO (Ptr NamedMetadata)
+
+foreign import ccall unsafe "LLVM_General_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_General_JustAddAlias" justAddAlias ::
+  Ptr Module -> Ptr Type -> CString -> IO (Ptr GlobalAlias)
+
+
+foreign import ccall unsafe "LLVMAddFunction" addFunction ::
+  Ptr Module -> CString -> Ptr Type -> IO (Ptr Function)
+
+foreign import ccall unsafe "LLVM_General_GetOrAddNamedMetadata" getOrAddNamedMetadata ::
+  Ptr Module -> CString -> IO (Ptr NamedMetadata)
+
+foreign import ccall unsafe "LLVM_General_ModuleAppendInlineAsm" moduleAppendInlineAsm' ::
+  Ptr Module -> Ptr CChar -> CUInt -> IO ()
+
+newtype ModuleAsm a = ModuleAsm a
+
+moduleAppendInlineAsm m (ModuleAsm (c, n)) = moduleAppendInlineAsm' m c n
+
+foreign import ccall unsafe "LLVM_General_ModuleGetInlineAsm" moduleGetInlineAsm ::
+  Ptr Module -> IO (ModuleAsm CString)
diff --git a/src/LLVM/General/Internal/FFI/ModuleC.cpp b/src/LLVM/General/Internal/FFI/ModuleC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/ModuleC.cpp
@@ -0,0 +1,58 @@
+#define __STDC_LIMIT_MACROS
+#include "llvm/LLVMContext.h"
+#include "llvm/Module.h"
+
+#include "llvm-c/Core.h"
+#include <iostream>
+
+using namespace llvm;
+
+extern "C" {
+
+char *LLVM_General_GetModuleIdentifier(LLVMModuleRef val) {
+	return strdup(unwrap(val)->getModuleIdentifier().c_str());
+}
+
+LLVMValueRef LLVM_General_GetFirstAlias(LLVMModuleRef m) {
+	Module *mod = unwrap(m);
+	Module::alias_iterator i = mod->alias_begin();
+	return i == mod->alias_end() ? 0 : wrap(i);
+}
+
+LLVMValueRef LLVM_General_GetNextAlias(LLVMValueRef a) {
+	GlobalAlias *alias = unwrap<GlobalAlias>(a);
+	Module::alias_iterator i = alias;
+	if (++i == alias->getParent()->alias_end()) return 0;
+	return wrap(i);
+}
+
+LLVMValueRef LLVM_General_JustAddAlias(LLVMModuleRef m, LLVMTypeRef ty, const char *name) {
+	return wrap(new GlobalAlias(unwrap(ty), GlobalValue::ExternalLinkage, name, 0, unwrap(m)));
+}
+
+NamedMDNode *LLVM_General_GetOrAddNamedMetadata(LLVMModuleRef m, const char *name) {
+	return unwrap(m)->getOrInsertNamedMetadata(name);
+}
+
+NamedMDNode *LLVM_General_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_General_GetNextNamedMetadata(NamedMDNode *a) {
+	Module::named_metadata_iterator i = a;
+	if (++i == a->getParent()->named_metadata_end()) return 0;
+	return i;
+}
+
+void LLVM_General_ModuleAppendInlineAsm(LLVMModuleRef m, const char *s, unsigned l) {
+	unwrap(m)->appendModuleInlineAsm(StringRef(s,l));
+}
+
+const char *LLVM_General_ModuleGetInlineAsm(LLVMModuleRef m) {
+	return unwrap(m)->getModuleInlineAsm().c_str();
+}
+
+}
+
diff --git a/src/LLVM/General/Internal/FFI/PassManager.hs b/src/LLVM/General/Internal/FFI/PassManager.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/PassManager.hs
@@ -0,0 +1,100 @@
+{-# LANGUAGE
+  TemplateHaskell,
+  ForeignFunctionInterface,
+  EmptyDataDecls
+  #-}
+
+module LLVM.General.Internal.FFI.PassManager where
+
+import Language.Haskell.TH
+
+import Control.Monad
+
+import Foreign.Ptr
+import Foreign.C
+
+import LLVM.General.Internal.FFI.LLVMCTypes
+import LLVM.General.Internal.FFI.PtrHierarchy
+import LLVM.General.Internal.FFI.Cleanup
+import LLVM.General.Internal.FFI.Module
+import LLVM.General.Internal.FFI.Target
+import LLVM.General.Internal.FFI.Transforms
+
+import qualified LLVM.General.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
+
+$(do
+  let declareForeign :: Name -> [Type] -> DecsQ
+      declareForeign hName extraParams = do
+        let n = nameBase hName
+        foreignDecl 
+          (cName n)
+          ("add" ++ n ++ "Pass")
+          ([[t| Ptr PassManager |]] 
+           ++ [[t| Ptr TargetLowering |] | needsTargetLowering n]
+           ++ map typeMapping extraParams)
+          (tupleT 0)
+
+  TyConI (DataD _ _ _ cons _) <- reify ''G.Pass
+  liftM concat $ forM cons $ \con -> case con of
+    RecC n l -> declareForeign n [ t | (_,_,t) <- l ]
+    NormalC n [] -> declareForeign n []
+    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 -> CUInt -> IO () 
+
+foreign import ccall unsafe "LLVMPassManagerBuilderSetDisableUnrollLoops" passManagerBuilderSetDisableUnrollLoops ::
+    Ptr PassManagerBuilder -> CUInt -> IO ()
+
+foreign import ccall unsafe "LLVMPassManagerBuilderSetDisableSimplifyLibCalls" passManagerBuilderSetDisableSimplifyLibCalls ::
+    Ptr PassManagerBuilder -> CUInt -> 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 () 
diff --git a/src/LLVM/General/Internal/FFI/PassManagerC.cpp b/src/LLVM/General/Internal/FFI/PassManagerC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/PassManagerC.cpp
@@ -0,0 +1,130 @@
+#define __STDC_LIMIT_MACROS
+#include "llvm/LLVMContext.h"
+#include "llvm/Transforms/Scalar.h"
+#include "llvm/Transforms/IPO.h"
+#include "llvm/Transforms/Vectorize.h"
+#include "llvm/PassManager.h"
+
+#include "llvm-c/Core.h"
+
+using namespace llvm;
+
+extern "C" {
+typedef struct LLVMOpaqueVectorizationConfig *LLVMVectorizationConfigRef;
+typedef struct LLVMOpaqueTargetLowering *LLVMTargetLoweringRef;
+}
+
+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));
+}
+}
+
+extern "C" {
+
+#define LLVM_GENERAL_FOR_EACH_PASS_WITHOUT_LLVM_C_BINDING(macro) \
+	macro(BlockPlacement)			\
+	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_General_Add ## p ## Pass(LLVMPassManagerRef PM) {	\
+	unwrap(PM)->add(create ## p ## Pass());			\
+}
+LLVM_GENERAL_FOR_EACH_PASS_WITHOUT_LLVM_C_BINDING(ENUM_CASE)
+#undef ENUM_CASE
+
+void LLVM_General_AddCodeGenPreparePass(LLVMPassManagerRef PM, LLVMTargetLoweringRef T) {
+	unwrap(PM)->add(createCodeGenPreparePass(unwrap(T)));
+}
+	
+void LLVM_General_AddGlobalValueNumberingPass(LLVMPassManagerRef PM, LLVMBool noLoads) {
+	unwrap(PM)->add(createGVNPass(noLoads));
+}
+
+void LLVM_General_AddInternalizePass(LLVMPassManagerRef PM, unsigned nExports, const char **exports) {
+	std::vector<const char *> exportList(exports, exports + nExports);
+	unwrap(PM)->add(createInternalizePass(exportList));
+}
+
+void LLVM_General_AddLoopStrengthReducePass(LLVMPassManagerRef PM, LLVMTargetLoweringRef T) {
+	unwrap(PM)->add(createLoopStrengthReducePass(unwrap(T)));
+}
+
+void LLVM_General_AddLowerInvokePass(LLVMPassManagerRef PM, LLVMTargetLoweringRef T, LLVMBool expensiveEH) {
+	unwrap(PM)->add(createLowerInvokePass(unwrap(T), expensiveEH));
+}
+	
+void LLVM_General_AddSROAPass(LLVMPassManagerRef PM, LLVMBool RequiresDomTree) {
+	unwrap(PM)->add(createSROAPass(RequiresDomTree));
+}
+
+void LLVM_General_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));
+}	
+
+}
diff --git a/src/LLVM/General/Internal/FFI/PtrHierarchy.hs b/src/LLVM/General/Internal/FFI/PtrHierarchy.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/PtrHierarchy.hs
@@ -0,0 +1,95 @@
+{-# LANGUAGE
+  ForeignFunctionInterface,
+  MultiParamTypeClasses,
+  EmptyDataDecls,
+  FunctionalDependencies,
+  FlexibleInstances,
+  UndecidableInstances,
+  OverlappingInstances
+  #-}
+
+-- | This module defines typeclasses to represent the relationships of an object-oriented inheritance hierarchy
+module LLVM.General.Internal.FFI.PtrHierarchy where
+
+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 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_1GlobalVariable.html>
+data GlobalVariable
+
+instance ChildOf GlobalValue 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 GlobalValue 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_1User.html>
+data User
+
+instance ChildOf Value User
+
+-- | <http://llvm.org/doxygen/classllvm_1_1MDNode.html>
+data MDNode
+
+instance ChildOf Value MDNode
+
+-- | <http://llvm.org/doxygen/classllvm_1_1MDString.html>
+data MDString
+
+instance ChildOf Value MDString
+
+-- | <http://llvm.org/doxygen/classllvm_1_1NamedMDNode.html>
+data NamedMetadata
+
+-- | <http://llvm.org/doxygen/classllvm_1_1InlineAsm.html>
+data InlineAsm
+
+instance ChildOf Value InlineAsm
diff --git a/src/LLVM/General/Internal/FFI/SMDiagnostic.h b/src/LLVM/General/Internal/FFI/SMDiagnostic.h
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/SMDiagnostic.h
@@ -0,0 +1,15 @@
+#ifndef __LLVM_GENERAL_INTERNAL_FFI__SMDIAGNOSTIC__
+#define __LLVM_GENERAL_INTERNAL_FFI__SMDIAGNOSTIC__
+
+#define LLVM_GENERAL_FOR_EACH_DIAGNOSTIC_KIND(macro) \
+	macro(Error) \
+	macro(Warning) \
+	macro(Note)
+
+typedef enum {
+#define ENUM_CASE(k) LLVMDiagnosticKind ## k,
+LLVM_GENERAL_FOR_EACH_DIAGNOSTIC_KIND(ENUM_CASE)
+#undef ENUM_CASE
+} LLVMDiagnosticKind;
+
+#endif
diff --git a/src/LLVM/General/Internal/FFI/SMDiagnostic.hs b/src/LLVM/General/Internal/FFI/SMDiagnostic.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/SMDiagnostic.hs
@@ -0,0 +1,40 @@
+{-# LANGUAGE
+  ForeignFunctionInterface,
+  EmptyDataDecls
+  #-}
+
+-- | FFI functions for handling the LLVM SMDiagnostic class
+module LLVM.General.Internal.FFI.SMDiagnostic where
+
+import Foreign.Ptr
+import Foreign.C
+
+import LLVM.General.Internal.FFI.LLVMCTypes
+
+data SMDiagnostic
+
+-- | allocate an SMDiagnostic object
+foreign import ccall unsafe "LLVM_General_CreateSMDiagnostic" createSMDiagnostic ::
+  IO (Ptr SMDiagnostic)
+
+foreign import ccall unsafe "LLVM_General_DisposeSMDiagnostic" disposeSMDiagnostic ::
+  Ptr SMDiagnostic -> IO ()
+
+foreign import ccall unsafe "LLVM_General_GetSMDiagnosticKind" getSMDiagnosticKind ::
+  Ptr SMDiagnostic -> IO DiagnosticKind
+
+foreign import ccall unsafe "LLVM_General_GetSMDiagnosticLineNo" getSMDiagnosticLineNo ::
+  Ptr SMDiagnostic -> IO CInt 
+
+foreign import ccall unsafe "LLVM_General_GetSMDiagnosticColumnNo" getSMDiagnosticColumnNo ::
+  Ptr SMDiagnostic -> IO CInt 
+
+foreign import ccall unsafe "LLVM_General_GetSMDiagnosticFilename" getSMDiagnosticFilename ::
+  Ptr SMDiagnostic -> IO CString 
+
+foreign import ccall unsafe "LLVM_General_GetSMDiagnosticMessage" getSMDiagnosticMessage ::
+  Ptr SMDiagnostic -> IO CString 
+
+foreign import ccall unsafe "LLVM_General_GetSMDiagnosticLineContents" getSMDiagnosticLineContents ::
+  Ptr SMDiagnostic -> IO CString 
+
diff --git a/src/LLVM/General/Internal/FFI/SMDiagnosticC.cpp b/src/LLVM/General/Internal/FFI/SMDiagnosticC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/SMDiagnosticC.cpp
@@ -0,0 +1,31 @@
+#define __STDC_LIMIT_MACROS
+#include "llvm/LLVMContext.h"
+
+#include "llvm/Support/SourceMgr.h"
+#include "llvm-c/Core.h"
+
+#include "LLVM/General/Internal/FFI/SMDiagnostic.h"
+
+using namespace llvm;
+
+extern "C" {
+
+SMDiagnostic *LLVM_General_CreateSMDiagnostic() { return new SMDiagnostic(); }
+void LLVM_General_DisposeSMDiagnostic(SMDiagnostic *p) { delete p; }
+
+LLVMDiagnosticKind LLVM_General_GetSMDiagnosticKind(SMDiagnostic *p) {
+	switch(p->getKind()) {
+#define ENUM_CASE(k) case SourceMgr::DK_ ## k: return LLVMDiagnosticKind ## k;
+		LLVM_GENERAL_FOR_EACH_DIAGNOSTIC_KIND(ENUM_CASE)
+#undef ENUM_CASE
+	default: return LLVMDiagnosticKind(0);
+	}
+}
+
+int LLVM_General_GetSMDiagnosticLineNo(SMDiagnostic *p) { return p->getLineNo(); }
+int LLVM_General_GetSMDiagnosticColumnNo(SMDiagnostic *p) { return p->getColumnNo(); }
+const char *LLVM_General_GetSMDiagnosticFilename(SMDiagnostic *p) { return p->getFilename().c_str(); }
+const char *LLVM_General_GetSMDiagnosticMessage(SMDiagnostic *p) { return p->getMessage().c_str(); }
+const char *LLVM_General_GetSMDiagnosticLineContents(SMDiagnostic *p) { return p->getLineContents().c_str(); }
+
+}
diff --git a/src/LLVM/General/Internal/FFI/Target.h b/src/LLVM/General/Internal/FFI/Target.h
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/Target.h
@@ -0,0 +1,74 @@
+#ifndef __LLVM_GENERAL_INTERNAL_FFI__TARGET__H__
+#define __LLVM_GENERAL_INTERNAL_FFI__TARGET__H__
+
+#define LLVM_GENERAL_FOR_EACH_RELOC_MODEL(macro)	\
+	macro(Default, Default)													\
+	macro(Static, Static)														\
+	macro(PIC, PIC_)																\
+	macro(DynamicNoPic, DynamicNoPIC)
+
+#define LLVM_GENERAL_FOR_EACH_CODE_MODEL(macro) \
+	macro(Default)																\
+	macro(JITDefault)															\
+	macro(Small)																	\
+	macro(Kernel)																	\
+	macro(Medium)																	\
+	macro(Large)
+
+#define LLVM_GENERAL_FOR_EACH_CODE_GEN_OPT_LEVEL(macro) \
+	macro(None)																						\
+	macro(Less)																						\
+	macro(Default)																				\
+	macro(Aggressive)
+
+#define LLVM_GENERAL_FOR_EACH_TARGET_OPTION_FLAG(macro)	\
+	macro(PrintMachineCode)																\
+	macro(NoFramePointerElim)															\
+	macro(NoFramePointerElimNonLeaf)											\
+	macro(LessPreciseFPMADOption)													\
+	macro(UnsafeFPMath)																		\
+	macro(NoInfsFPMath)																		\
+	macro(NoNaNsFPMath)																		\
+	macro(HonorSignDependentRoundingFPMathOption)					\
+	macro(UseSoftFloat)																		\
+	macro(NoZerosInBSS)																		\
+	macro(JITExceptionHandling)														\
+	macro(JITEmitDebugInfo)																\
+	macro(JITEmitDebugInfoToDisk)													\
+	macro(GuaranteedTailCallOpt)													\
+	macro(DisableTailCalls)																\
+	macro(RealignStack)																		\
+	macro(EnableFastISel)																	\
+	macro(PositionIndependentExecutable)									\
+	macro(EnableSegmentedStacks)													\
+	macro(UseInitArray)
+
+typedef enum {
+#define ENUM_CASE(n) LLVM_General_TargetOptionFlag_ ## n,
+	LLVM_GENERAL_FOR_EACH_TARGET_OPTION_FLAG(ENUM_CASE)
+#undef ENUM_CASE
+} LLVM_General_TargetOptionFlag;
+
+#define LLVM_GENERAL_FOR_EACH_FLOAT_ABI(macro)	\
+	macro(Default)																\
+	macro(Soft)																		\
+	macro(Hard) 
+
+typedef enum {
+#define ENUM_CASE(n) LLVM_General_FloatABI_ ## n,
+	LLVM_GENERAL_FOR_EACH_FLOAT_ABI(ENUM_CASE)
+#undef ENUM_CASE
+} LLVM_General_FloatABI;
+
+#define LLVM_GENERAL_FOR_EACH_FP_OP_FUSION_MODE(macro)	\
+	macro(Fast)																						\
+	macro(Standard)																				\
+	macro(Strict)
+
+typedef enum {
+#define ENUM_CASE(n) LLVM_General_FPOpFusionMode_ ## n,
+	LLVM_GENERAL_FOR_EACH_FP_OP_FUSION_MODE(ENUM_CASE)
+#undef ENUM_CASE
+} LLVM_General_FPOpFusionMode;
+
+#endif
diff --git a/src/LLVM/General/Internal/FFI/Target.hs b/src/LLVM/General/Internal/FFI/Target.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/Target.hs
@@ -0,0 +1,85 @@
+{-# LANGUAGE
+  ForeignFunctionInterface,
+  GeneralizedNewtypeDeriving,
+  EmptyDataDecls
+  #-}
+
+module LLVM.General.Internal.FFI.Target where
+
+import Foreign.Ptr
+import Foreign.C
+
+import LLVM.General.Internal.FFI.LLVMCTypes
+
+data Target
+
+foreign import ccall unsafe "LLVM_General_InitializeNativeTarget" initializeNativeTarget ::
+    IO LLVMBool
+
+foreign import ccall unsafe "LLVM_General_LookupTarget" lookupTarget ::
+    CString -> CString -> Ptr CString -> Ptr CString -> IO (Ptr Target)
+
+data TargetOptions
+
+foreign import ccall unsafe "LLVM_General_CreateTargetOptions" createTargetOptions ::
+  IO (Ptr TargetOptions)
+
+foreign import ccall unsafe "LLVM_General_SetTargetOptionFlag" setTargetOptionFlag ::
+  Ptr TargetOptions -> TargetOptionFlag -> LLVMBool -> IO ()
+
+foreign import ccall unsafe "LLVM_General_GetTargetOptionFlag" getTargetOptionsFlag ::
+  Ptr TargetOptions -> TargetOptionFlag -> IO LLVMBool
+
+foreign import ccall unsafe "LLVM_General_SetStackAlignmentOverride" setStackAlignmentOverride ::
+  Ptr TargetOptions -> CUInt -> IO ()
+
+foreign import ccall unsafe "LLVM_General_GetStackAlignmentOverride" getStackAlignmentOverride ::
+  Ptr TargetOptions -> IO CUInt
+
+foreign import ccall unsafe "LLVM_General_SetTrapFuncName" setTrapFuncName ::
+  Ptr TargetOptions -> CString -> IO ()
+
+foreign import ccall unsafe "LLVM_General_GetTrapFuncName" getTrapFuncName ::
+  Ptr TargetOptions -> IO CString
+
+foreign import ccall unsafe "LLVM_General_SetFloatABIType" setFloatABIType ::
+  Ptr TargetOptions -> FloatABIType -> IO ()
+
+foreign import ccall unsafe "LLVM_General_GetFloatABIType" getFloatABIType ::
+  Ptr TargetOptions -> IO FloatABIType
+
+foreign import ccall unsafe "LLVM_General_SetAllowFPOpFusion" setAllowFPOpFusion ::
+  Ptr TargetOptions -> FPOpFusionMode -> IO ()
+
+foreign import ccall unsafe "LLVM_General_GetAllowFPOpFusion" getAllowFPOpFusion ::
+  Ptr TargetOptions -> IO FPOpFusionMode
+
+foreign import ccall unsafe "LLVM_General_SetSSPBufferSize" setSSPBufferSize ::
+  Ptr TargetOptions -> CUInt -> IO ()
+
+foreign import ccall unsafe "LLVM_General_GetSSPBufferSize" getSSPBufferSize ::
+  Ptr TargetOptions -> IO CUInt
+
+foreign import ccall unsafe "LLVM_General_DisposeTargetOptions" disposeTargetOptions ::
+    Ptr TargetOptions -> IO ()
+
+data TargetMachine
+
+foreign import ccall unsafe "LLVM_General_CreateTargetMachine" createTargetMachine ::
+    Ptr Target
+    -> CString 
+    -> CString
+    -> CString
+    -> Ptr TargetOptions
+    -> RelocModel
+    -> CodeModel
+    -> CodeGenOptLevel
+    -> IO (Ptr TargetMachine)
+
+foreign import ccall unsafe "LLVMDisposeTargetMachine" disposeTargetMachine ::
+    Ptr TargetMachine -> IO ()
+
+data TargetLowering
+
+foreign import ccall unsafe "LLVM_General_GetTargetLowering" getTargetLowering ::
+    Ptr TargetMachine -> IO (Ptr TargetLowering)
diff --git a/src/LLVM/General/Internal/FFI/TargetC.cpp b/src/LLVM/General/Internal/FFI/TargetC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/TargetC.cpp
@@ -0,0 +1,201 @@
+#define __STDC_LIMIT_MACROS
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Target/TargetMachine.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm-c/Target.h"
+#include "llvm-c/TargetMachine.h"
+#include "llvm-c/Core.h"
+#include "LLVM/General/Internal/FFI/Target.h"
+
+using namespace llvm;
+
+namespace llvm {
+static Reloc::Model unwrap(LLVMRelocMode x) {
+	switch(x) {
+#define ENUM_CASE(x,y) case LLVMReloc ## x: return Reloc::y;
+LLVM_GENERAL_FOR_EACH_RELOC_MODEL(ENUM_CASE)
+#undef ENUM_CASE
+	default: return Reloc::Model(0);
+	}
+}
+
+static CodeModel::Model unwrap(LLVMCodeModel x) {
+	switch(x) {
+#define ENUM_CASE(x) case LLVMCodeModel ## x: return CodeModel::x;
+LLVM_GENERAL_FOR_EACH_CODE_MODEL(ENUM_CASE)
+#undef ENUM_CASE
+	default: return CodeModel::Model(0);
+	}
+}
+
+static CodeGenOpt::Level unwrap(LLVMCodeGenOptLevel x) {
+	switch(x) {
+#define ENUM_CASE(x) case LLVMCodeGenLevel ## x: return CodeGenOpt::x;
+LLVM_GENERAL_FOR_EACH_CODE_GEN_OPT_LEVEL(ENUM_CASE)
+#undef ENUM_CASE
+	default: return CodeGenOpt::Level(0);
+	}
+}
+
+static FloatABI::ABIType unwrap(LLVM_General_FloatABI x) {
+	switch(x) {
+#define ENUM_CASE(x) case LLVM_General_FloatABI_ ## x: return FloatABI::x;
+LLVM_GENERAL_FOR_EACH_FLOAT_ABI(ENUM_CASE)
+#undef ENUM_CASE
+	default: return FloatABI::ABIType(0);
+	}
+}
+
+static LLVM_General_FloatABI wrap(FloatABI::ABIType x) {
+	switch(x) {
+#define ENUM_CASE(x) case FloatABI::x: return LLVM_General_FloatABI_ ## x;
+LLVM_GENERAL_FOR_EACH_FLOAT_ABI(ENUM_CASE)
+#undef ENUM_CASE
+	default: return LLVM_General_FloatABI(0);
+	}
+}
+
+static FPOpFusion::FPOpFusionMode unwrap(LLVM_General_FPOpFusionMode x) {
+	switch(x) {
+#define ENUM_CASE(x) case LLVM_General_FPOpFusionMode_ ## x: return FPOpFusion::x;
+LLVM_GENERAL_FOR_EACH_FP_OP_FUSION_MODE(ENUM_CASE)
+#undef ENUM_CASE
+	default: return FPOpFusion::FPOpFusionMode(0);
+	}
+}
+
+static LLVM_General_FPOpFusionMode wrap(FPOpFusion::FPOpFusionMode x) {
+	switch(x) {
+#define ENUM_CASE(x) case FPOpFusion::x: return LLVM_General_FPOpFusionMode_ ## x;
+LLVM_GENERAL_FOR_EACH_FP_OP_FUSION_MODE(ENUM_CASE)
+#undef ENUM_CASE
+	default: return  LLVM_General_FPOpFusionMode(0);
+	}
+}
+}
+
+extern "C" {
+
+LLVMBool LLVM_General_InitializeNativeTarget() {
+	return LLVMInitializeNativeTarget();
+}
+
+LLVMTargetRef LLVM_General_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_General_CreateTargetOptions() {
+	TargetOptions *to = new TargetOptions();
+	to->SSPBufferSize = 0; // this field was left uninitialized in LLVM 3.2
+	return to;
+}
+
+void LLVM_General_SetTargetOptionFlag(
+	TargetOptions *to,
+	LLVM_General_TargetOptionFlag f,
+	unsigned v
+) {
+	switch(f) {
+#define ENUM_CASE(op) case LLVM_General_TargetOptionFlag_ ## op: to->op = v ? 1 : 0; break;
+	LLVM_GENERAL_FOR_EACH_TARGET_OPTION_FLAG(ENUM_CASE)
+#undef ENUM_CASE
+	}
+}
+
+unsigned LLVM_General_GetTargetOptionFlag(
+	TargetOptions *to,
+	LLVM_General_TargetOptionFlag f
+) {
+	switch(f) {
+#define ENUM_CASE(op) case LLVM_General_TargetOptionFlag_ ## op: return to->op;
+	LLVM_GENERAL_FOR_EACH_TARGET_OPTION_FLAG(ENUM_CASE)
+#undef ENUM_CASE
+	}
+}
+
+void LLVM_General_SetStackAlignmentOverride(TargetOptions *to, unsigned v) {
+	to->StackAlignmentOverride = v;
+}
+
+unsigned LLVM_General_GetStackAlignmentOverride(TargetOptions *to) {
+	return to->StackAlignmentOverride;
+}
+
+void LLVM_General_SetTrapFuncName(TargetOptions *to, const char *v) {
+	to->TrapFuncName = v;
+}
+
+const char *LLVM_General_GetTrapFuncName(TargetOptions *to) {
+	return to->TrapFuncName.c_str();
+}
+
+void LLVM_General_SetFloatABIType(TargetOptions *to, LLVM_General_FloatABI v) {
+	to->FloatABIType = unwrap(v);
+}
+
+LLVM_General_FloatABI LLVM_General_GetFloatABIType(TargetOptions *to) {
+	return wrap(to->FloatABIType);
+}
+
+void LLVM_General_SetAllowFPOpFusion(TargetOptions *to, LLVM_General_FPOpFusionMode v) {
+	to->AllowFPOpFusion = unwrap(v);
+}
+
+LLVM_General_FPOpFusionMode LLVM_General_GetAllowFPOpFusion(TargetOptions *to) {
+	return wrap(to->AllowFPOpFusion);
+}
+
+void LLVM_General_SetSSPBufferSize(TargetOptions *to, unsigned v) {
+	to->SSPBufferSize = v;
+}
+
+unsigned LLVM_General_GetSSPBufferSize(TargetOptions *to) {
+	return to->SSPBufferSize;
+}
+
+void LLVM_General_DisposeTargetOptions(TargetOptions *t) {
+	delete t;
+}
+
+
+LLVMTargetMachineRef LLVM_General_CreateTargetMachine(
+	LLVMTargetRef target,
+	const char *triple,
+	const char *cpu,
+	const char *features,
+	const TargetOptions *targetOptions,
+	LLVMRelocMode relocModel,
+	LLVMCodeModel codeModel,
+	LLVMCodeGenOptLevel codeGenOptLevel
+) {
+	return wrap(
+		unwrap(target)->createTargetMachine(
+			triple,
+			cpu,
+			features,
+			*targetOptions, 
+			unwrap(relocModel),
+			unwrap(codeModel),
+			unwrap(codeGenOptLevel)
+		)
+	);
+}
+	
+const TargetLowering *LLVM_General_GetTargetLowering(LLVMTargetMachineRef t) {
+	return unwrap(t)->getTargetLowering();
+}
+
+}
+
diff --git a/src/LLVM/General/Internal/FFI/Transforms.hs b/src/LLVM/General/Internal/FFI/Transforms.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/Transforms.hs
@@ -0,0 +1,62 @@
+-- | Code used with Template Haskell to build the FFI for transform passes.
+module LLVM.General.Internal.FFI.Transforms where
+
+-- | does the constructor for this pass require a TargetLowering object
+needsTargetLowering "CodeGenPrepare" = True
+needsTargetLowering "LoopStrengthReduce" = True
+needsTargetLowering "LowerInvoke" = True
+needsTargetLowering _ = 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_General_" prefix).
+cName n = 
+    let core = case n of
+            "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"
+            h -> h
+        patchImpls = [
+         "CodeGenPrepare",
+         "GlobalValueNumbering",
+         "InternalizeFunctions",
+         "BasicBlockVectorize",
+	 "BlockPlacement",
+	 "BreakCriticalEdges",
+	 "DeadCodeElimination",
+	 "DeadInstructionElimination",
+	 "DemoteRegisterToMemory",
+	 "LoopClosedSingleStaticAssignment",
+	 "LoopInstructionSimplify",
+         "LoopStrengthReduce",
+	 "LowerAtomic",
+	 "LowerInvoke",
+	 "LowerSwitch",
+	 "MergeFunctions",
+	 "PartialInlining",
+         "ScalarReplacementOfAggregates",
+	 "Sinking",
+	 "StripDeadDebugInfo",
+	 "StripDebugDeclare",
+	 "StripNonDebugSymbols"
+         ]
+    in
+      (if (n `elem` patchImpls) then "LLVM_General_" else "LLVM") ++ "Add" ++ core ++ "Pass"
diff --git a/src/LLVM/General/Internal/FFI/Type.h b/src/LLVM/General/Internal/FFI/Type.h
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/Type.h
@@ -0,0 +1,22 @@
+#ifndef __LLVM_GENERAL_INTERNAL_FFI__TYPE__H__
+#define __LLVM_GENERAL_INTERNAL_FFI__TYPE__H__
+
+#define LLVM_GENERAL_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) 
+
+#endif
diff --git a/src/LLVM/General/Internal/FFI/Type.hs b/src/LLVM/General/Internal/FFI/Type.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/Type.hs
@@ -0,0 +1,143 @@
+{-# LANGUAGE
+  ForeignFunctionInterface,
+  EmptyDataDecls
+  #-}
+
+-- | Functions for handling the LLVM types
+module LLVM.General.Internal.FFI.Type where
+
+import Foreign.Ptr
+import Foreign.C
+
+import LLVM.General.Internal.FFI.LLVMCTypes
+import LLVM.General.Internal.FFI.Context
+
+-- | a blind type to correspond to llvm::Type
+data Type
+
+-- | <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 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_General_ArrayType" arrayType ::
+  Ptr Type -> CULong -> 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 ctx (n, ts) p = structTypeInContext' ctx ts n p
+
+foreign import ccall unsafe "LLVM_General_StructCreateNamed" structCreateNamed ::
+  Ptr Context -> CString -> IO (Ptr Type)
+
+foreign import ccall unsafe "LLVMGetStructName" getStructName ::
+  Ptr Type -> IO CString
+
+foreign import ccall unsafe "LLVM_General_StructIsLiteral" structIsLiteral ::
+  Ptr Type -> IO LLVMBool
+
+foreign import ccall unsafe "LLVM_General_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 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_General_GetArrayLength" getArrayLength ::
+  Ptr Type -> IO CULong
+
+-- | <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)
diff --git a/src/LLVM/General/Internal/FFI/TypeC.cpp b/src/LLVM/General/Internal/FFI/TypeC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/TypeC.cpp
@@ -0,0 +1,34 @@
+#define __STDC_LIMIT_MACROS
+#include "llvm-c/Core.h"
+#include "llvm/Type.h"
+
+using namespace llvm;
+
+extern "C" {
+
+LLVMTypeRef LLVM_General_StructCreateNamed(LLVMContextRef C, const char *Name) {
+	if (Name) {
+		return wrap(StructType::create(*unwrap(C), Name));
+	} else {
+		return wrap(StructType::create(*unwrap(C)));
+	}
+}
+
+LLVMBool LLVM_General_StructIsLiteral(LLVMTypeRef t) {
+	return unwrap<StructType>(t)->isLiteral();
+}
+
+LLVMBool LLVM_General_StructIsOpaque(LLVMTypeRef t) {
+	return unwrap<StructType>(t)->isOpaque();
+}
+
+LLVMTypeRef LLVM_General_ArrayType(LLVMTypeRef ElementType, uint64_t ElementCount) {
+	return wrap(ArrayType::get(unwrap(ElementType), ElementCount));
+}
+
+uint64_t LLVM_General_GetArrayLength(LLVMTypeRef ArrayTy) {
+	return unwrap<ArrayType>(ArrayTy)->getNumElements();
+}
+
+
+}
diff --git a/src/LLVM/General/Internal/FFI/User.hs b/src/LLVM/General/Internal/FFI/User.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/User.hs
@@ -0,0 +1,40 @@
+{-# LANGUAGE
+  ForeignFunctionInterface,
+  EmptyDataDecls,
+  MultiParamTypeClasses,
+  FlexibleInstances,
+  UndecidableInstances,
+  OverlappingInstances
+  #-}
+
+-- | FFI functions for handling the LLVM User class
+module LLVM.General.Internal.FFI.User where
+
+import Foreign.Ptr
+import Foreign.C
+
+import LLVM.General.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/General/Internal/FFI/Value.h b/src/LLVM/General/Internal/FFI/Value.h
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/Value.h
@@ -0,0 +1,35 @@
+#ifndef __LLVM_GENERAL_INTERNAL_FFI__VALUE__H__
+#define __LLVM_GENERAL_INTERNAL_FFI__VALUE__H__
+
+#define LLVM_GENERAL_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(ConstantVector) \
+	macro(ConstantPointerNull) \
+	macro(MDNode) \
+	macro(MDString) \
+	macro(InlineAsm) \
+	macro(PseudoSourceValue) \
+	macro(FixedStackPseudoSourceValue) \
+	macro(Instruction)
+
+typedef enum {
+#define ENUM_CASE(class) LLVM ## class ## SubclassId,
+LLVM_GENERAL_FOR_EACH_VALUE_SUBCLASS(ENUM_CASE)
+#undef ENUM_CASE
+} LLVMValueSubclassId;
+
+#endif
diff --git a/src/LLVM/General/Internal/FFI/Value.hs b/src/LLVM/General/Internal/FFI/Value.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/Value.hs
@@ -0,0 +1,39 @@
+{-# LANGUAGE
+  ForeignFunctionInterface,
+  EmptyDataDecls,
+  MultiParamTypeClasses,
+  FlexibleContexts,
+  FlexibleInstances,
+  UndecidableInstances,
+  OverlappingInstances
+  #-}
+
+-- | FFI functions for handling the LLVM Value class
+module LLVM.General.Internal.FFI.Value where
+
+import Foreign.Ptr
+import Foreign.C
+
+import LLVM.General.Internal.FFI.LLVMCTypes
+import LLVM.General.Internal.FFI.Type 
+import LLVM.General.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_General_GetValueSubclassId" getValueSubclassId ::
+  Ptr Value -> IO ValueSubclassId
+
+foreign import ccall unsafe "LLVMReplaceAllUsesWith" replaceAllUsesWith ::
+  Ptr Value -> Ptr Value -> IO ()
diff --git a/src/LLVM/General/Internal/FFI/ValueC.cpp b/src/LLVM/General/Internal/FFI/ValueC.cpp
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FFI/ValueC.cpp
@@ -0,0 +1,20 @@
+#define __STDC_LIMIT_MACROS
+#include "llvm-c/Core.h"
+#include "llvm/Value.h"
+#include "LLVM/General/Internal/FFI/Value.h"
+
+using namespace llvm;
+
+extern "C" {
+
+LLVMValueSubclassId LLVM_General_GetValueSubclassId(LLVMValueRef v) {
+	switch(unwrap(v)->getValueID()) {
+#define VALUE_SUBCLASS_ID_CASE(class) case Value::class ## Val: return LLVM ## class ## SubclassId;
+LLVM_GENERAL_FOR_EACH_VALUE_SUBCLASS(VALUE_SUBCLASS_ID_CASE)
+#undef VALUE_SUBCLASS_ID_CASE
+	default: break;
+	}
+	return LLVMValueSubclassId(0);
+}
+
+}
diff --git a/src/LLVM/General/Internal/FloatingPointPredicate.hs b/src/LLVM/General/Internal/FloatingPointPredicate.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/FloatingPointPredicate.hs
@@ -0,0 +1,31 @@
+{-# LANGUAGE
+  MultiParamTypeClasses,
+  TemplateHaskell,
+  FlexibleInstances
+  #-}
+
+module LLVM.General.Internal.FloatingPointPredicate where
+
+import LLVM.General.Internal.Coding
+
+import qualified LLVM.General.Internal.FFI.LLVMCTypes as FFI
+import qualified LLVM.General.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/General/Internal/Function.hs b/src/LLVM/General/Internal/Function.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/Function.hs
@@ -0,0 +1,40 @@
+module LLVM.General.Internal.Function where
+
+import Control.Monad
+import Control.Monad.Trans
+import Control.Monad.AnyCont
+
+import Foreign.Ptr
+
+import qualified LLVM.General.Internal.FFI.Function as FFI
+import qualified LLVM.General.Internal.FFI.PtrHierarchy as FFI
+
+import LLVM.General.Internal.DecodeAST
+import LLVM.General.Internal.Value
+import LLVM.General.Internal.Coding
+import LLVM.General.Internal.Attribute ()
+
+import qualified LLVM.General.AST as A
+import qualified LLVM.General.AST.Attribute as A.A
+
+getFunctionAttrs :: Ptr FFI.Function -> IO [A.A.FunctionAttribute]
+getFunctionAttrs = decodeM <=< FFI.getFunctionAttr
+
+setFunctionAttrs :: Ptr FFI.Function -> [A.A.FunctionAttribute] -> IO ()
+setFunctionAttrs f = FFI.addFunctionAttr f <=< encodeM 
+
+getParameterAttrs :: Ptr FFI.Parameter -> IO [A.A.ParameterAttribute]
+getParameterAttrs = decodeM <=< FFI.getAttribute
+
+getParameters :: Ptr FFI.Function -> DecodeAST [A.Parameter]
+getParameters f = scopeAnyCont $ do
+  n <- liftIO (FFI.countParams f)
+  ps <- allocaArray n
+  liftIO $ FFI.getParams f ps
+  params <- peekArray n ps
+  forM params $ \param -> 
+    return A.Parameter 
+       `ap` typeOf param
+       `ap` getLocalName param
+       `ap` (liftIO $ getParameterAttrs param)
+  
diff --git a/src/LLVM/General/Internal/Global.hs b/src/LLVM/General/Internal/Global.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/Global.hs
@@ -0,0 +1,77 @@
+{-# LANGUAGE
+  TemplateHaskell,
+  FlexibleInstances,
+  MultiParamTypeClasses,
+  FlexibleContexts
+  #-}
+module LLVM.General.Internal.Global where
+
+import Control.Monad.IO.Class
+import Data.Functor
+import Foreign.Ptr
+import Control.Monad.AnyCont
+
+import Data.Word
+
+import qualified LLVM.General.Internal.FFI.PtrHierarchy as FFI
+import qualified LLVM.General.Internal.FFI.LLVMCTypes as FFI
+import qualified LLVM.General.Internal.FFI.GlobalValue as FFI
+
+import LLVM.General.Internal.Coding
+import LLVM.General.Internal.DecodeAST
+import LLVM.General.Internal.EncodeAST
+
+import qualified LLVM.General.AST.Linkage as A.L
+import qualified LLVM.General.AST.Visibility as A.V
+
+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.linkageDLLImport, A.L.DLLImport),
+  (FFI.linkageDLLExport, A.L.DLLExport),
+  (FFI.linkageExternalWeak, A.L.ExternWeak),
+  (FFI.linkageCommon, A.L.Common),
+  (FFI.linkageLinkerPrivate, A.L.LinkerPrivate),
+  (FFI.linkageLinkerPrivateWeak, A.L.LinkerPrivateWeak)
+ ]
+
+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
+
+getSection :: FFI.DescendentOf FFI.GlobalValue v => Ptr v -> DecodeAST (Maybe String)
+getSection g = liftIO $ do
+  s <- decodeM =<< FFI.getSection (FFI.upCast g)
+  return $ if (s == "") then Nothing else Just s
+
+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
+
+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)
diff --git a/src/LLVM/General/Internal/InlineAssembly.hs b/src/LLVM/General/Internal/InlineAssembly.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/InlineAssembly.hs
@@ -0,0 +1,68 @@
+{-# LANGUAGE
+  TemplateHaskell,
+  MultiParamTypeClasses,
+  FlexibleInstances
+  #-}
+module LLVM.General.Internal.InlineAssembly where
+ 
+import Control.Monad
+import Control.Monad.IO.Class
+
+import Foreign.C
+import Foreign.Ptr
+
+import qualified LLVM.General.Internal.FFI.InlineAssembly as FFI
+import qualified LLVM.General.Internal.FFI.LLVMCTypes as FFI
+import qualified LLVM.General.Internal.FFI.Module as FFI
+import qualified LLVM.General.Internal.FFI.PtrHierarchy as FFI
+
+import qualified LLVM.General.AST as A (Definition(..))
+import qualified LLVM.General.AST.InlineAssembly as A
+import qualified LLVM.General.AST.Type as A
+
+import LLVM.General.Internal.Coding 
+import LLVM.General.Internal.EncodeAST
+import LLVM.General.Internal.DecodeAST
+import LLVM.General.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/General/Internal/Instruction.hs b/src/LLVM/General/Internal/Instruction.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/Instruction.hs
@@ -0,0 +1,614 @@
+{-# LANGUAGE 
+  TemplateHaskell,
+  QuasiQuotes,
+  TupleSections,
+  MultiParamTypeClasses,
+  FlexibleContexts,
+  FlexibleInstances
+  #-}
+module LLVM.General.Internal.Instruction where
+
+import qualified Language.Haskell.TH as TH
+import qualified Language.Haskell.TH.Quote as TH
+import qualified LLVM.General.Internal.InstructionDefs as ID
+
+import Data.Functor
+import Control.Monad
+import Control.Monad.Trans
+import Control.Monad.AnyCont
+import Control.Monad.State
+import Control.Monad.Phased
+
+import Foreign.Ptr
+
+import qualified Data.Map as Map
+import qualified Data.List as List
+
+import qualified LLVM.General.Internal.FFI.PtrHierarchy as FFI
+import qualified LLVM.General.Internal.FFI.BinaryOperator as FFI
+import qualified LLVM.General.Internal.FFI.Instruction as FFI
+import qualified LLVM.General.Internal.FFI.Value as FFI
+import qualified LLVM.General.Internal.FFI.User as FFI
+import qualified LLVM.General.Internal.FFI.Builder as FFI
+import qualified LLVM.General.Internal.FFI.Constant as FFI
+import qualified LLVM.General.Internal.FFI.BasicBlock as FFI
+
+import LLVM.General.Internal.Atomicity ()
+import LLVM.General.Internal.Attribute ()
+import LLVM.General.Internal.CallingConvention ()
+import LLVM.General.Internal.Coding
+import LLVM.General.Internal.DecodeAST
+import LLVM.General.Internal.EncodeAST
+import LLVM.General.Internal.Metadata ()
+import LLVM.General.Internal.Operand ()
+import LLVM.General.Internal.RMWOperation ()
+import LLVM.General.Internal.Type
+import LLVM.General.Internal.Value
+
+import qualified LLVM.General.AST as A
+import qualified LLVM.General.AST.Constant as A.C
+
+callInstAttr i j = liftIO $ decodeM =<< FFI.getCallInstAttr i j
+
+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
+
+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
+      [ID.instrP|Ret|] -> do
+        returnOperand' <- if nOps == 0 then return Nothing else Just <$> op 0
+        return $ A.Ret { A.returnOperand = returnOperand', A.metadata' = md }
+      [ID.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
+             }
+      [ID.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
+        }
+      [ID.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
+        }
+      [ID.instrP|Invoke|] -> do
+        cc <- decodeM =<< liftIO (FFI.getInstructionCallConv i)
+        rAttrs <- callInstAttr i 0
+        fv <- liftIO $ FFI.getCallInstCalledValue i
+        f <- decodeM fv
+        args <- forM [1..nOps-3] $ \j -> return (,) `ap` op (j-1) `ap` callInstAttr i j
+        fAttrs <- decodeM =<< liftIO (FFI.getCallInstFunctionAttr i)
+        rd <- successor (nOps - 2)
+        ed <- successor (nOps - 1)
+        return A.Invoke {
+          A.callingConvention' = cc,
+          A.returnAttributes' = rAttrs,
+          A.function' = f,
+          A.arguments' = args,
+          A.functionAttributes' = fAttrs,
+          A.returnDest = rd,
+          A.exceptionDest = ed,
+          A.metadata' = md
+        }
+      [ID.instrP|Resume|] -> do
+        op0 <- op 0
+        return A.Resume {
+          A.operand0' = op0,
+          A.metadata' = md
+        }
+      [ID.instrP|Unreachable|] -> do
+        return A.Unreachable {
+          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
+        forM (zip (rAttrs : argAttrs) [0..]) $ \(attrs, j) -> do
+          attrs <- encodeM attrs
+          liftIO $ FFI.addCallInstAttr i j attrs
+        fAttrs <- encodeM fAttrs
+        liftIO $ FFI.addCallInstFunctionAttr i fAttrs
+        cc <- encodeM cc
+        liftIO $ FFI.setInstructionCallConv 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
+    forM (A.metadata' t) $ \(k, mdn) -> do
+      k <- encodeM k
+      mdn <- encodeM mdn
+      liftIO $ FFI.setMetadata t' k mdn
+    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 b
+            get_nuw b = liftIO $ decodeM =<< FFI.hasNoUnsignedWrap b
+            get_exact b = liftIO $ decodeM =<< FFI.isExact 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") |])
+                "operand0" -> ([], [| op 0 |])
+                "operand1" -> ([], [| op 1 |])
+                "address" -> ([], [| op 0 |])
+                "value" -> ([], [| 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 |])
+                "personalityFunction" -> ([], [| op 0 |])
+                "mask" -> ([], [| cop 2 |])
+                "aggregate" -> ([], [| op 0 |])
+                "metadata" -> ([], [| meta i |])
+                "iPredicate" -> ([], [| decodeM =<< liftIO (FFI.getICmpPredicate i) |])
+                "fpPredicate" -> ([], [| decodeM =<< liftIO (FFI.getFCmpPredicate i) |])
+                "isTailCall" -> ([], [| decodeM =<< liftIO (FFI.isTailCall i) |])
+                "callingConvention" -> ([], [| decodeM =<< liftIO (FFI.getInstructionCallConv i) |])
+                "returnAttributes" -> ([], [| callInstAttr i 0 |])
+                "f" -> ([], [| liftIO $ FFI.getCallInstCalledValue i |])
+                "function" -> (["f"], [| decodeM $(TH.dyn "f") |])
+                "arguments" -> ([], [| forM [1..nOps-1] $ \j -> return (,) `ap` op (j-1) `ap` callInstAttr i j |])
+                "clauses" -> 
+                  ([], [| forM [1..nOps-1] $ \j -> do
+                          v <- liftIO $ FFI.getOperand (FFI.upCast i) j
+                          c <- decodeM =<< (liftIO $ FFI.isAConstant v)
+                          t <- typeOf v
+                          return $ case t of { A.ArrayType _ _ -> A.Filter; _ -> A.Catch} $ c |])
+                "functionAttributes" ->
+                    ([], [| decodeM =<< liftIO (FFI.getCallInstFunctionAttr i) |])
+                "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)) |])
+                "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.getAtomicRMWOperation i) |])
+                "cleanup" -> ([], [| decodeM =<< liftIO (FFI.isCleanup i) |])
+                _ -> ([], [| 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) where
+      encodeM o = scopeAnyCont $ do
+        builder <- gets encodeStateBuilder
+        s <- encodeM ""
+        $(
+          [|
+            case o of
+              A.ICmp { 
+                A.iPredicate = pred, 
+                A.operand0 = op0, 
+                A.operand1 = op1, 
+                A.metadata = [] 
+              } -> do
+                op0' <- encodeM op0
+                op1' <- encodeM op1
+                pred <- encodeM pred
+                i <- liftIO $ FFI.buildICmp builder pred op0' op1' s
+                return $ FFI.upCast i
+              A.FCmp {
+                A.fpPredicate = pred, 
+                A.operand0 = op0, 
+                A.operand1 = op1, 
+                A.metadata = [] 
+              } -> do
+                op0' <- encodeM op0
+                op1' <- encodeM op1
+                pred <- encodeM pred
+                i <- liftIO $ FFI.buildFCmp builder pred op0' op1' s
+                return $ FFI.upCast i
+              A.Phi { A.type' = t, A.incomingValues = ivs } -> do
+                 t' <- encodeM t
+                 i <- liftIO $ FFI.buildPhi builder t' s
+                 defer
+                 let (ivs3, bs3) = unzip ivs
+                 ivs3' <- encodeM ivs3
+                 bs3' <- encodeM bs3
+                 liftIO $ FFI.addIncoming i ivs3' bs3'
+                 return $ FFI.upCast i
+              A.Call {
+                A.isTailCall = tc,
+                A.callingConvention = cc,
+                A.returnAttributes = rAttrs,
+                A.function = f,
+                A.arguments = args,
+                A.functionAttributes = fAttrs,
+                A.metadata = []
+              } -> do
+                fv <- encodeM f
+                let (argvs, argAttrs) = unzip args
+                (n, argvs) <- encodeM argvs
+                i <- liftIO $ FFI.buildCall builder fv argvs n s
+                forM (zip (rAttrs : argAttrs) [0..]) $ \(attrs, j) -> do
+                  attrs <- encodeM attrs
+                  liftIO $ FFI.addCallInstAttr i j attrs
+                fAttrs <- encodeM fAttrs
+                liftIO $ FFI.addCallInstFunctionAttr i fAttrs
+                when tc $ do
+                  tc <- encodeM tc
+                  liftIO $ FFI.setTailCall i tc
+                cc <- encodeM cc
+                liftIO $ FFI.setInstructionCallConv i cc
+                return $ FFI.upCast 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 $ FFI.upCast 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 $ FFI.upCast 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 $ FFI.upCast 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 $ FFI.upCast 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 $ FFI.upCast 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 $ FFI.upCast 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 $ FFI.upCast i
+              A.LandingPad { 
+                A.type' = t,
+                A.personalityFunction = pf,
+                A.cleanup = cl, 
+                A.clauses = cs
+              } -> do
+                t' <- encodeM t
+                pf' <- encodeM pf
+                i <- liftIO $ FFI.buildLandingPad builder t' pf' (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 $ fail $ "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 $ fail $ "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 $ FFI.upCast i
+              A.Alloca { A.allocatedType = alt, A.numElements = n, A.alignment = alignment } -> do 
+                 alt' <- encodeM alt
+                 n' <- maybe (return nullPtr) encodeM n
+                 i <- liftIO $ FFI.buildAlloca builder alt' n' s
+                 unless (alignment == 0) $ liftIO $ FFI.setInstrAlignment i (fromIntegral alignment)
+                 return $ FFI.upCast i
+              A.Load {
+                A.volatile = vol,
+                A.address = a,
+                A.alignment = al,
+                A.maybeAtomicity = mat,
+                A.metadata = []
+              } -> do
+                 a' <- encodeM a
+                 al <- encodeM al
+                 vol <- encodeM vol
+                 (ss, mo) <- encodeM mat
+                 i <- liftIO $ FFI.buildLoad builder a' al vol mo ss s
+                 return $ FFI.upCast i
+              A.Store { 
+                A.volatile = vol, 
+                A.address = a, 
+                A.value = v, 
+                A.maybeAtomicity = mat, 
+                A.alignment = al, 
+                A.metadata = []
+              } -> do
+                 a' <- encodeM a
+                 v' <- encodeM v
+                 al <- encodeM al
+                 vol <- encodeM vol
+                 (ss, mo) <- encodeM mat
+                 i <- liftIO $ FFI.buildStore builder a' v' al vol mo ss s
+                 return $ FFI.upCast i
+              A.GetElementPtr { A.address = a, A.indices = is, A.inBounds = ib } -> do
+                 a' <- encodeM a
+                 (n, is') <- encodeM is
+                 i <- liftIO $ FFI.buildGetElementPtr builder a' is' n s
+                 when ib $ do
+                   ib <- encodeM ib 
+                   liftIO $ FFI.setInBounds i ib
+                 return $ FFI.upCast i
+              A.Fence { A.atomicity = at } -> do
+                 (ss, mo) <- encodeM at
+                 i <- liftIO $ FFI.buildFence builder mo ss s
+                 return $ FFI.upCast i
+              A.CmpXchg { 
+                A.volatile = vol, 
+                A.address = a, A.expected = e, A.replacement = r,
+                A.atomicity = at,
+                A.metadata = []
+              } -> do
+                 a' <- encodeM a
+                 e' <- encodeM e
+                 r' <- encodeM r
+                 vol <- encodeM vol
+                 (ss, mo) <- encodeM at
+                 i <- liftIO $ FFI.buildCmpXchg builder a' e' r' vol mo ss s
+                 return $ FFI.upCast i
+              A.AtomicRMW {
+                A.volatile = vol,
+                A.rmwOperation = rmwOp,
+                A.address = a,
+                A.value = v,
+                A.atomicity = at,
+                A.metadata = []
+              } -> do
+                 a' <- encodeM a
+                 v' <- encodeM v
+                 rmwOp <- encodeM rmwOp
+                 vol <- encodeM vol
+                 (ss, mo) <- encodeM at
+                 i <- liftIO $ FFI.buildAtomicRMW builder rmwOp a' v' vol mo ss s
+                 return $ FFI.upCast i
+              o -> $(
+                     let
+                       fieldData :: String -> [Either TH.ExpQ TH.ExpQ]
+                       fieldData s = case s of
+                         "operand0" -> [Left [| encodeM $(TH.dyn s) |] ]
+                         "operand1" -> [Left [| encodeM $(TH.dyn s) |] ]
+                         "type'" -> [Left [| encodeM $(TH.dyn s) |] ]
+                         "nsw" -> [Left [| encodeM $(TH.dyn s) |] ]
+                         "nuw" -> [Left [| encodeM $(TH.dyn s) |] ]
+                         "exact" -> [Left [| encodeM $(TH.dyn s) |] ]
+                         "metadata" -> 
+                           [Right [| unless (List.null $(TH.dyn s)) $ error "can't handle metadata yet" |]]
+                         _ -> error $ "unhandled instruction field " ++ show s
+                     in
+                     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.InstructionDef { ID.instructionKind = k }) <- Map.toList ID.instructionDefs,
+                       k `List.elem` [ID.Binary, ID.Cast],
+                       let
+                         TH.RecC fullName fields = findInstrFields name
+                         (fieldNames, _, _) = unzip3 fields
+                         cTorFields = [
+                            (s, binding)
+                            | f <- fieldNames,
+                            let s = TH.nameBase f,
+                            Left binding <- fieldData s
+                           ]
+                         handlerBody = (
+                           [ TH.bindS (TH.varP (TH.mkName s)) binding | (s, binding) <- cTorFields ]
+                           ++ [
+                            TH.bindS 
+                              (TH.varP (TH.mkName "i"))
+                              [| liftIO $ $(foldl1 TH.appE . map TH.dyn $ [ 
+                                   "FFI.build" ++ name,
+                                   "builder"
+                                   ] ++ [
+                                    s | (s, _) <- cTorFields, s /= "metadata"
+                                   ] ++ [
+                                   "s"
+                                   ] ) |]
+
+                           ] ++ [
+                            TH.noBindS action
+                            | f <- fieldNames,
+                              let s = TH.nameBase f,
+                              Right action <- fieldData s
+                           ] ++ [
+                            TH.noBindS [| return $ FFI.upCast $(TH.dyn "i") |]
+                           ]
+                          )
+                         ]
+                    )
+
+           |]
+         )
+   |]
+ )
+
+
+instance DecodeM DecodeAST a (Ptr FFI.Instruction) => DecodeM DecodeAST (A.Named a) (Ptr FFI.Instruction) where
+  decodeM i = do
+    t <- typeOf i
+    (if t == A.VoidType then (return A.Do) else (return (A.:=) `ap` getLocalName i)) `ap` (do defer; 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
+
+
diff --git a/src/LLVM/General/Internal/InstructionDefs.hs b/src/LLVM/General/Internal/InstructionDefs.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/InstructionDefs.hs
@@ -0,0 +1,59 @@
+{-# LANGUAGE
+  TemplateHaskell
+  #-}
+
+module LLVM.General.Internal.InstructionDefs (
+  astInstructionRecs,
+  astConstantRecs,
+  instructionDefs,
+  ID.InstructionKind(..),
+  ID.InstructionDef(..),
+  instrP,
+  innerJoin,
+  outerJoin
+  ) where
+
+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.General.Internal.FFI.InstructionDefs as ID
+
+import qualified LLVM.General.AST.Instruction as A
+import qualified LLVM.General.AST.Constant as A.C
+
+$(do
+   let ctorRecs t = do
+         TH.TyConI (TH.DataD _ _ _ cons _) <- TH.reify t
+         TH.dataToExpQ (const Nothing) $ [ (TH.nameBase n, rec) | rec@(TH.RecC n _) <- cons ]
+
+   [d| 
+      astInstructionRecs = Map.fromList $(ctorRecs ''A.Instruction)
+      astConstantRecs = Map.fromList $(ctorRecs ''A.C.Constant)
+    |]
+ )
+
+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.mergeWithKey (\_ a b -> Just (a,b)) (const Map.empty) (const Map.empty)
+       
+outerJoin :: Ord k => Map k a -> Map k b -> Map k (Maybe a, Maybe b)
+outerJoin = Map.mergeWithKey 
+            (\_ a b -> Just (Just a, Just b))
+            (Map.map $ \a -> (Just a, Nothing))
+            (Map.map $ \b -> (Nothing, Just b))
+
+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/General/Internal/IntegerPredicate.hs b/src/LLVM/General/Internal/IntegerPredicate.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/IntegerPredicate.hs
@@ -0,0 +1,25 @@
+{-# LANGUAGE
+  TemplateHaskell,
+  MultiParamTypeClasses,
+  FlexibleInstances
+  #-}
+
+module LLVM.General.Internal.IntegerPredicate where
+
+import LLVM.General.Internal.Coding
+
+import qualified LLVM.General.Internal.FFI.LLVMCTypes as FFI
+import qualified LLVM.General.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/General/Internal/Metadata.hs b/src/LLVM/General/Internal/Metadata.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/Metadata.hs
@@ -0,0 +1,55 @@
+{-# LANGUAGE
+  MultiParamTypeClasses,
+  FlexibleInstances
+  #-}
+module LLVM.General.Internal.Metadata where
+
+import Control.Monad.State
+import Control.Monad.AnyCont
+
+import Foreign.Ptr
+
+import qualified Foreign.Marshal.Array as FMA
+import qualified Data.Array as Array
+
+import qualified LLVM.General.Internal.FFI.LLVMCTypes as FFI
+import qualified LLVM.General.Internal.FFI.Metadata as FFI
+import qualified LLVM.General.Internal.FFI.PtrHierarchy as FFI
+
+import LLVM.General.Internal.Context
+import LLVM.General.Internal.Coding
+import LLVM.General.Internal.EncodeAST
+import LLVM.General.Internal.DecodeAST
+import LLVM.General.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/General/Internal/Module.hs b/src/LLVM/General/Internal/Module.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/Module.hs
@@ -0,0 +1,281 @@
+{-#
+  LANGUAGE
+  TupleSections,
+  ScopedTypeVariables
+  #-}
+
+-- | This Haskell module is for/of functions for handling LLVM modules.
+module LLVM.General.Internal.Module where
+
+import Control.Monad.Trans
+import Control.Monad.State
+import Control.Monad.Phased
+import Control.Monad.AnyCont
+import Control.Applicative
+import Control.Exception
+
+import Foreign.Ptr
+import Foreign.Marshal.Alloc (free)
+
+import qualified LLVM.General.Internal.FFI.Assembly as FFI
+import qualified LLVM.General.Internal.FFI.Builder as FFI
+import qualified LLVM.General.Internal.FFI.Function as FFI
+import qualified LLVM.General.Internal.FFI.GlobalAlias as FFI
+import qualified LLVM.General.Internal.FFI.GlobalValue as FFI
+import qualified LLVM.General.Internal.FFI.GlobalVariable as FFI
+import qualified LLVM.General.Internal.FFI.Iterate as FFI
+import qualified LLVM.General.Internal.FFI.Module as FFI
+import qualified LLVM.General.Internal.FFI.PtrHierarchy as FFI
+import qualified LLVM.General.Internal.FFI.Value as FFI
+import qualified LLVM.General.Internal.FFI.Metadata as FFI
+
+import LLVM.General.Internal.BasicBlock
+import LLVM.General.Internal.Coding
+import LLVM.General.Internal.Context
+import LLVM.General.Internal.DataLayout
+import LLVM.General.Internal.DecodeAST
+import LLVM.General.Internal.Diagnostic
+import LLVM.General.Internal.EncodeAST
+import LLVM.General.Internal.Function
+import LLVM.General.Internal.Global
+import LLVM.General.Internal.Metadata
+import LLVM.General.Internal.Operand
+import LLVM.General.Internal.Type
+import LLVM.General.Internal.Value
+
+import LLVM.General.Diagnostic
+
+import qualified LLVM.General.AST as A
+import qualified LLVM.General.AST.DataLayout as A
+import qualified LLVM.General.AST.AddrSpace as A
+import qualified LLVM.General.AST.Global as A.G
+
+-- | <http://llvm.org/doxygen/classllvm_1_1Module.html>
+newtype Module = Module (Ptr FFI.Module)
+
+-- | parse 'Module' from LLVM assembly
+withModuleFromString :: Context -> String -> (Module -> IO a) -> IO (Either Diagnostic a)
+withModuleFromString (Context c) s f = flip runAnyContT return $ do
+  s <- encodeM s
+  liftIO $ withSMDiagnostic $ \smDiag -> do
+    m <- FFI.getModuleFromAssemblyInContext c s smDiag
+    if m == nullPtr then
+      Left <$> getDiagnostic smDiag
+     else
+      Right <$> finally (f (Module m)) (FFI.disposeModule m)
+
+-- | generate LLVM assembly from a 'Module'
+moduleString :: Module -> IO String
+moduleString (Module m) = bracket (FFI.getModuleAssembly m) free $ decodeM
+
+setTargetTriple :: Ptr FFI.Module -> String -> IO ()
+setTargetTriple m t = flip runAnyContT return $ 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 -> IO ()
+setDataLayout m dl = flip runAnyContT return $ do
+  s <- encodeM (dataLayoutToString dl)
+  liftIO $ FFI.setDataLayout m s
+
+getDataLayout :: Ptr FFI.Module -> IO (Maybe A.DataLayout)
+getDataLayout m = parseDataLayout <$> (decodeM =<< FFI.getDataLayout m)
+
+-- | Build a 'Module' from a 'LLVM.General.AST.Module'.
+withModuleFromAST :: Context -> A.Module -> (Module -> IO a) -> IO (Either String a)
+withModuleFromAST context@(Context c) (A.Module moduleId dataLayout triple definitions) f = do
+  let makeModule = flip runAnyContT return $ do
+                     moduleId <- encodeM moduleId
+                     liftIO $ FFI.moduleCreateWithNameInContext moduleId c
+  bracket makeModule FFI.disposeModule $ \m -> do
+    maybe (return ()) (setDataLayout m) dataLayout
+    maybe (return ()) (setTargetTriple m) triple
+    r <- runEncodeAST context $ forInterleavedM definitions $ \d -> case d of
+      A.TypeDefinition n t -> do
+        t' <- createNamedType n
+        defineType n t'
+        defer
+        maybe (return ()) (setNamedType t') t
+
+      A.MetadataNodeDefinition i os -> do
+        replicateM_ 2 defer
+        t <- liftIO $ FFI.createTemporaryMDNodeInContext c
+        defineMDNode i t
+        defer
+        n <- encodeM (A.MetadataNode os)
+        liftIO $ FFI.replaceAllUsesWith (FFI.upCast t) (FFI.upCast n)
+        defineMDNode i n
+        liftIO $ FFI.destroyTemporaryMDNode t
+
+      A.NamedMetadataDefinition n ids -> do
+        replicateM_ 4 defer
+        n <- encodeM n
+        ids <- encodeM (map A.MetadataNodeReference ids)
+        nm <- liftIO $ FFI.getOrAddNamedMetadata m n
+        liftIO $ FFI.namedMetadataAddOperands nm ids
+
+      A.ModuleInlineAssembly s -> do
+        s <- encodeM s
+        liftIO $ FFI.moduleAppendInlineAsm m (FFI.ModuleAsm s)
+
+      A.GlobalDefinition g -> do
+        replicateM_ 2 defer
+        g' :: 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 m typ gName 
+                             (fromIntegral ((\(A.AddrSpace a) -> a) $ A.G.addrSpace g))
+            defineGlobal n g'
+            liftIO $ do
+              tl <- encodeM (A.G.isThreadLocal g)
+              FFI.setThreadLocal g' tl
+              hua <- encodeM (A.G.hasUnnamedAddr g)
+              FFI.setUnnamedAddr (FFI.upCast g') hua
+              ic <- encodeM (A.G.isConstant g)
+              FFI.setGlobalConstant g' ic
+            defer
+            maybe (return ()) ((liftIO . FFI.setInitializer g') <=< encodeM) (A.G.initializer g)
+            setSection g' (A.G.section g)
+            setAlignment g' (A.G.alignment g)
+            return (FFI.upCast g')
+          (a@A.G.GlobalAlias { A.G.name = n }) -> do
+            typ <- encodeM (A.G.type' a)
+            a' <- liftIO $ withName n $ \name -> FFI.justAddAlias m typ name
+            defineGlobal n a'
+            defer
+            (liftIO . FFI.setAliasee a') =<< encodeM (A.G.aliasee a)
+            return (FFI.upCast a')
+          (A.Function _ _ cc rAttrs resultType fName (args,isVarArgs) attrs _ _ blocks) -> do
+            typ <- encodeM $ A.FunctionType resultType (map (\(A.Parameter t _ _) -> t) args) isVarArgs
+            f <- liftIO . withName fName $ \fName -> FFI.addFunction m fName typ
+            defineGlobal fName f
+            cc <- encodeM cc
+            liftIO $ FFI.setFunctionCallConv f cc
+            rAttrs <- encodeM rAttrs
+            liftIO $ FFI.addFunctionRetAttr f rAttrs
+            liftIO $ setFunctionAttrs f attrs
+            setSection f (A.G.section g)
+            setAlignment f (A.G.alignment g)
+            encodeScope $ do
+              forM blocks $ \(A.BasicBlock bName _ _) -> do
+                b <- liftIO $ withName bName $ \bName -> FFI.appendBasicBlockInContext c f bName
+                defineBasicBlock fName bName b
+              defer
+              let nParams = length args
+              ps <- allocaArray nParams
+              liftIO $ FFI.getParams f ps
+              params <- peekArray nParams ps
+              forM (zip args params) $ \(A.Parameter _ n attrs, p) -> do
+                defineLocal n p
+                n <- encodeM n
+                liftIO $ FFI.setValueName (FFI.upCast p) n
+                attrs <- encodeM attrs
+                liftIO $ FFI.addAttribute p attrs
+                return ()
+              forInterleavedM blocks $ \(A.BasicBlock bName namedInstrs term) -> do
+                b <- encodeM bName
+                (do builder <- gets encodeStateBuilder; liftIO $ FFI.positionBuilderAtEnd builder b)
+                (mapM encodeM namedInstrs :: EncodeAST [Ptr FFI.Instruction])
+                encodeM term :: EncodeAST (Ptr FFI.Instruction)
+            return (FFI.upCast f)
+        setLinkage g' (A.G.linkage g)
+        setVisibility g' (A.G.visibility g)
+
+    either (return . Left) (const $ Right <$> f (Module m)) r
+
+-- | Get a 'LLVM.General.AST.Module' from a 'Module'.
+moduleAST :: Module -> IO A.Module
+moduleAST (Module mod) = runDecodeAST $ do
+  c <- return Context `ap` liftIO (FFI.getModuleContext mod)
+  getMetadataKindNames c
+  return A.Module 
+   `ap` (liftIO $ bracket (FFI.getModuleIdentifier mod) free decodeM)
+   `ap` (liftIO $ getDataLayout mod)
+   `ap` (liftIO $ do
+           s <- decodeM <=< FFI.getTargetTriple $ mod
+           return $ if s == "" then Nothing else Just s)
+   `ap` (
+     do
+       gs <- map A.GlobalDefinition . concat <$> runInterleaved [
+          do
+            ffiGlobals <- liftIO $ FFI.getXs (FFI.getFirstGlobal mod) FFI.getNextGlobal
+            forM ffiGlobals $ \g -> do
+              A.PointerType t as <- typeOf g
+              return A.GlobalVariable
+               `ap` getGlobalName g
+               `ap` getLinkage g
+               `ap` getVisibility g
+               `ap` (liftIO $ decodeM =<< FFI.isThreadLocal g)
+               `ap` return as
+               `ap` (liftIO $ decodeM =<< FFI.hasUnnamedAddr (FFI.upCast g))
+               `ap` (liftIO $ decodeM =<< FFI.isGlobalConstant g)
+               `ap` return t
+               `ap` (do
+                      defer
+                      i <- liftIO $ FFI.getInitializer g
+                      if i == nullPtr then return Nothing else Just <$> decodeM i)
+               `ap` getSection g
+               `ap` getAlignment g,
+
+          do
+            ffiAliases <- liftIO $ FFI.getXs (FFI.getFirstAlias mod) FFI.getNextAlias
+            forM ffiAliases $ \a -> do
+              return A.G.GlobalAlias
+               `ap` (do n <- getGlobalName a; defer; return n)
+               `ap` getLinkage a
+               `ap` getVisibility a
+               `ap` typeOf a
+               `ap` (decodeM =<< (liftIO $ FFI.getAliasee a)),
+
+          do
+            ffiFunctions <- liftIO $ FFI.getXs (FFI.getFirstFunction mod) FFI.getNextFunction
+            forM ffiFunctions $ \f -> localScope $ do
+              A.PointerType (A.FunctionType returnType _ isVarArg) _ <- typeOf f
+              return A.Function
+                 `ap` getLinkage f
+                 `ap` getVisibility f
+                 `ap` (liftIO $ decodeM =<< FFI.getFunctionCallConv f)
+                 `ap` (liftIO $ decodeM =<< FFI.getFunctionRetAttr f)
+                 `ap` return returnType
+                 `ap` (getGlobalName f)
+                 `ap` ((, isVarArg) <$> getParameters f)
+                 `ap` (liftIO $ getFunctionAttrs f)
+                 `ap` getSection f
+                 `ap` getAlignment f
+                 `ap` (do
+                       ffiBasicBlocks <- liftIO $ FFI.getXs (FFI.getFirstBasicBlock f) FFI.getNextBasicBlock
+                       runInterleaved . flip map ffiBasicBlocks $ \b -> 
+                           return A.BasicBlock
+                            `ap` (do n <- getLocalName b; defer; return n)
+                            `iap` getNamedInstructions b
+                            `iap` getBasicBlockTerminator b
+                     )
+        ]
+
+       tds <- getStructDefinitions
+
+       ias <- decodeM =<< liftIO (FFI.moduleGetInlineAsm mod)
+
+       nmds <- 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
+              l <- alloca
+              cs <- liftIO $ FFI.getNamedMetadataName nm l
+              l <- peek l
+              return A.NamedMetadataDefinition
+                 `ap` decodeM (cs, l)
+                 `ap` liftM (map (\(A.MetadataNodeReference mid) -> mid)) (decodeM (n, os))
+         
+       mds <- getMetadataDefinitions
+
+       return $ tds ++ ias ++ gs ++ nmds ++ mds
+   )
diff --git a/src/LLVM/General/Internal/Operand.hs b/src/LLVM/General/Internal/Operand.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/Operand.hs
@@ -0,0 +1,96 @@
+{-# LANGUAGE
+  MultiParamTypeClasses,
+  FlexibleInstances
+  #-}
+module LLVM.General.Internal.Operand where
+
+import Data.Functor
+import Control.Monad.State
+import Control.Monad.AnyCont
+
+import Foreign.Ptr
+
+import qualified LLVM.General.Internal.FFI.Constant as FFI
+import qualified LLVM.General.Internal.FFI.InlineAssembly as FFI
+import qualified LLVM.General.Internal.FFI.Metadata as FFI
+import qualified LLVM.General.Internal.FFI.PtrHierarchy as FFI
+
+import LLVM.General.Internal.Coding
+import LLVM.General.Internal.Constant ()
+import LLVM.General.Internal.Context
+import LLVM.General.Internal.DecodeAST
+import LLVM.General.Internal.EncodeAST
+import LLVM.General.Internal.InlineAssembly ()
+import LLVM.General.Internal.Metadata ()
+
+import qualified LLVM.General.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
+        mds <- liftIO $ FFI.isAMDString v
+        if mds /= nullPtr 
+         then return A.MetadataStringOperand `ap` decodeM mds
+         else
+           do
+             mdn <- liftIO $ FFI.isAMDNode v
+             if mdn /= nullPtr
+              then return A.MetadataNodeOperand `ap` decodeM mdn
+              else
+                return A.LocalReference `ap` getLocalName v
+
+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 n) = referLocal n
+  encodeM (A.MetadataStringOperand s) = do
+    Context c <- gets encodeStateContext
+    s <- encodeM s
+    liftM FFI.upCast $ liftIO $ FFI.mdStringInContext c s
+  encodeM (A.MetadataNodeOperand mdn) = (FFI.upCast :: Ptr FFI.MDNode -> Ptr FFI.Value) <$> encodeM mdn
+
+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 [A.Operand] (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.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/General/Internal/PassManager.hs b/src/LLVM/General/Internal/PassManager.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/PassManager.hs
@@ -0,0 +1,110 @@
+{-# LANGUAGE
+  TemplateHaskell,
+  FlexibleInstances
+  #-}
+
+module LLVM.General.Internal.PassManager where
+
+import qualified Language.Haskell.TH as TH
+
+import Control.Exception
+import Control.Monad
+import Control.Monad.IO.Class
+import Control.Applicative
+
+import Control.Monad.AnyCont
+
+import Foreign.Ptr
+
+import qualified LLVM.General.Internal.FFI.PassManager as FFI
+import qualified LLVM.General.Internal.FFI.Transforms as FFI
+
+import LLVM.General.Internal.Module
+import LLVM.General.Internal.Target
+import LLVM.General.Internal.Coding
+import LLVM.General.Transforms
+
+-- | <http://llvm.org/doxygen/classllvm_1_1PassManager.html>
+newtype PassManager = PassManager (Ptr FFI.PassManager)
+
+-- | There are different ways to get a 'PassManager'. This class embodies them.
+class PassManagerSpecification s where
+    -- | make a 'PassManager'
+    createPassManager :: s -> IO (Ptr FFI.PassManager)
+
+-- | 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.
+data CuratedPassSetSpec = CuratedPassSetSpec {
+    optLevel :: Maybe Int,
+    sizeLevel :: Maybe Int,
+    unitAtATime :: Maybe Bool,
+    simplifyLibCalls :: Maybe Bool,
+    useInlinerWithThreshold :: Maybe Int
+  }
+
+-- | Helper to make a 'CuratedPassSetSpec'
+defaultCuratedPassSetSpec = CuratedPassSetSpec {
+    optLevel = Nothing,
+    sizeLevel = Nothing,
+    unitAtATime = Nothing,
+    simplifyLibCalls = Nothing,
+    useInlinerWithThreshold = Nothing
+  }
+
+instance PassManagerSpecification CuratedPassSetSpec where
+  createPassManager s = bracket FFI.passManagerBuilderCreate FFI.passManagerBuilderDispose $ \b -> do
+    let handleOption g m = maybe (return ()) (g b . fromIntegral . fromEnum) (m s)
+    handleOption FFI.passManagerBuilderSetOptLevel optLevel
+    handleOption FFI.passManagerBuilderSetSizeLevel sizeLevel
+    handleOption FFI.passManagerBuilderSetDisableUnitAtATime (liftM not . unitAtATime)
+    handleOption FFI.passManagerBuilderSetDisableSimplifyLibCalls (liftM not . simplifyLibCalls)
+    handleOption FFI.passManagerBuilderUseInlinerWithThreshold useInlinerWithThreshold
+    pm <- FFI.createPassManager
+    FFI.passManagerBuilderPopulateModulePassManager b pm
+    return pm
+
+data PassSetSpec = PassSetSpec [Pass] (Maybe TargetLowering)
+
+instance PassManagerSpecification PassSetSpec where
+  createPassManager (PassSetSpec ps tl') = flip runAnyContT return $ do
+    let tl = maybe nullPtr (\(TargetLowering tl) -> tl) tl'
+    pm <- liftIO $ FFI.createPassManager
+    forM ps $ \p -> $(
+      do
+        TH.TyConI (TH.DataD _ _ _ cons _) <- TH.reify ''Pass
+        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"]
+                      ++ ["tl" | FFI.needsTargetLowering (TH.nameBase n)]
+                      ++ fns)
+                  )
+                 |]
+               ]
+          TH.match (TH.conP n $ map (TH.varP . TH.mkName) fns) (TH.normalB (TH.doE actions)) []
+     )
+    return pm
+
+instance PassManagerSpecification [Pass] where
+  createPassManager ps = createPassManager (PassSetSpec ps Nothing)
+
+instance PassManagerSpecification ([Pass], TargetLowering) where
+  createPassManager (ps, tl) = createPassManager (PassSetSpec ps (Just tl))
+
+-- | bracket the creation of a 'PassManager'
+withPassManager :: PassManagerSpecification s => s -> (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) (Module m) = toEnum . fromIntegral <$> FFI.runPassManager p m
diff --git a/src/LLVM/General/Internal/RMWOperation.hs b/src/LLVM/General/Internal/RMWOperation.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/RMWOperation.hs
@@ -0,0 +1,27 @@
+{-# LANGUAGE
+  TemplateHaskell,
+  MultiParamTypeClasses,
+  FlexibleInstances
+  #-}
+
+module LLVM.General.Internal.RMWOperation where
+
+import LLVM.General.AST.RMWOperation
+
+import qualified LLVM.General.Internal.FFI.LLVMCTypes as FFI
+
+import LLVM.General.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/General/Internal/String.hs b/src/LLVM/General/Internal/String.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/String.hs
@@ -0,0 +1,31 @@
+{-# LANGUAGE
+  MultiParamTypeClasses,
+  FlexibleInstances,
+  UndecidableInstances
+  #-}
+module LLVM.General.Internal.String where
+
+import Control.Arrow
+import Control.Monad
+import Foreign.C (CString, CChar)
+import Foreign.Ptr
+import Control.Monad.AnyCont
+import Control.Monad.IO.Class
+
+import LLVM.General.Internal.Coding
+
+import qualified Data.ByteString as BS
+import qualified Data.Text as T
+import qualified Data.Text.Encoding as T
+
+instance (MonadAnyCont IO e) => EncodeM e String CString where
+  encodeM s = anyContToM (BS.useAsCString . T.encodeUtf8 . T.pack $ s)
+
+instance (Integral i, MonadAnyCont IO e) => EncodeM e String (Ptr CChar, i) where
+  encodeM s = anyContToM ((. (. second fromIntegral)) . BS.useAsCStringLen . T.encodeUtf8 . T.pack $ s)
+
+instance (MonadIO d) => DecodeM d String CString where
+  decodeM = liftIO . liftM (T.unpack . T.decodeUtf8) . BS.packCString
+
+instance (Integral i, MonadIO d) => DecodeM d String (Ptr CChar, i) where
+  decodeM = liftIO . liftM (T.unpack . T.decodeUtf8) . BS.packCStringLen . second fromIntegral
diff --git a/src/LLVM/General/Internal/Target.hs b/src/LLVM/General/Internal/Target.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/Target.hs
@@ -0,0 +1,229 @@
+{-# LANGUAGE
+  TemplateHaskell,
+  FlexibleInstances,
+  MultiParamTypeClasses,
+  TupleSections,
+  RecordWildCards
+  #-}
+module LLVM.General.Internal.Target where
+
+import Control.Monad
+import Control.Exception
+import Data.Functor
+import Control.Monad.IO.Class
+import Control.Monad.AnyCont
+
+import Foreign.Ptr
+import Foreign.Marshal.Alloc (free)
+
+import LLVM.General.Internal.Coding
+import LLVM.General.Internal.String ()
+
+import qualified LLVM.General.Internal.FFI.LLVMCTypes as FFI
+import qualified LLVM.General.Internal.FFI.Target as FFI
+
+import qualified LLVM.General.Relocation as Reloc
+import qualified LLVM.General.Target.Options as TO
+import qualified LLVM.General.CodeModel as CodeModel
+import qualified LLVM.General.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)
+ ]
+
+newtype Target = Target (Ptr FFI.Target)
+
+-- | Find a Target given an architecture and/or a \"triple\".
+-- | <http://llvm.org/doxygen/structllvm_1_1TargetRegistry.html#a3105b45e546c9cc3cf78d0f2ec18ad89>
+lookupTarget :: 
+  Maybe String -- ^ arch
+  -> String -- ^ \"triple\" - e.g. x86_64-unknown-linux-gnu
+  -> IO (Either String (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
+  let readString p = do
+        s <- peek p
+        r <- decodeM s
+        liftIO $ free s
+        return r
+  if (target == nullPtr) then
+     Left <$> readString cErrorP
+   else
+     Right . (Target target, ) <$> readString 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.targetOptionFlagNoFramePointerElim, TO.noFramePointerElimination),
+    (FFI.targetOptionFlagNoFramePointerElimNonLeaf, TO.noFramePointerEliminationNonLeaf),
+    (FFI.targetOptionFlagLessPreciseFPMADOption, TO.lessPreciseFloatingPointMultiplyAddOption),
+    (FFI.targetOptionFlagUnsafeFPMath, TO.unsafeFloatingPointMath),
+    (FFI.targetOptionFlagNoInfsFPMath, TO.noInfinitiesFloatingPointMath),
+    (FFI.targetOptionFlagNoNaNsFPMath, TO.noNaNsFloatingPointMath),
+    (FFI.targetOptionFlagHonorSignDependentRoundingFPMathOption, TO.honorSignDependentRoundingFloatingPointMathOption),
+    (FFI.targetOptionFlagUseSoftFloat, TO.useSoftFloat),
+    (FFI.targetOptionFlagNoZerosInBSS, TO.noZerosInBSS),
+    (FFI.targetOptionFlagJITExceptionHandling, TO.jITExceptionHandling),
+    (FFI.targetOptionFlagJITEmitDebugInfo, TO.jITEmitDebugInfo),
+    (FFI.targetOptionFlagJITEmitDebugInfoToDisk, TO.jITEmitDebugInfoToDisk),
+    (FFI.targetOptionFlagGuaranteedTailCallOpt, TO.guaranteedTailCallOptimization),
+    (FFI.targetOptionFlagDisableTailCalls, TO.disableTailCalls),
+    (FFI.targetOptionFlagEnableFastISel, TO.enableFastInstructionSelection),
+    (FFI.targetOptionFlagPositionIndependentExecutable, TO.positionIndependentExecutable),
+    (FFI.targetOptionFlagEnableSegmentedStacks, TO.enableSegmentedStacks),
+    (FFI.targetOptionFlagUseInitArray, TO.useInitArray),
+    (FFI.targetOptionFlagRealignStack, TO.realignStack) 
+   ]
+  FFI.setStackAlignmentOverride cOpts =<< encodeM (TO.stackAlignmentOverride hOpts)
+  flip runAnyContT return $ do
+    n <- encodeM (TO.trapFunctionName hOpts)
+    liftIO $ FFI.setTrapFuncName cOpts n
+  FFI.setFloatABIType cOpts =<< encodeM (TO.floatABIType hOpts)
+  FFI.setAllowFPOpFusion cOpts =<< encodeM (TO.allowFloatingPointOperationFusion hOpts)
+  FFI.setSSPBufferSize cOpts =<< encodeM (TO.stackSmashingProtectionBufferSize hOpts)
+  
+-- | get all target options
+peekTargetOptions :: TargetOptions -> IO TO.Options
+peekTargetOptions (TargetOptions tOpts) = do
+  let gof = decodeM <=< FFI.getTargetOptionsFlag tOpts 
+  printMachineCode
+    <- gof FFI.targetOptionFlagPrintMachineCode
+  noFramePointerElimination
+    <- gof FFI.targetOptionFlagNoFramePointerElim
+  noFramePointerEliminationNonLeaf
+    <- gof FFI.targetOptionFlagNoFramePointerElimNonLeaf
+  lessPreciseFloatingPointMultiplyAddOption
+    <- gof FFI.targetOptionFlagLessPreciseFPMADOption
+  unsafeFloatingPointMath
+    <- gof FFI.targetOptionFlagUnsafeFPMath
+  noInfinitiesFloatingPointMath
+    <- gof FFI.targetOptionFlagNoInfsFPMath
+  noNaNsFloatingPointMath
+    <- gof FFI.targetOptionFlagNoNaNsFPMath
+  honorSignDependentRoundingFloatingPointMathOption
+    <- gof FFI.targetOptionFlagHonorSignDependentRoundingFPMathOption
+  useSoftFloat
+    <- gof FFI.targetOptionFlagUseSoftFloat
+  noZerosInBSS
+    <- gof FFI.targetOptionFlagNoZerosInBSS
+  jITExceptionHandling
+    <- gof FFI.targetOptionFlagJITExceptionHandling
+  jITEmitDebugInfo
+    <- gof FFI.targetOptionFlagJITEmitDebugInfo
+  jITEmitDebugInfoToDisk
+    <- gof FFI.targetOptionFlagJITEmitDebugInfoToDisk
+  guaranteedTailCallOptimization
+    <- gof FFI.targetOptionFlagGuaranteedTailCallOpt
+  disableTailCalls
+    <- gof FFI.targetOptionFlagDisableTailCalls
+  enableFastInstructionSelection
+    <- gof FFI.targetOptionFlagEnableFastISel
+  positionIndependentExecutable
+    <- gof FFI.targetOptionFlagPositionIndependentExecutable
+  enableSegmentedStacks
+    <- gof FFI.targetOptionFlagEnableSegmentedStacks
+  useInitArray
+    <- gof FFI.targetOptionFlagUseInitArray
+  realignStack
+    <- decodeM =<< FFI.getTargetOptionsFlag tOpts FFI.targetOptionFlagRealignStack
+  stackAlignmentOverride <- decodeM =<< FFI.getStackAlignmentOverride tOpts
+  trapFunctionName <- decodeM =<< FFI.getTrapFuncName tOpts
+  floatABIType <- decodeM =<< FFI.getFloatABIType tOpts
+  allowFloatingPointOperationFusion <- decodeM =<< FFI.getAllowFPOpFusion tOpts
+  stackSmashingProtectionBufferSize <- decodeM =<< FFI.getSSPBufferSize 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
+    -> String -- ^ 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
+  anyContT $ 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 <$> FFI.getTargetLowering tm
diff --git a/src/LLVM/General/Internal/Type.hs b/src/LLVM/General/Internal/Type.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/Type.hs
@@ -0,0 +1,158 @@
+{-# LANGUAGE
+  QuasiQuotes,
+  FlexibleInstances,
+  MultiParamTypeClasses
+  #-}
+module LLVM.General.Internal.Type where
+
+import Control.Applicative
+import Control.Monad.State
+import Control.Monad.AnyCont
+
+import qualified Data.Set as Set
+
+import Foreign.Ptr
+
+import qualified LLVM.General.Internal.FFI.LLVMCTypes as FFI
+import qualified LLVM.General.Internal.FFI.Type as FFI
+
+import qualified LLVM.General.AST as A
+import qualified LLVM.General.AST.AddrSpace as A
+
+import LLVM.General.Internal.Context
+import LLVM.General.Internal.Coding
+import LLVM.General.Internal.DecodeAST
+import LLVM.General.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 _ _ -> fail $ "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
+
+instance DecodeM DecodeAST A.Type (Ptr FFI.Type) where
+  decodeM t = scopeAnyCont $ do
+    k <- liftIO $ FFI.getTypeKind t
+    case k of
+      [FFI.typeKindP|Void|] -> return A.VoidType
+      [FFI.typeKindP|Integer|] -> A.IntegerType <$> (decodeM =<< liftIO (FFI.getIntTypeWidth t))
+      [FFI.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))
+      [FFI.typeKindP|Pointer|] ->
+          return A.PointerType
+             `ap` (decodeM =<< liftIO (FFI.getElementType t))
+             `ap` (decodeM =<< liftIO (FFI.getPointerAddressSpace t))
+      [FFI.typeKindP|Half|] -> return $ A.FloatingPointType 16 A.IEEE
+      [FFI.typeKindP|Float|] -> return $ A.FloatingPointType 32 A.IEEE
+      [FFI.typeKindP|Double|] -> return $ A.FloatingPointType 64 A.IEEE
+      [FFI.typeKindP|FP128|] -> return $ A.FloatingPointType 128 A.IEEE
+      [FFI.typeKindP|X86_FP80|] -> return $ A.FloatingPointType 80 A.DoubleExtended
+      [FFI.typeKindP|PPC_FP128|] -> return $ A.FloatingPointType 128 A.PairOfFloats
+      [FFI.typeKindP|Vector|] -> 
+        return A.VectorType
+         `ap` (decodeM =<< liftIO (FFI.getVectorSize t))
+         `ap` (decodeM =<< liftIO (FFI.getElementType t))
+      [FFI.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)
+
+      [FFI.typeKindP|Array|] -> 
+        return A.ArrayType
+         `ap` (decodeM =<< liftIO (FFI.getArrayLength t))
+         `ap` (decodeM =<< liftIO (FFI.getElementType t))
+      _ -> 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/General/Internal/Value.hs b/src/LLVM/General/Internal/Value.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Internal/Value.hs
@@ -0,0 +1,24 @@
+{-# LANGUAGE
+  FlexibleContexts,
+  FlexibleInstances,
+  MultiParamTypeClasses
+  #-}
+module LLVM.General.Internal.Value where
+
+import Control.Monad.State
+
+import Foreign.Ptr
+
+import qualified LLVM.General.Internal.FFI.PtrHierarchy as FFI
+import qualified LLVM.General.Internal.FFI.Value as FFI
+
+import LLVM.General.Internal.Coding
+import LLVM.General.Internal.DecodeAST
+import LLVM.General.Internal.Type () 
+import LLVM.General.Internal.Constant () 
+
+import qualified LLVM.General.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/General/Module.hs b/src/LLVM/General/Module.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Module.hs
@@ -0,0 +1,11 @@
+-- | 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.General.ExecutionEngine' and so JIT compiled to get funciton pointers.
+module LLVM.General.Module (
+    Module,
+    withModuleFromAST,
+    moduleAST,
+    withModuleFromString,
+    moduleString
+  ) where
+
+import LLVM.General.Internal.Module
diff --git a/src/LLVM/General/PassManager.hs b/src/LLVM/General/PassManager.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/PassManager.hs
@@ -0,0 +1,19 @@
+-- | A 'PassManager' holds collection of passes, to be run on 'Module's.
+-- Build one with 'createPassManager':
+-- 
+--  * from a 'CuratedPassSetSpec' if you want optimization but not to play with your compiler
+--
+--  * from a ['LLVM.General.Transform.Pass'] if you do want to play with your compiler
+--
+--  * from a (['LLVM.General.Transform.Pass'], 'LLVM.General.Target.TargetLowering') if you
+--    want to provide target-specific information (e.g. instruction costs) to the few passes
+--    that use it (see comments on 'LLVM.General.Transforms.Pass').
+module LLVM.General.PassManager (
+  PassManager,
+  PassManagerSpecification,
+  CuratedPassSetSpec(..), defaultCuratedPassSetSpec,
+  withPassManager,
+  runPassManager
+  ) where
+
+import LLVM.General.Internal.PassManager
diff --git a/src/LLVM/General/Relocation.hs b/src/LLVM/General/Relocation.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Relocation.hs
@@ -0,0 +1,15 @@
+{-# LANGUAGE
+  DeriveDataTypeable
+  #-}
+-- | Relocations, used in specifying TargetMachine
+module LLVM.General.Relocation where
+
+import Data.Data
+
+-- | <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/General/Target.hs b/src/LLVM/General/Target.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Target.hs
@@ -0,0 +1,13 @@
+-- | 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.General.Target (
+   lookupTarget,
+   TargetOptions,
+   withTargetOptions, peekTargetOptions, pokeTargetOptions,
+   withTargetMachine,
+   getTargetLowering
+ ) where
+
+import LLVM.General.Internal.Target
+
diff --git a/src/LLVM/General/Target/Options.hs b/src/LLVM/General/Target/Options.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Target/Options.hs
@@ -0,0 +1,54 @@
+{-# LANGUAGE
+  DeriveDataTypeable 
+  #-}
+-- | <http://llvm.org/doxygen/classllvm_1_1TargetOptions.html>
+module LLVM.General.Target.Options where
+
+import Data.Data
+import Data.Word
+
+-- | <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.General.Target.TargetOptions'
+-- <http://llvm.org/doxygen/classllvm_1_1TargetOptions.html>
+data Options = Options {
+  printMachineCode :: Bool,
+  noFramePointerElimination :: Bool,
+  noFramePointerEliminationNonLeaf :: Bool,
+  lessPreciseFloatingPointMultiplyAddOption :: Bool,
+  unsafeFloatingPointMath :: Bool,
+  noInfinitiesFloatingPointMath :: Bool,
+  noNaNsFloatingPointMath :: Bool,
+  honorSignDependentRoundingFloatingPointMathOption :: Bool,
+  useSoftFloat :: Bool,
+  noZerosInBSS :: Bool,
+  jITExceptionHandling :: Bool,
+  jITEmitDebugInfo :: Bool,
+  jITEmitDebugInfoToDisk :: Bool,
+  guaranteedTailCallOptimization :: Bool,
+  disableTailCalls :: Bool,
+  realignStack :: Bool,
+  enableFastInstructionSelection :: Bool,
+  positionIndependentExecutable :: Bool,
+  enableSegmentedStacks :: Bool,
+  useInitArray :: Bool,
+  stackAlignmentOverride :: Word32,
+  trapFunctionName :: String,
+  floatABIType :: FloatABI,
+  allowFloatingPointOperationFusion :: FloatingPointOperationFusionMode,
+  stackSmashingProtectionBufferSize :: Word32
+  }
+  deriving (Eq, Ord, Read, Show)
+
diff --git a/src/LLVM/General/Transforms.hs b/src/LLVM/General/Transforms.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/General/Transforms.hs
@@ -0,0 +1,143 @@
+{-# LANGUAGE
+  DeriveDataTypeable
+  #-}
+-- | This module provides an enumeration of the various transformation (e.g. optimization) passes
+-- provided by LLVM. They can be used to create a 'LLVM.General.PassManager.PassManager' to, in turn,
+-- run the passes on 'LLVM.General.Module.Module's. If you don't know what passes you want, consider
+-- instead using 'LLVM.General.PassManager.CuratedPassSetSpec'.
+module LLVM.General.Transforms where
+
+import Data.Data
+import Data.Int
+import Data.Word
+
+-- | <http://llvm.org/docs/Passes.html#transform-passes>
+-- A few passes can make use of information in a 'LLVM.General.Target.TargetLowering' if one
+-- is provided to 'LLVM.General.PassManager.createPassManager'.
+-- <http://llvm.org/doxygen/classllvm_1_1Pass.html>
+data Pass
+  -- here begin the Scalar passes
+  = AggressiveDeadCodeElimination
+  | BlockPlacement
+  | BreakCriticalEdges
+  -- | can use a 'LLVM.General.Target.TargetLowering'
+  | CodeGenPrepare
+  | ConstantPropagation
+  | CorrelatedValuePropagation
+  | DeadCodeElimination
+  | DeadInstructionElimination
+  | DeadStoreElimination
+  | DemoteRegisterToMemory
+  | EarlyCommonSubexpressionElimination
+  | GlobalValueNumbering { noLoads :: Bool }
+  | InductionVariableSimplify
+  | InstructionCombining
+  | JumpThreading
+  | LoopClosedSingleStaticAssignment
+  | LoopInvariantCodeMotion
+  | LoopDeletion
+  | LoopIdiom
+  | LoopInstructionSimplify
+  | LoopRotate
+  -- | can use a 'LLVM.General.Target.TargetLowering'
+  | LoopStrengthReduce
+  | LoopUnroll { loopUnrollThreshold :: Int32, count :: Int32, allowPartial :: Int32 }
+  | LoopUnswitch { optimizeForSize :: Bool }
+  | LowerAtomic
+  -- | can use a 'LLVM.General.Target.TargetLowering'
+  | LowerInvoke { useExpensiveExceptionHandlingSupport :: Bool } 
+  | LowerSwitch
+  | LowerExpectIntrinsic
+  | MemcpyOptimization
+  | PromoteMemoryToRegister
+  | Reassociate
+  | ScalarReplacementOfAggregates { requiresDominatorTree :: Bool }
+  | OldScalarReplacementOfAggregates { 
+      oldScalarReplacementOfAggregatesThreshold :: Int32, 
+      useDominatorTree :: Bool, 
+      structMemberThreshold :: Int32,
+      arrayElementThreshold :: Int32,
+      scalarLoadThreshold :: Int32
+    }
+  | SparseConditionalConstantPropagation
+  | SimplifyLibCalls
+  | SimplifyControlFlowGraph
+  | Sinking
+  | TailCallElimination
+
+  -- here begin the Interprocedural passes
+  | AlwaysInline { insertLifetime :: Bool }
+  | ArgumentPromotion
+  | ConstantMerge
+  | FunctionAttributes
+  | FunctionInlining { 
+      functionInliningThreshold :: Int32
+    }
+  | GlobalDeadCodeElimination
+  | InternalizeFunctions { exportList :: [String] }
+  | InterproceduralConstantPropagation
+  | InterproceduralSparseConditionalConstantPropagation
+  | MergeFunctions
+  | PartialInlining
+  | PruneExceptionHandling
+  | StripDeadDebugInfo
+  | StripDebugDeclare
+  | StripNonDebugSymbols
+  | StripSymbols { onlyDebugInfo :: Bool }
+
+  -- here begin the vectorization passes
+  | BasicBlockVectorize { 
+    vectorBits :: Word32,
+    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 :: Word32,
+    searchLimit :: Word32,
+    maxCandidatePairsForCycleCheck :: Word32,
+    splatBreaksChain :: Bool,
+    maxInstructions :: Word32,
+    maxIterations :: Word32,
+    powerOfTwoLengthsOnly :: Bool,
+    noMemoryOperationBoost :: Bool,
+    fastDependencyAnalysis :: Bool
+    }
+  | LoopVectorize
+  deriving (Eq, Ord, Read, Show, Typeable, Data)
+
+-- | 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 = 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
+  }
diff --git a/test/LLVM/General/Test/Constants.hs b/test/LLVM/General/Test/Constants.hs
new file mode 100644
--- /dev/null
+++ b/test/LLVM/General/Test/Constants.hs
@@ -0,0 +1,170 @@
+module LLVM.General.Test.Constants where
+
+import Test.Framework
+import Test.Framework.Providers.HUnit
+import Test.HUnit
+
+import LLVM.General.Test.Support
+
+import Control.Monad
+import Data.Functor
+import Data.Maybe
+import Foreign.Ptr
+import Data.Word
+
+import LLVM.General.Context
+import LLVM.General.Module
+import LLVM.General.Diagnostic
+import LLVM.General.AST
+import LLVM.General.AST.Type
+import LLVM.General.AST.Name
+import LLVM.General.AST.AddrSpace
+import qualified LLVM.General.AST.Linkage as L
+import qualified LLVM.General.AST.Visibility as V
+import qualified LLVM.General.AST.CallingConvention as CC
+import qualified LLVM.General.AST.Attribute as A
+import qualified LLVM.General.AST.Global as G
+import qualified LLVM.General.AST.Constant as C
+import qualified LLVM.General.AST.Float as F
+import qualified LLVM.General.AST.IntegerPredicate as IPred
+
+tests = testGroup "Constants" [
+  testCase name $ strCheck mAST mStr
+  | (name, type', value, str) <- [
+    (
+      "integer",
+      IntegerType 32,
+      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",
+      FloatingPointType 16 IEEE,
+      C.Float (F.Half 0x1234),
+      "global half 0xH1234"
+    ), (
+      "float",
+      FloatingPointType 32 IEEE,
+      C.Float (F.Single 1),
+      "global float 1.000000e+00"
+    ), (
+      "double",
+      FloatingPointType 64 IEEE,
+      C.Float (F.Double 1),
+      "global double 1.000000e+00"
+    ), (
+      "quad",
+      FloatingPointType 128 IEEE,
+      C.Float (F.Quadruple 0x0007000600050004 0x0003000200010000),
+      "global fp128 0xL00030002000100000007000600050004" -- yes, this order is weird
+    ), (
+      "quad 1.0",
+      FloatingPointType 128 IEEE,
+      C.Float (F.Quadruple 0x3fff000000000000 0x0000000000000000),
+      "global fp128 0xL00000000000000003FFF000000000000" -- yes, this order is weird
+    ), (
+      "x86_fp80",
+      FloatingPointType 80 DoubleExtended,
+      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",
+      FloatingPointType 128 PairOfFloats,
+      C.Float (F.PPC_FP128 0x0007000600050004 0x0003000200010000),
+      "global ppc_fp128 0xM????????????????"
+-}
+    ), (
+      "struct",
+      StructureType False (replicate 2 (IntegerType 32)),
+      C.Struct False (replicate 2 (C.Int 32 1)),
+      "global { i32, i32 } { i32 1, i32 1 }"
+    ), (
+      "dataarray",
+      ArrayType 3 (IntegerType 32),
+      C.Array (IntegerType 32) [C.Int 32 i | i <- [1,2,1]],
+      "global [3 x i32] [i32 1, i32 2, i32 1]"
+    ), (
+      "array",
+      ArrayType 3 (StructureType False [IntegerType 32]),
+      C.Array (StructureType False [IntegerType 32]) [C.Struct 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 (IntegerType 32),
+      C.Vector [C.Int 32 i | i <- [1,2,1]],
+      "global <3 x i32> <i32 1, i32 2, i32 1>"
+    ), (
+      "binop/cast",
+      IntegerType 64,
+      C.Add (C.PtrToInt (C.GlobalReference (UnName 1)) (IntegerType 64)) (C.Int 64 2),
+      "global i64 add (i64 ptrtoint (i32* @1 to i64), i64 2)"
+    ), (
+      "icmp",
+      IntegerType 1,
+      C.ICmp IPred.SGE (C.GlobalReference (UnName 1)) (C.GlobalReference (UnName 2)),
+      "global i1 icmp sge (i32* @1, i32* @2)"
+    ), (
+      "getelementptr",
+      PointerType (IntegerType 32) (AddrSpace 0),
+      C.GetElementPtr True (C.GlobalReference (UnName 1)) [C.Int 64 27],
+      "global i32* getelementptr inbounds (i32* @1, i64 27)"
+    ), (
+      "selectvalue",
+      IntegerType 32,
+      C.Select (C.PtrToInt (C.GlobalReference (UnName 1)) (IntegerType 1)) 
+         (C.Int 32 1)
+         (C.Int 32 2),
+      "global i32 select (i1 ptrtoint (i32* @1 to i1), i32 1, i32 2)"
+    ), (
+      "extractelement",
+      IntegerType 32,
+      C.ExtractElement
+         (C.BitCast
+             (C.PtrToInt (C.GlobalReference (UnName 1)) (IntegerType 64))
+             (VectorType 2 (IntegerType 32)))
+         (C.Int 32 1),
+      "global i32 extractelement (<2 x i32> bitcast (i64 ptrtoint (i32* @1 to i64) to <2 x i32>), i32 1)"
+{-
+    ), (
+--  This test make llvm abort, as of llvm-3.2.
+      "extractvalue",
+      IntegerType 8,
+      C.ExtractValue
+        (C.Select (C.PtrToInt (C.GlobalReference (UnName 1)) (IntegerType 1)) 
+         (C.Array (IntegerType 8) [C.Int 8 0])
+         (C.Array (IntegerType 8) [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>" Nothing Nothing [
+             GlobalDefinition $ globalVariableDefaults {
+               G.name = UnName 0, G.type' = type', G.initializer = Just value 
+             },
+             GlobalDefinition $ globalVariableDefaults {
+               G.name = UnName 1, G.type' = IntegerType 32, G.initializer = Nothing 
+             },
+             GlobalDefinition $ globalVariableDefaults {
+               G.name = UnName 2, G.type' = IntegerType 32, G.initializer = Nothing 
+             }
+           ]
+       mStr = "; ModuleID = '<string>'\n\n@0 = " ++ str ++ "\n\
+              \@1 = external global i32\n\
+              \@2 = external global i32\n"
+ ]
diff --git a/test/LLVM/General/Test/DataLayout.hs b/test/LLVM/General/Test/DataLayout.hs
new file mode 100644
--- /dev/null
+++ b/test/LLVM/General/Test/DataLayout.hs
@@ -0,0 +1,29 @@
+module LLVM.General.Test.DataLayout where
+
+import Test.Framework
+import Test.Framework.Providers.HUnit
+import Test.HUnit
+
+import LLVM.General.Test.Support
+
+import qualified Data.Set as Set
+
+import LLVM.General.Context
+import LLVM.General.Module
+import LLVM.General.AST
+import LLVM.General.AST.DataLayout
+import qualified LLVM.General.AST.Global as G
+
+tests = testGroup "DataLayout" [
+  testCase name $ strCheck (Module "<string>" mdl Nothing []) ("; ModuleID = '<string>'\n" ++ sdl)
+  | (name, mdl, sdl) <- [
+   ("none",Nothing, "")
+  ] ++ [
+   (name, Just mdl, "target datalayout = \"" ++ sdl ++ "\"\n")
+   | (name, mdl, sdl) <- [
+    ("little-endian", defaultDataLayout { endianness = Just LittleEndian }, "e"),
+    ("big-endian", defaultDataLayout { endianness = Just BigEndian }, "E"),
+    ("native", defaultDataLayout { nativeSizes = Just (Set.fromList [8,32]) }, "n8:32")
+   ]
+  ]
+ ]
diff --git a/test/LLVM/General/Test/ExecutionEngine.hs b/test/LLVM/General/Test/ExecutionEngine.hs
new file mode 100644
--- /dev/null
+++ b/test/LLVM/General/Test/ExecutionEngine.hs
@@ -0,0 +1,52 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+module LLVM.General.Test.ExecutionEngine where
+
+import Test.Framework
+import Test.Framework.Providers.HUnit
+import Test.HUnit
+
+import Control.Monad
+import Data.Functor
+import Data.Maybe
+
+import Foreign.Ptr
+import Data.Word
+
+import LLVM.General.Context
+import LLVM.General.Module
+import LLVM.General.ExecutionEngine
+import LLVM.General.AST
+import LLVM.General.AST.Type
+import LLVM.General.AST.Name
+import LLVM.General.AST.AddrSpace
+import qualified LLVM.General.AST.IntegerPredicate as IPred
+
+import qualified LLVM.General.AST.Linkage as L
+import qualified LLVM.General.AST.Visibility as V
+import qualified LLVM.General.AST.CallingConvention as CC
+import qualified LLVM.General.AST.Attribute as A
+import qualified LLVM.General.AST.Global as G
+import qualified LLVM.General.AST.Constant as C
+
+foreign import ccall "dynamic" mkIO32Stub :: FunPtr (Word32 -> IO Word32) -> (Word32 -> IO Word32)
+
+tests = testGroup "ExecutionEngine" [
+
+  testCase "runSomething" $ withContext $ \context -> withExecutionEngine context $ \executionEngine -> do
+    let mAST = Module "runSomethingModule" Nothing Nothing [
+                GlobalDefinition $ Function L.External V.Default CC.C [] (IntegerType 32) (Name "foo") ([
+                              Parameter (IntegerType 32) (Name "foo") []
+                             ],False) [] 
+                 Nothing 0
+                 [
+                  BasicBlock (UnName 0) [] (
+                    Do $ Ret (Just (ConstantOperand (C.Int 32 42))) []
+                   )
+                 ]
+                ]
+    s <- withModuleFromAST context mAST $ \m -> do
+          withModuleInEngine executionEngine m $ do
+            Just p <- findFunction executionEngine (Name "foo")
+            (mkIO32Stub ((castPtrToFunPtr p) :: FunPtr (Word32 -> IO Word32))) 7
+    s @?= Right 42
+ ]
diff --git a/test/LLVM/General/Test/Global.hs b/test/LLVM/General/Test/Global.hs
new file mode 100644
--- /dev/null
+++ b/test/LLVM/General/Test/Global.hs
@@ -0,0 +1,40 @@
+module LLVM.General.Test.Global where
+
+import Test.Framework
+import Test.Framework.Providers.HUnit
+import Test.HUnit
+
+import LLVM.General.Context
+import LLVM.General.Module
+import LLVM.General.AST
+import qualified LLVM.General.AST.Global as G
+
+tests = testGroup "Global" [
+  testGroup "Alignment" [
+    testCase name $ withContext $ \context -> do
+      let ast = Module "<string>" Nothing Nothing [ GlobalDefinition g ]
+      Right ast' <- withModuleFromAST context ast moduleAST
+      ast' @?= ast
+    | a <- [0,1],
+      s <- [Nothing, Just "foo"],
+      g <- [
+       globalVariableDefaults {
+        G.name = UnName 0,
+        G.type' = IntegerType 32,
+        G.alignment = a,
+        G.section = s
+        },
+       functionDefaults {
+         G.returnType = VoidType,
+         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/General/Test/InlineAssembly.hs b/test/LLVM/General/Test/InlineAssembly.hs
new file mode 100644
--- /dev/null
+++ b/test/LLVM/General/Test/InlineAssembly.hs
@@ -0,0 +1,76 @@
+module LLVM.General.Test.InlineAssembly where
+
+import Test.Framework
+import Test.Framework.Providers.HUnit
+import Test.HUnit
+
+import LLVM.General.Test.Support
+
+import LLVM.General.Context
+import LLVM.General.Module
+
+import LLVM.General.AST
+import LLVM.General.AST.InlineAssembly as IA
+import qualified LLVM.General.AST.Linkage as L
+import qualified LLVM.General.AST.Visibility as V
+import qualified LLVM.General.AST.CallingConvention as CC
+import qualified LLVM.General.AST.Constant as C
+import qualified LLVM.General.AST.Global as G
+
+tests = testGroup "InlineAssembly" [
+  testCase "expression" $ do
+    let ast = Module "<string>" Nothing Nothing [
+                GlobalDefinition $ 
+                  Function L.External V.Default CC.C [] (IntegerType 32) (Name "foo")
+                    ([Parameter (IntegerType 32) (Name "x") []],False)
+                    [] Nothing 0 [
+                      BasicBlock (UnName 0) [
+                        UnName 1 := Call {
+                          isTailCall = False,
+                          callingConvention = CC.C,
+                          returnAttributes = [],
+                          function = Left $ InlineAssembly {
+                                       IA.type' = FunctionType (IntegerType 32) [IntegerType 32] False,
+                                       assembly = "bswap $0",
+                                       constraints = "=r,r",
+                                       hasSideEffects = False,
+                                       alignStack = False,
+                                       dialect = ATTDialect
+                                     },
+                          arguments = [
+                            (LocalReference (Name "x"), [])
+                           ],
+                          functionAttributes = [],
+                          metadata = []
+                        }
+                      ] (
+                        Do $ Ret (Just (LocalReference (UnName 1))) []
+                      )
+                    ]
+
+              ]
+        s = "; ModuleID = '<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>" Nothing Nothing [
+                ModuleInlineAssembly "foo",
+                ModuleInlineAssembly "bar",
+                GlobalDefinition $ globalVariableDefaults {
+                  G.name = UnName 0,
+                  G.type' = IntegerType 32
+                }
+              ]
+        s = "; ModuleID = '<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/General/Test/Instructions.hs b/test/LLVM/General/Test/Instructions.hs
new file mode 100644
--- /dev/null
+++ b/test/LLVM/General/Test/Instructions.hs
@@ -0,0 +1,777 @@
+module LLVM.General.Test.Instructions where
+
+import Test.Framework
+import Test.Framework.Providers.HUnit
+import Test.HUnit
+
+import LLVM.General.Test.Support
+
+import Control.Monad
+import Data.Functor
+import Data.Maybe
+import Foreign.Ptr
+import Data.Word
+
+import LLVM.General.Context
+import LLVM.General.Module
+import LLVM.General.Diagnostic
+import LLVM.General.AST
+import LLVM.General.AST.Type
+import LLVM.General.AST.Name
+import LLVM.General.AST.AddrSpace
+import qualified LLVM.General.AST.IntegerPredicate as IPred
+import qualified LLVM.General.AST.FloatingPointPredicate as FPPred
+import qualified LLVM.General.AST.Linkage as L
+import qualified LLVM.General.AST.Visibility as V
+import qualified LLVM.General.AST.CallingConvention as CC
+import qualified LLVM.General.AST.Attribute as A
+import qualified LLVM.General.AST.Global as G
+import qualified LLVM.General.AST.Constant as C
+import qualified LLVM.General.AST.RMWOperation as RMWOp
+
+tests = testGroup "Instructions" [
+  testGroup "regular" [
+    testCase name $ do
+      let mAST = Module "<string>" Nothing Nothing [
+            GlobalDefinition $ Function L.External V.Default CC.C [] (VoidType) (UnName 0) ([
+                  Parameter (IntegerType 32) (UnName 0) [],
+                  Parameter (FloatingPointType 32 IEEE) (UnName 1) [],
+                  Parameter (PointerType (IntegerType 32) (AddrSpace 0)) (UnName 2) [],
+                  Parameter (IntegerType 64) (UnName 3) [],
+                  Parameter (IntegerType 1) (UnName 4) [],
+                  Parameter (VectorType 2 (IntegerType 32)) (UnName 5) [],
+                  Parameter (StructureType False [IntegerType 32, IntegerType 32]) (UnName 6) []
+                 ],False) [] Nothing 0 [
+              BasicBlock (UnName 7) [
+                namedInstr
+               ] (
+                Do $ Ret Nothing []
+               )
+             ]
+            ]
+          mStr = "; ModuleID = '<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 a = LocalReference . UnName,
+      (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 {
+             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 {
+             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 {
+             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 {
+             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 {
+             operand0 = a 1,
+             operand1 = a 1,
+             metadata = [] 
+           },
+           "frem 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 = IntegerType 32,
+             numElements = Nothing,
+             alignment = 0,
+             metadata = [] 
+           },
+           "alloca i32"),
+          ("load",
+           Load {
+             volatile = False,
+             address = a 2,
+             maybeAtomicity = Nothing,
+             alignment = 0,
+             metadata = [] 
+           },
+           "load i32* %2"),
+          ("volatile",
+           Load {
+             volatile = True,
+             address = a 2,
+             maybeAtomicity = Nothing,
+             alignment = 0,
+             metadata = [] 
+           },
+           "load volatile i32* %2"),
+          ("acquire",
+           Load {
+             volatile = False,
+             address = a 2,
+             maybeAtomicity = Just (Atomicity { crossThread = True, memoryOrdering = Acquire }),
+             alignment = 1,
+             metadata = [] 
+           },
+           "load atomic i32* %2 acquire, align 1"),
+          ("singlethread",
+           Load {
+             volatile = False,
+             address = a 2,
+             maybeAtomicity = Just (Atomicity { crossThread = False, memoryOrdering = Monotonic }),
+             alignment = 1,
+             metadata = [] 
+           },
+           "load atomic i32* %2 singlethread monotonic, align 1"),
+          ("GEP",
+           GetElementPtr {
+             inBounds = False,
+             address = a 2,
+             indices = [ a 0 ],
+             metadata = [] 
+           },
+           "getelementptr i32* %2, i32 %0"),
+          ("inBounds",
+           GetElementPtr {
+             inBounds = True,
+             address = a 2,
+             indices = [ a 0 ],
+             metadata = [] 
+           },
+           "getelementptr inbounds i32* %2, i32 %0"),
+          ("cmpxchg",
+           CmpXchg {
+             volatile = False,
+             address = a 2,
+             expected = a 0,
+             replacement = a 0,
+             atomicity = Atomicity { crossThread = True, memoryOrdering = Monotonic },
+             metadata = [] 
+           },
+           "cmpxchg i32* %2, i32 %0, i32 %0 monotonic"),
+          ("atomicrmw",
+           AtomicRMW {
+             volatile = False,
+             rmwOperation = RMWOp.UMax,
+             address = a 2,
+             value = a 0,
+             atomicity = Atomicity { crossThread = True, memoryOrdering = Release },
+             metadata = []
+           },
+           "atomicrmw umax i32* %2, i32 %0 release"),
+
+          ("trunc",
+           Trunc {
+             operand0 = a 0,
+             type' = IntegerType 16,
+             metadata = [] 
+           },
+           "trunc i32 %0 to i16"),
+          ("zext",
+           ZExt {
+             operand0 = a 0,
+             type' = IntegerType 64,
+             metadata = [] 
+           },
+           "zext i32 %0 to i64"),
+          ("sext",
+           SExt {
+             operand0 = a 0,
+             type' = IntegerType 64,
+             metadata = [] 
+           },
+           "sext i32 %0 to i64"),
+          ("fptoui",
+           FPToUI {
+             operand0 = a 1,
+             type' = IntegerType 64,
+             metadata = [] 
+           },
+           "fptoui float %1 to i64"),
+          ("fptosi",
+           FPToSI {
+             operand0 = a 1,
+             type' = IntegerType 64,
+             metadata = [] 
+           },
+           "fptosi float %1 to i64"),
+          ("uitofp",
+           UIToFP {
+             operand0 = a 0,
+             type' = FloatingPointType 32 IEEE,
+             metadata = [] 
+           },
+           "uitofp i32 %0 to float"),
+          ("sitofp",
+           SIToFP {
+             operand0 = a 0,
+             type' = FloatingPointType 32 IEEE,
+             metadata = [] 
+           },
+           "sitofp i32 %0 to float"),
+          ("fptrunc",
+           FPTrunc {
+             operand0 = a 1,
+             type' = FloatingPointType 16 IEEE,
+             metadata = [] 
+           },
+           "fptrunc float %1 to half"),
+          ("fpext",
+           FPExt {
+             operand0 = a 1,
+             type' = FloatingPointType 64 IEEE,
+             metadata = [] 
+           },
+           "fpext float %1 to double"),
+          ("ptrtoint",
+           PtrToInt {
+             operand0 = a 2,
+             type' = IntegerType 32,
+             metadata = [] 
+           },
+           "ptrtoint i32* %2 to i32"),
+          ("inttoptr",
+           IntToPtr {
+             operand0 = a 0,
+             type' = PointerType (IntegerType 32) (AddrSpace 0),
+             metadata = [] 
+           },
+           "inttoptr i32 %0 to i32*"),
+          ("bitcast",
+           BitCast {
+             operand0 = a 0,
+             type' = FloatingPointType 32 IEEE,
+             metadata = [] 
+           },
+           "bitcast i32 %0 to float"),
+          ("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' = IntegerType 16,
+             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 [ 
+                PointerType (IntegerType 8) (AddrSpace 0),
+                IntegerType 32
+               ],
+             personalityFunction = ConstantOperand (C.GlobalReference (UnName 0)),
+             cleanup = cp,
+             clauses = cls,
+             metadata = []
+           },
+           "landingpad { i8*, i32 } personality void (i32, float, i32*, i64, i1, <2 x i32>, { i32, i32 })* @0" ++ s)
+          | (clsn,cls,clss) <- [
+           ("catch",
+            [Catch (C.Null (PointerType (IntegerType 8) (AddrSpace 0)))],
+            "\n          catch i8* null"),
+           ("filter",
+            [Filter (C.Null (ArrayType 1 (PointerType (IntegerType 8) (AddrSpace 0))))],
+            "\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 0,
+            value = a 2,
+            maybeAtomicity = Nothing,
+            alignment = 0,
+            metadata = [] 
+          },
+          "store i32 %0, i32* %2"),
+         ("fence",
+          Do $ Fence {
+            atomicity = Atomicity { crossThread = True, memoryOrdering = Acquire },
+            metadata = [] 
+          },
+          "fence acquire"),
+          ("call",
+           Do $ Call {
+             isTailCall = False,
+             callingConvention = CC.C,
+             returnAttributes = [],
+             function = Right (ConstantOperand (C.GlobalReference (UnName 0))),
+             arguments = [ (LocalReference (UnName 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)")
+        ]
+      )
+   ],
+  testGroup "terminators" [
+    testCase name $ strCheck mAST mStr
+    | (name, mAST, mStr) <- [
+     (
+       "ret",
+       Module "<string>" Nothing Nothing [
+        GlobalDefinition $ Function L.External V.Default CC.C [] (VoidType) (UnName 0) ([
+             ],False) [] Nothing 0
+         [
+          BasicBlock (UnName 0) [
+           ] (
+            Do $ Ret Nothing []
+           )
+         ]
+        ],
+       "; ModuleID = '<string>'\n\
+       \\n\
+       \define void @0() {\n\
+       \  ret void\n\
+       \}\n"
+     ), (
+       "br",
+       Module "<string>" Nothing Nothing [
+        GlobalDefinition $ Function L.External V.Default CC.C [] (VoidType) (UnName 0) ([
+             ],False) [] Nothing 0
+         [
+          BasicBlock (UnName 0) [] (
+            Do $ Br (Name "foo") []
+           ),
+          BasicBlock (Name "foo") [] (
+            Do $ Ret Nothing []
+           )
+         ]
+        ],
+       "; ModuleID = '<string>'\n\
+       \\n\
+       \define void @0() {\n\
+       \  br label %foo\n\
+       \\n\
+       \foo:                                              ; preds = %0\n\
+       \  ret void\n\
+       \}\n"
+     ), (
+       "condbr",
+       Module "<string>" Nothing Nothing [
+        GlobalDefinition $ Function L.External V.Default CC.C [] (VoidType) (UnName 0) ([
+             ],False) [] Nothing 0
+         [
+          BasicBlock (Name "bar") [] (
+            Do $ CondBr (ConstantOperand (C.Int 1 1)) (Name "foo") (Name "bar") []
+           ),
+          BasicBlock (Name "foo") [] (
+            Do $ Ret Nothing []
+           )
+         ]
+        ],
+       "; ModuleID = '<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>" Nothing Nothing [
+        GlobalDefinition $ Function L.External V.Default CC.C [] (VoidType) (UnName 0) ([
+             ],False) [] Nothing 0
+         [
+          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\
+       \\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>" Nothing Nothing [
+        GlobalDefinition $ globalVariableDefaults {
+          G.name = UnName 0,
+          G.type' = PointerType (IntegerType 8) (AddrSpace 0),
+          G.initializer = Just (C.BlockAddress (Name "foo") (UnName 2))
+        },
+        GlobalDefinition $ Function L.External V.Default CC.C [] (VoidType) (Name "foo") ([
+             ],False) [] Nothing 0
+         [
+          BasicBlock (UnName 0) [
+            UnName 1 := Load {
+                     volatile = False,
+                     address = ConstantOperand (C.GlobalReference (UnName 0)),
+                     maybeAtomicity = Nothing,
+                     alignment = 0,
+                     metadata = [] 
+                   }
+          ] (
+            Do $ IndirectBr {
+              operand0' = LocalReference (UnName 1),
+              possibleDests = [UnName 2],
+              metadata' = []
+           }
+          ),
+          BasicBlock (UnName 2) [] (
+            Do $ Ret Nothing []
+           )
+         ]
+        ],
+--       \  indirectbr i8* null, [label %foo]\n\
+       "; ModuleID = '<string>'\n\
+       \\n\
+       \@0 = global i8* blockaddress(@foo, %2)\n\
+       \\n\
+       \define void @foo() {\n\
+       \  %1 = load i8** @0\n\
+       \  indirectbr i8* %1, [label %2]\n\
+       \\n\
+       \; <label>:2                                       ; preds = %0\n\
+       \  ret void\n\
+       \}\n"
+     ), (
+       "invoke",
+       Module "<string>" Nothing Nothing [
+        GlobalDefinition $ Function L.External V.Default CC.C [] (VoidType) (UnName 0) ([
+                  Parameter (IntegerType 32) (UnName 0) [],
+                  Parameter (IntegerType 16) (UnName 1) []
+             ],False) [] Nothing 0
+         [
+          BasicBlock (UnName 2) [] (
+            Do $ Invoke {
+             callingConvention' = CC.C,
+             returnAttributes' = [],
+             function' = Right (ConstantOperand (C.GlobalReference (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 [ 
+                PointerType (IntegerType 8) (AddrSpace 0),
+                IntegerType 32
+               ],
+             personalityFunction = ConstantOperand (C.GlobalReference (UnName 0)),
+             cleanup = True,
+             clauses = [Catch (C.Null (PointerType (IntegerType 8) (AddrSpace 0)))],
+             metadata = []
+           }
+           ] (
+            Do $ Ret Nothing []
+           )
+         ]
+        ],
+       "; ModuleID = '<string>'\n\
+       \\n\
+       \define void @0(i32, i16) {\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 } personality void (i32, i16)* @0\n\
+       \          cleanup\n\
+       \          catch i8* null\n\
+       \  ret void\n\
+       \}\n"
+     ), (
+       "resume",
+       Module "<string>" Nothing Nothing [
+        GlobalDefinition $ Function L.External V.Default CC.C [] (VoidType) (UnName 0) ([
+             ],False) [] Nothing 0
+         [
+          BasicBlock (UnName 0) [] (
+            Do $ Resume (ConstantOperand (C.Int 32 1)) []
+           )
+         ]
+        ],
+       "; ModuleID = '<string>'\n\
+       \\n\
+       \define void @0() {\n\
+       \  resume i32 1\n\
+       \}\n"
+     ), (
+       "unreachable",
+       Module "<string>" Nothing Nothing [
+        GlobalDefinition $ Function L.External V.Default CC.C [] (VoidType) (UnName 0) ([
+             ],False) [] Nothing 0
+         [
+          BasicBlock (UnName 0) [] (
+            Do $ Unreachable []
+           )
+         ]
+        ],
+       "; ModuleID = '<string>'\n\
+       \\n\
+       \define void @0() {\n\
+       \  unreachable\n\
+       \}\n"
+     )
+    ]
+   ]
+ ]
diff --git a/test/LLVM/General/Test/Metadata.hs b/test/LLVM/General/Test/Metadata.hs
new file mode 100644
--- /dev/null
+++ b/test/LLVM/General/Test/Metadata.hs
@@ -0,0 +1,138 @@
+module LLVM.General.Test.Metadata where
+
+import Test.Framework
+import Test.Framework.Providers.HUnit
+import Test.HUnit
+
+import LLVM.General.Test.Support
+
+import LLVM.General.AST as A
+import qualified LLVM.General.AST.Linkage as L
+import qualified LLVM.General.AST.Visibility as V
+import qualified LLVM.General.AST.CallingConvention as CC
+import qualified LLVM.General.AST.Constant as C
+import LLVM.General.AST.Global as G
+
+tests = testGroup "Metadata" [
+  testCase "local" $ do
+    let ast = Module "<string>" Nothing Nothing [
+         GlobalDefinition $ globalVariableDefaults { G.name = UnName 0, G.type' = IntegerType 32 },
+         GlobalDefinition $ Function L.External V.Default CC.C [] (IntegerType 32) (Name "foo") ([
+            ],False)
+          [] 
+          Nothing 0         
+          [
+           BasicBlock (UnName 0) [
+              UnName 1 := Load {
+                         volatile = False,
+                         address = ConstantOperand (C.GlobalReference (UnName 0)),
+                         maybeAtomicity = Nothing,
+                         A.alignment = 0,
+                         metadata = []
+                       }
+              ] (
+              Do $ Ret (Just (ConstantOperand (C.Int 32 0))) [
+                (
+                  "my-metadatum", 
+                  MetadataNode [
+                   LocalReference (UnName 1),
+                   MetadataStringOperand "super hyper"
+                  ]
+                )
+              ]
+            )
+          ]
+         ]
+    let s = "; ModuleID = '<string>'\n\
+            \\n\
+            \@0 = external global i32\n\
+            \\n\
+            \define i32 @foo() {\n\
+            \  %1 = load i32* @0\n\
+            \  ret i32 0, !my-metadatum !{i32 %1, metadata !\"super hyper\"}\n\
+            \}\n"
+    strCheck ast s,
+
+  testCase "global" $ do
+    let ast = Module "<string>" Nothing Nothing [
+         GlobalDefinition $ Function L.External V.Default CC.C [] (IntegerType 32) (Name "foo") ([
+            ],False)
+          [] 
+          Nothing 0         
+          [
+           BasicBlock (UnName 0) [
+              ] (
+              Do $ Ret (Just (ConstantOperand (C.Int 32 0))) [
+                ("my-metadatum", MetadataNodeReference (MetadataNodeID 0))
+              ]
+            )
+          ],
+          MetadataNodeDefinition (MetadataNodeID 0) [ ConstantOperand (C.Int 32 1) ]
+         ]
+    let s = "; ModuleID = '<string>'\n\
+            \\n\
+            \define i32 @foo() {\n\
+            \  ret i32 0, !my-metadatum !0\n\
+            \}\n\
+            \\n\
+            \!0 = metadata !{i32 1}\n"
+    strCheck ast s,
+
+  testCase "named" $ do
+    let ast = Module "<string>" Nothing Nothing [
+          NamedMetadataDefinition "my-module-metadata" [MetadataNodeID 0],
+          MetadataNodeDefinition (MetadataNodeID 0) [ ConstantOperand (C.Int 32 1) ]
+         ]
+    let s = "; ModuleID = '<string>'\n\
+            \\n\
+            \!my-module-metadata = !{!0}\n\
+            \\n\
+            \!0 = metadata !{i32 1}\n"
+    strCheck ast s,
+
+  testGroup "cyclic" [
+    testCase "metadata-only" $ do
+      let ast = Module "<string>" Nothing Nothing [
+            NamedMetadataDefinition "my-module-metadata" [MetadataNodeID 0],
+            MetadataNodeDefinition (MetadataNodeID 0) [
+              MetadataNodeOperand (MetadataNodeReference (MetadataNodeID 1)) 
+             ],
+            MetadataNodeDefinition (MetadataNodeID 1) [
+              MetadataNodeOperand (MetadataNodeReference (MetadataNodeID 0)) 
+             ]
+           ]
+      let s = "; ModuleID = '<string>'\n\
+              \\n\
+              \!my-module-metadata = !{!0}\n\
+              \\n\
+              \!0 = metadata !{metadata !1}\n\
+              \!1 = metadata !{metadata !0}\n"
+      strCheck ast s,
+
+    testCase "metadata-global" $ do
+      let ast = Module "<string>" Nothing Nothing [
+           GlobalDefinition $ Function L.External V.Default CC.C [] VoidType (Name "foo") ([
+              ],False)
+            [] 
+            Nothing 0         
+            [
+             BasicBlock (UnName 0) [
+              ] (
+                Do $ Ret Nothing [ ("my-metadatum", MetadataNodeReference (MetadataNodeID 0)) ]
+              )
+            ],
+            MetadataNodeDefinition (MetadataNodeID 0) [
+              ConstantOperand (C.GlobalReference (Name "foo"))
+             ]
+           ]
+      let s = "; ModuleID = '<string>'\n\
+              \\n\
+              \define void @foo() {\n\
+              \  ret void, !my-metadatum !0\n\
+              \}\n\
+              \\n\
+              \!0 = metadata !{void ()* @foo}\n"
+      strCheck ast s
+   ]
+
+ ]
diff --git a/test/LLVM/General/Test/Module.hs b/test/LLVM/General/Test/Module.hs
new file mode 100644
--- /dev/null
+++ b/test/LLVM/General/Test/Module.hs
@@ -0,0 +1,281 @@
+module LLVM.General.Test.Module where
+
+import Test.Framework
+import Test.Framework.Providers.HUnit
+import Test.HUnit
+
+import LLVM.General.Test.Support
+
+import Data.Bits
+import Data.Functor
+
+import LLVM.General.Context
+import LLVM.General.Module
+import LLVM.General.Diagnostic
+import LLVM.General.AST
+import LLVM.General.AST.AddrSpace
+import qualified LLVM.General.AST.IntegerPredicate as IPred
+
+import qualified LLVM.General.AST.Linkage as L
+import qualified LLVM.General.AST.Visibility as V
+import qualified LLVM.General.AST.CallingConvention as CC
+import qualified LLVM.General.AST.Attribute as A
+import qualified LLVM.General.AST.Global as G
+import qualified LLVM.General.AST.Constant as C
+
+handString = "; ModuleID = '<string>'\n\
+    \\n\
+    \%0 = type { i32, %1*, %0* }\n\
+    \%1 = type opaque\n\
+    \\n\
+    \@0 = global i32 1\n\
+    \@1 = external protected addrspace(3) global i32, section \"foo\"\n\
+    \@2 = unnamed_addr global i8 2\n\
+    \@3 = external global %0\n\
+    \@4 = external global [4294967296 x i32]\n\
+    \@.argyle = thread_local global i32 0\n\
+    \\n\
+    \@three = alias private i32 addrspace(3)* @1\n\
+    \@two = alias i32 addrspace(3)* @three\n\
+    \\n\
+    \define i32 @bar() {\n\
+    \  %1 = call zeroext i32 @foo(i32 inreg 1, i8 signext 4) nounwind uwtable readnone\n\
+    \  ret i32 %1\n\
+    \}\n\
+    \\n\
+    \define zeroext i32 @foo(i32 inreg %x, i8 signext %y) nounwind uwtable readnone {\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"
+
+handAST = Module "<string>" Nothing Nothing [
+      TypeDefinition (UnName 0) (
+         Just $ StructureType False [
+           IntegerType 32,
+           PointerType (NamedTypeReference (UnName 1)) (AddrSpace 0),
+           PointerType (NamedTypeReference (UnName 0)) (AddrSpace 0)
+          ]),
+      TypeDefinition (UnName 1) Nothing,
+      GlobalDefinition $ globalVariableDefaults {
+        G.name = UnName 0,
+        G.type' = IntegerType 32,
+        G.initializer = Just (C.Int 32 1)
+      },
+      GlobalDefinition $ globalVariableDefaults {
+        G.name = UnName 1,
+        G.visibility = V.Protected,
+        G.type' = IntegerType 32,
+        G.addrSpace = AddrSpace 3,
+        G.section = Just "foo"
+      },
+      GlobalDefinition $ globalVariableDefaults {
+        G.name = UnName 2,
+        G.hasUnnamedAddr = True,
+        G.type' = IntegerType 8,
+        G.initializer = Just (C.Int 8 2)
+      },
+      GlobalDefinition $ globalVariableDefaults {
+        G.name = UnName 3,
+        G.type' = NamedTypeReference (UnName 0)
+      },
+      GlobalDefinition $ globalVariableDefaults {
+        G.name = UnName 4,
+        G.type' = ArrayType (1 `shift` 32) (IntegerType 32)
+      },
+      GlobalDefinition $ globalVariableDefaults {
+        G.name = Name ".argyle",
+        G.type' = IntegerType 32,
+        G.initializer = Just (C.Int 32 0),
+        G.isThreadLocal = True
+      },
+      GlobalDefinition $ globalAliasDefaults {
+        G.name = Name "three", 
+        G.linkage = L.Private,
+        G.type' = PointerType (IntegerType 32) (AddrSpace 3),
+        G.aliasee = C.GlobalReference (UnName 1)
+      },
+      GlobalDefinition $ globalAliasDefaults {
+        G.name = Name "two",
+        G.type' = PointerType (IntegerType 32) (AddrSpace 3),
+        G.aliasee = C.GlobalReference (Name "three")
+      },
+      GlobalDefinition $ Function L.External V.Default CC.C [] (IntegerType 32) (Name "bar") ([],False) [] Nothing 0 [
+        BasicBlock (UnName 0) [
+         UnName 1 := Call {
+           isTailCall = False,
+           callingConvention = CC.C,
+           returnAttributes = [A.ZeroExt],
+           function = Right (ConstantOperand (C.GlobalReference (Name "foo"))),
+           arguments = [
+            (ConstantOperand (C.Int 32 1), [A.InReg]),
+            (ConstantOperand (C.Int 8 4), [A.SignExt])
+           ],
+           functionAttributes = [A.NoUnwind, A.ReadNone, A.UWTable],
+           metadata = []
+         }
+       ] (
+         Do $ Ret (Just (LocalReference (UnName 1))) []
+       )
+      ],
+      GlobalDefinition $ Function L.External V.Default CC.C [A.ZeroExt] (IntegerType 32) (Name "foo") ([
+          Parameter (IntegerType 32) (Name "x") [A.InReg],
+          Parameter (IntegerType 8) (Name "y") [A.SignExt]
+         ],False)
+         [A.NoUnwind, A.ReadNone, A.UWTable] Nothing 0 
+       [
+        BasicBlock (UnName 0) [
+         UnName 1 := Mul {
+           nsw = True,
+           nuw = False,
+           operand0 = LocalReference (Name "x"),
+           operand1 = LocalReference (Name "x"),
+           metadata = []
+         }
+         ] (
+           Do $ Br (Name "here") []
+         ),
+        BasicBlock (Name "here") [
+         Name "go" := ICmp {
+           iPredicate = IPred.EQ,
+           operand0 = LocalReference (UnName 1),
+           operand1 = LocalReference (Name "x"),
+           metadata = []
+         }
+         ] (
+            Do $ CondBr {
+              condition = LocalReference (Name "go"),
+              trueDest = Name "there",
+              falseDest = Name "elsewhere",
+              metadata' = []
+            }
+         ),
+        BasicBlock (Name "there") [
+         UnName 2 := Add {
+           nsw = True,
+           nuw = False,
+           operand0 = LocalReference (UnName 1),
+           operand1 = ConstantOperand (C.Int 32 3),
+           metadata = []
+         }
+         ] (
+           Do $ Br (Name "elsewhere") []
+         ),
+        BasicBlock (Name "elsewhere") [
+         Name "r" := Phi {
+           type' = IntegerType 32,
+           incomingValues = [
+             (ConstantOperand (C.Int 32 2), Name "there"),
+             (ConstantOperand (C.Int 32 57), Name "here")
+           ],
+           metadata = []
+         }
+         ] (
+           Do $ Ret (Just (LocalReference (Name "r"))) []
+         )
+       ]
+      ]
+
+tests = testGroup "Module" [
+  testGroup "withModuleFromString" [
+    testCase "basic" $ withContext $ \context -> do
+      z <- withModuleFromString' 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 <- withModuleFromString' context s (const $ return 0)
+      z @?= 0
+   ],
+
+  testCase "handStringIsCanonical" $ withContext $ \context -> do
+    s <- withModuleFromString' context handString moduleString
+    s @?= handString,
+
+  testCase "moduleAST" $ withContext $ \context -> do
+    ast <- withModuleFromString' context handString moduleAST
+    ast @?= handAST,
+    
+  testCase "withModuleFromAST" $ withContext $ \context -> do
+   s <- withModuleFromAST context handAST moduleString
+   s @?= Right handString,
+
+  testCase "triple" $ withContext $ \context -> do
+   let hAST = "; ModuleID = '<string>'\n\
+              \target triple = \"x86_64-unknown-linux\"\n"
+   ast <- withModuleFromString' context hAST moduleAST
+   ast @?= defaultModule { moduleTargetTriple = Just "x86_64-unknown-linux" },
+
+  testGroup "regression" [
+    testCase "set flag on constant expr" $ withContext $ \context -> do
+      let ast = Module "<string>" Nothing Nothing [
+           GlobalDefinition $ Function L.External V.Default CC.C [] (IntegerType 32) (Name "foo") ([
+               Parameter (IntegerType 32) (Name "x") []
+              ],False)
+              [] Nothing 0 
+            [
+             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 (UnName 1))) []
+              )
+            ]
+           ]
+      t <- withModuleFromAST context ast $ \_ -> return True
+      t @?= Right True
+   ],
+
+  testGroup "failures" [
+    testCase "bad block reference" $ withContext $ \context -> do
+      let badAST = Module "<string>" Nothing Nothing [
+           GlobalDefinition $ Function L.External V.Default CC.C [] (IntegerType 32) (Name "foo") ([
+               Parameter (IntegerType 32) (Name "x") []
+              ],False)
+              [] Nothing 0 
+            [
+             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 (UnName 1))) []
+              )
+            ]
+           ]
+      t <- withModuleFromAST context badAST $ \_ -> return True
+      t @?= Left "reference to undefined block: Name \"not here\""
+   ]
+ ]
diff --git a/test/LLVM/General/Test/Optimization.hs b/test/LLVM/General/Test/Optimization.hs
new file mode 100644
--- /dev/null
+++ b/test/LLVM/General/Test/Optimization.hs
@@ -0,0 +1,327 @@
+module LLVM.General.Test.Optimization where
+
+import Test.Framework
+import Test.Framework.Providers.HUnit
+import Test.HUnit
+
+import Data.Functor
+import qualified Data.Map as Map
+
+import LLVM.General.Module
+import LLVM.General.Context
+import LLVM.General.PassManager
+import LLVM.General.Transforms
+import LLVM.General.Target
+
+import LLVM.General.AST as A
+import LLVM.General.AST.Type
+import LLVM.General.AST.Name
+import LLVM.General.AST.AddrSpace
+import LLVM.General.AST.DataLayout
+import qualified LLVM.General.AST.IntegerPredicate as IPred
+import qualified LLVM.General.AST.Linkage as L
+import qualified LLVM.General.AST.Visibility as V
+import qualified LLVM.General.AST.CallingConvention as CC
+import qualified LLVM.General.AST.Attribute as A
+import qualified LLVM.General.AST.Global as G
+import qualified LLVM.General.AST.Constant as C
+
+import qualified LLVM.General.Relocation as R
+import qualified LLVM.General.CodeModel as CM
+import qualified LLVM.General.CodeGenOpt as CGO
+
+handAST = 
+  Module "<string>" Nothing Nothing [
+      GlobalDefinition $ Function L.External V.Default CC.C [] (IntegerType 32) (Name "foo") ([
+          Parameter (IntegerType 32) (Name "x") []
+         ],False)
+        [A.NoUnwind, A.ReadNone, A.UWTable] 
+        Nothing 0
+        [
+        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 (UnName 1),
+           operand1 = ConstantOperand (C.Int 32 42),
+           metadata = []
+         }
+         ] (
+            Do $ CondBr {
+              condition = LocalReference (Name "go"),
+              trueDest = Name "take",
+              falseDest = Name "done",
+              metadata' = []
+            }
+         ),
+        BasicBlock (Name "take") [
+         UnName 2 := Sub {
+           nsw = False,
+           nuw = False,
+           operand0 = LocalReference (Name "x"),
+           operand1 = LocalReference (Name "x"),
+           metadata = []
+         }
+         ] (
+           Do $ Br (Name "done") []
+         ),
+        BasicBlock (Name "done") [
+         Name "r" := Phi {
+           type' = IntegerType 32,
+           incomingValues = [
+             (LocalReference (UnName 2), Name "take"),
+             (ConstantOperand (C.Int 32 57), Name "here")
+           ],
+           metadata = []
+         }
+         ] (
+           Do $ Ret (Just (LocalReference (Name "r"))) []
+         )
+       ]
+      ]
+
+optimize :: PassManagerSpecification s => s -> A.Module -> IO A.Module
+optimize s m = do
+  mOut <- withContext $ \context -> withModuleFromAST context m $ \mIn' -> do
+                  withPassManager s $ \pm -> runPassManager pm mIn'
+                  moduleAST mIn'
+  either fail return mOut
+
+tests = testGroup "Optimization" [
+  testCase "curated" $ do
+    mOut <- optimize defaultCuratedPassSetSpec handAST
+
+    mOut @?= Module "<string>" Nothing Nothing [
+      GlobalDefinition $ Function L.External V.Default CC.C [] (IntegerType 32) (Name "foo") ([
+          Parameter (IntegerType 32) (Name "x") []
+         ],False)
+       [A.NoUnwind, A.ReadNone, A.UWTable] 
+       Nothing 0         
+       [
+        BasicBlock (Name "here") [
+           ] (
+           Do $ Ret (Just (ConstantOperand (C.Int 32 0))) []
+         )
+       ]
+      ],
+
+  testGroup "individual" [
+    testCase "ConstantPropagation" $ do
+      mOut <- optimize [ConstantPropagation] handAST
+
+      mOut @?= Module "<string>" Nothing Nothing [
+        GlobalDefinition $ Function L.External V.Default CC.C [] (IntegerType 32) (Name "foo") ([
+            Parameter (IntegerType 32) (Name "x") []
+           ],False)
+         [A.NoUnwind, A.ReadNone, A.UWTable] 
+         Nothing 0         
+         [
+          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 (Name "x"),
+             operand1 = LocalReference (Name "x"),
+             metadata = []
+            }
+          ] (
+           Do $ Br (Name "done") []
+          ),
+          BasicBlock (Name "done") [
+           Name "r" := Phi {type' = IntegerType 32, incomingValues = [(LocalReference (UnName 1),Name "take"),(ConstantOperand (C.Int 32 57), Name "here")], metadata = []}
+          ] (
+            Do $ Ret (Just (LocalReference (Name "r"))) []
+          )
+         ]
+        ],
+
+    testCase "BasicBlockVectorization" $ do
+      let
+        mIn = Module "<string>" Nothing Nothing [
+         GlobalDefinition $ Function L.External V.Default CC.C [] (FloatingPointType 64 IEEE) (Name "foo") ([
+             Parameter (FloatingPointType 64 IEEE) (Name "a1") [],
+             Parameter (FloatingPointType 64 IEEE) (Name "a2") [],
+             Parameter (FloatingPointType 64 IEEE) (Name "b1") [],
+             Parameter (FloatingPointType 64 IEEE) (Name "b2") []
+            ],False)
+          [] 
+          Nothing 0         
+          [
+           BasicBlock (UnName 0) [
+             Name "x1" := FSub { 
+                        operand0 = LocalReference (Name "a1"), 
+                        operand1 = LocalReference (Name "b1"),
+                        metadata = []
+                      },
+             Name "x2" := FSub { 
+                        operand0 = LocalReference (Name "a2"), 
+                        operand1 = LocalReference (Name "b2"),
+                        metadata = []
+                      },
+             Name "y1" := FMul { 
+                        operand0 = LocalReference (Name "x1"), 
+                        operand1 = LocalReference (Name "a1"),
+                        metadata = []
+                      },
+             Name "y2" := FMul { 
+                        operand0 = LocalReference (Name "x2"), 
+                        operand1 = LocalReference (Name "a2"),
+                        metadata = []
+                      },
+             Name "z1" := FAdd { 
+                        operand0 = LocalReference (Name "y1"), 
+                        operand1 = LocalReference (Name "b1"),
+                        metadata = []
+                      },
+             Name "z2" := FAdd { 
+                        operand0 = LocalReference (Name "y2"), 
+                        operand1 = LocalReference (Name "b2"),
+                        metadata = []
+                      },
+             Name "r" := FMul {
+                        operand0 = LocalReference (Name "z1"), 
+                        operand1 = LocalReference (Name "z2"),
+                        metadata = []
+                      }
+           ] (Do $ Ret (Just (LocalReference (Name "r"))) [])
+          ]
+         ]
+      mOut <- optimize [ 
+               defaultVectorizeBasicBlocks {
+                 vectorizePointers = False,
+                 requiredChainDepth = 3
+               },
+               InstructionCombining,
+               GlobalValueNumbering False
+              ] mIn
+      mOut @?= Module "<string>" Nothing Nothing [
+       GlobalDefinition $ Function 
+        L.External V.Default CC.C [] (FloatingPointType 64 IEEE) (Name "foo") ([
+              Parameter (FloatingPointType 64 IEEE) (Name "a1") [],
+              Parameter (FloatingPointType 64 IEEE) (Name "a2") [],
+              Parameter (FloatingPointType 64 IEEE) (Name "b1") [],
+              Parameter (FloatingPointType 64 IEEE) (Name "b2") []
+             ],False)
+             []
+             Nothing 0
+          [
+           BasicBlock (UnName 0) [
+             Name "x1.v.i1.1" := InsertElement {
+               vector = ConstantOperand (C.Undef (VectorType 2 (FloatingPointType 64 IEEE))),
+               element = LocalReference (Name "b1"),
+               index = ConstantOperand (C.Int 32 0),
+               metadata = []
+              },
+             Name "x1.v.i1.2" := InsertElement {
+               vector = LocalReference (Name "x1.v.i1.1"),
+               element = LocalReference (Name "b2"),
+               index = ConstantOperand (C.Int 32 1),
+               metadata = []
+              },
+             Name "x1.v.i0.1" := InsertElement {
+               vector = ConstantOperand (C.Undef (VectorType 2 (FloatingPointType 64 IEEE))),
+               element = LocalReference (Name "a1"),
+               index = ConstantOperand (C.Int 32 0),
+               metadata = []
+              },
+             Name "x1.v.i0.2" := InsertElement {
+               vector = LocalReference (Name "x1.v.i0.1"),
+               element = LocalReference (Name "a2"),
+               index = ConstantOperand (C.Int 32 1),
+               metadata = []
+              },
+             Name "x1" := FSub {
+               operand0 = LocalReference (Name "x1.v.i0.2"),
+               operand1 = LocalReference (Name "x1.v.i1.2"),
+               metadata = []
+              },
+             Name "y1" := FMul {
+               operand0 = LocalReference (Name "x1"),
+               operand1 = LocalReference (Name "x1.v.i0.2"),
+               metadata = []
+              },
+             Name "z1" := FAdd {
+               operand0 = LocalReference (Name "y1"),
+               operand1 = LocalReference (Name "x1.v.i1.2"),
+               metadata = []
+              },
+             Name "z1.v.r1" := ExtractElement {
+               vector = LocalReference (Name "z1"),
+               index = ConstantOperand (C.Int 32 0),
+               metadata = []
+              },
+             Name "z1.v.r2" := ExtractElement {
+               vector = LocalReference (Name "z1"),
+               index = ConstantOperand (C.Int 32 1),
+               metadata = []
+              },
+             Name "r" := FMul {
+               operand0 = LocalReference (Name "z1.v.r1"),
+               operand1 = LocalReference (Name "z1.v.r2"),
+               metadata = []
+              }
+            ] (
+             Do $ Ret (Just (LocalReference (Name "r"))) []
+            )
+          ]
+        ],
+      
+    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
+        let triple = "x86_64-apple-darwin"
+        Right (target, _) <- lookupTarget Nothing triple
+        withTargetOptions $ \targetOptions -> do
+          withTargetMachine target triple "" "" targetOptions
+                            R.Default CM.Default CGO.Default $ \targetMachine -> do
+            targetLowering <- getTargetLowering targetMachine
+            withPassManager ([LowerInvoke False], targetLowering) $ \passManager -> do
+              let astIn = 
+                    Module "<string>" Nothing Nothing [
+                     GlobalDefinition $ Function L.External V.Default CC.C [] (IntegerType 32) (Name "foo") ([
+                           Parameter (IntegerType 32) (Name "x") []
+                          ],False) [] Nothing 0 [
+                            BasicBlock (Name "here") [
+                            ] (
+                              Do $ Ret (Just (ConstantOperand (C.Int 32 0))) []
+                            )
+                          ]
+                     ] 
+              Right astOut <- withModuleFromAST context astIn $ \mIn -> do
+                runPassManager passManager mIn
+                moduleAST mIn
+              astOut @?= Module "<string>" Nothing Nothing [
+                      GlobalDefinition $ Function L.External V.Default CC.C [] (IntegerType 32) (Name "foo") ([
+                          Parameter (IntegerType 32) (Name "x") []
+                        ],False) [] Nothing 0 [
+                         BasicBlock (Name "here") [
+                         ] (
+                           Do $ Ret (Just (ConstantOperand (C.Int 32 0))) []
+                         )
+                        ],
+                       GlobalDefinition $ Function L.External V.Default CC.C [] VoidType (Name "abort") ([],False)
+                               [] Nothing 0 []
+                      ]
+   ]
+ ]
diff --git a/test/LLVM/General/Test/Support.hs b/test/LLVM/General/Test/Support.hs
new file mode 100644
--- /dev/null
+++ b/test/LLVM/General/Test/Support.hs
@@ -0,0 +1,27 @@
+module LLVM.General.Test.Support where
+
+import Test.Framework
+import Test.Framework.Providers.HUnit
+import Test.HUnit
+
+import Data.Functor
+
+import LLVM.General.Context
+import LLVM.General.Module
+import LLVM.General.Diagnostic
+
+withModuleFromString' c s f  = do
+  e <- withModuleFromString c s f
+  case e of
+    Right r -> return r
+    Left d -> do
+           let e = diagnosticDisplay d
+           putStrLn e
+           fail e
+
+strCheck mAST mStr = withContext $ \context -> do
+  a <- withModuleFromString' context mStr moduleAST
+  s <- either error id <$> withModuleFromAST context mAST moduleString
+  (a,s) @?= (mAST, mStr)
+
+  
diff --git a/test/LLVM/General/Test/Target.hs b/test/LLVM/General/Test/Target.hs
new file mode 100644
--- /dev/null
+++ b/test/LLVM/General/Test/Target.hs
@@ -0,0 +1,69 @@
+{-# LANGUAGE
+  RecordWildCards
+  #-}
+module LLVM.General.Test.Target where
+
+import Test.Framework
+import Test.Framework.Providers.HUnit
+import Test.HUnit
+import Test.Framework.Providers.QuickCheck2
+import Test.QuickCheck
+import Test.QuickCheck.Property
+
+import Control.Monad
+
+import LLVM.General.Target
+import LLVM.General.Target.Options
+
+instance Arbitrary FloatABI where
+  arbitrary = elements [minBound .. maxBound]
+
+instance Arbitrary FloatingPointOperationFusionMode where
+  arbitrary = elements [minBound .. maxBound]
+
+instance Arbitrary Options where
+  arbitrary = do
+    printMachineCode <- arbitrary
+    noFramePointerElimination <- arbitrary
+    noFramePointerEliminationNonLeaf <- arbitrary
+    lessPreciseFloatingPointMultiplyAddOption <- arbitrary
+    unsafeFloatingPointMath <- arbitrary
+    noInfinitiesFloatingPointMath <- arbitrary
+    noNaNsFloatingPointMath <- arbitrary
+    honorSignDependentRoundingFloatingPointMathOption <- arbitrary
+    useSoftFloat <- arbitrary
+    noZerosInBSS <- arbitrary
+    jITExceptionHandling <- arbitrary
+    jITEmitDebugInfo <- arbitrary
+    jITEmitDebugInfoToDisk <- arbitrary
+    guaranteedTailCallOptimization <- arbitrary
+    disableTailCalls <- arbitrary
+    realignStack <- arbitrary
+    enableFastInstructionSelection <- arbitrary
+    positionIndependentExecutable <- arbitrary
+    enableSegmentedStacks <- arbitrary
+    useInitArray <- arbitrary
+    stackAlignmentOverride <- arbitrary
+    trapFunctionName <- elements [ "foo", "bar", "baz" ]
+    floatABIType <- arbitrary
+    allowFloatingPointOperationFusion <- arbitrary
+    stackSmashingProtectionBufferSize <- arbitrary
+    return Options { .. }
+
+tests = testGroup "Target" [
+  testGroup "Options" [
+     testGroup "regressions" [
+       testCase "hurm" $ do
+         withTargetOptions $ \to -> do
+           let o = Options {printMachineCode = True, noFramePointerElimination = False, noFramePointerEliminationNonLeaf = True, lessPreciseFloatingPointMultiplyAddOption = True, unsafeFloatingPointMath = True, noInfinitiesFloatingPointMath = True, noNaNsFloatingPointMath = False, honorSignDependentRoundingFloatingPointMathOption = True, useSoftFloat = True, noZerosInBSS = False, jITExceptionHandling = True, jITEmitDebugInfo = True, jITEmitDebugInfoToDisk = False, guaranteedTailCallOptimization = False, disableTailCalls = False, realignStack = False, enableFastInstructionSelection = True, positionIndependentExecutable = True, enableSegmentedStacks = False, useInitArray = True, stackAlignmentOverride = 9432851444, trapFunctionName = "baz", floatABIType = FloatABISoft, allowFloatingPointOperationFusion = FloatingPointOperationFusionStrict, stackSmashingProtectionBufferSize = 2650013862}
+           pokeTargetOptions o to
+           o' <- peekTargetOptions to
+           o' @?= o
+       ],
+     testProperty "basic" $ \options -> morallyDubiousIOProperty $ do
+       withTargetOptions $ \to -> do
+         pokeTargetOptions options to
+         options' <- peekTargetOptions to
+         return $ options == options'
+   ]
+ ]
diff --git a/test/LLVM/General/Test/Tests.hs b/test/LLVM/General/Test/Tests.hs
new file mode 100644
--- /dev/null
+++ b/test/LLVM/General/Test/Tests.hs
@@ -0,0 +1,28 @@
+module LLVM.General.Test.Tests where
+
+import Test.Framework
+
+import qualified LLVM.General.Test.Constants as Constants
+import qualified LLVM.General.Test.DataLayout as DataLayout
+import qualified LLVM.General.Test.ExecutionEngine as ExecutionEngine
+import qualified LLVM.General.Test.Global as Global
+import qualified LLVM.General.Test.InlineAssembly as InlineAssembly
+import qualified LLVM.General.Test.Instructions as Instructions
+import qualified LLVM.General.Test.Metadata as Metadata
+import qualified LLVM.General.Test.Module as Module
+import qualified LLVM.General.Test.Optimization as Optimization
+import qualified LLVM.General.Test.Target as Target
+
+
+tests = testGroup "llvm-general" [
+    Constants.tests,
+    DataLayout.tests,
+    ExecutionEngine.tests,
+    Global.tests,
+    InlineAssembly.tests,
+    Instructions.tests,
+    Metadata.tests,
+    Module.tests,
+    Optimization.tests,
+    Target.tests
+  ]
diff --git a/test/Test.hs b/test/Test.hs
new file mode 100644
--- /dev/null
+++ b/test/Test.hs
@@ -0,0 +1,6 @@
+import Test.Framework
+import qualified LLVM.General.Test.Tests as LLVM.General
+
+main = defaultMain [
+        LLVM.General.tests
+  ]
