diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,19 @@
+## 7.0.0 (2018-09-28)
+
+* Throw an `EncodeException` if a local variable is defined multiple
+  times. This is consistent with `llc` which also forbids reusing
+  variable names.
+* Update for LLVM 7.0:
+  * The ORC JIT API has been changed to reflect the changes in LLVM
+    itself. The test suite should give you a good idea of how to adapt
+    your code but here is a summary:
+    * There is now a separate `ExecutionSession` that tracks module handles.
+    * `SymbolResolver` only has a single resolver function.
+    * Creating an `ObjectLinkingLayer` requires a way to get the
+      symbol resolver for a module.
+    * Creating a `CompileOnDemandLayer` requires a way to set and get
+      the symbol resolver for a module.
+
 ## 6.3.0 (2018-06-12)
 
 * `findSymbol` and `findSymbolIn` now return `Either JITSymbolError
diff --git a/Setup.hs b/Setup.hs
--- a/Setup.hs
+++ b/Setup.hs
@@ -41,7 +41,7 @@
 #endif
 
 llvmVersion :: Version
-llvmVersion = mkVersion [6,0]
+llvmVersion = mkVersion [7,0]
 
 llvmConfigNames :: [String]
 llvmConfigNames = [
diff --git a/llvm-hs.cabal b/llvm-hs.cabal
--- a/llvm-hs.cabal
+++ b/llvm-hs.cabal
@@ -1,5 +1,5 @@
 name: llvm-hs
-version: 6.3.0
+version: 7.0.0
 license: BSD3
 license-file: LICENSE
 author: Anthony Cowley, Stephen Diehl, Moritz Kiefer <moritz.kiefer@purelyfunctional.org>, Benjamin S. Scarlet
@@ -45,7 +45,7 @@
 source-repository head
   type: git
   location: git://github.com/llvm-hs/llvm-hs.git
-  branch: llvm-4
+  branch: llvm-7
 
 flag shared-llvm
   description: link against llvm shared rather than static library
@@ -76,7 +76,7 @@
     template-haskell >= 2.5.0.0,
     containers >= 0.4.2.1,
     array >= 0.4.0.0,
-    llvm-hs-pure == 6.2.*
+    llvm-hs-pure == 7.0.*
   hs-source-dirs: src
   default-extensions:
     NoImplicitPrelude
@@ -252,7 +252,7 @@
     mtl >= 2.1,
     transformers >= 0.3.0.0,
     temporary >= 1.2 && < 1.4,
-    pretty-show >= 1.6 && < 1.8,
+    pretty-show >= 1.6 && < 1.9,
     process,
     temporary
   hs-source-dirs: test
diff --git a/src/Control/Monad/Trans/AnyCont.hs b/src/Control/Monad/Trans/AnyCont.hs
--- a/src/Control/Monad/Trans/AnyCont.hs
+++ b/src/Control/Monad/Trans/AnyCont.hs
@@ -6,7 +6,8 @@
 import LLVM.Prelude
 
 import Control.Monad.Catch
-import Control.Monad.Cont
+import Control.Monad.Cont as Cont
+import Control.Monad.Fail as Fail
 
 newtype AnyContT m a = AnyContT { unAnyContT :: forall r . ContT r m a }
 
@@ -20,8 +21,11 @@
 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))
-  
+  fail s = AnyContT (ContT (\_ -> Cont.fail s))
+
+instance MonadFail m => MonadFail (AnyContT m) where
+  fail s = AnyContT (ContT (\_ -> Fail.fail s))
+
 instance MonadIO m => MonadIO (AnyContT m) where
   liftIO = lift . liftIO
 
@@ -33,6 +37,7 @@
 
 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)
 
diff --git a/src/LLVM/Internal/DecodeAST.hs b/src/LLVM/Internal/DecodeAST.hs
--- a/src/LLVM/Internal/DecodeAST.hs
+++ b/src/LLVM/Internal/DecodeAST.hs
@@ -8,6 +8,7 @@
 
 import LLVM.Prelude
 
+import Control.Monad.Fail
 import Control.Monad.Catch
 import Control.Monad.State
 import Control.Monad.AnyCont
@@ -72,6 +73,7 @@
     Functor,
     Monad,
     MonadIO,
+    MonadFail,
     MonadState DecodeState,
     MonadThrow,
     MonadAnyCont IO,
diff --git a/src/LLVM/Internal/EncodeAST.hs b/src/LLVM/Internal/EncodeAST.hs
--- a/src/LLVM/Internal/EncodeAST.hs
+++ b/src/LLVM/Internal/EncodeAST.hs
@@ -122,6 +122,7 @@
   def <- gets $ Map.lookup n . encodeStateLocals
   case def of
     Just (ForwardValue dummy) -> liftIO $ FFI.replaceAllUsesWith dummy v
+    Just _ -> throwM (EncodeException ("Duplicate definition of local variable: " <> show n <> "."))
     _ -> return ()
   modify $ \b -> b { encodeStateLocals = Map.insert n (DefinedValue v) (encodeStateLocals b) }
 
diff --git a/src/LLVM/Internal/FFI/Attribute.h b/src/LLVM/Internal/FFI/Attribute.h
--- a/src/LLVM/Internal/FFI/Attribute.h
+++ b/src/LLVM/Internal/FFI/Attribute.h
@@ -28,6 +28,7 @@
 	macro(NoAlias,T,T,F)                              \
 	macro(NoBuiltin,F,F,T)                            \
 	macro(NoCapture,T,F,F)                            \
+    macro(NoCfCheck,F,F,T)                            \
 	macro(NoDuplicate,F,F,T)                          \
 	macro(NoImplicitFloat,F,F,T)                      \
 	macro(NoInline,F,F,T)                             \
@@ -37,6 +38,7 @@
 	macro(NoUnwind,F,F,T)                             \
 	macro(NonLazyBind,F,F,T)                          \
 	macro(NonNull,T,T,F)                              \
+    macro(OptForFuzzing,F,F,T)                        \
 	macro(OptimizeForSize,F,F,T)                      \
 	macro(OptimizeNone,F,F,T)                         \
 	macro(ReadNone,T,F,T)                             \
@@ -49,6 +51,7 @@
     macro(SanitizeHWAddress,F,F,T)                    \
 	macro(SanitizeMemory,F,F,T)                       \
 	macro(SanitizeThread,F,F,T)                       \
+    macro(ShadowCallStack,F,F,T)                      \
     macro(Speculatable,F,F,T)                         \
 	macro(StackAlignment,F,F,T)                       \
 	macro(StackProtect,F,F,T)                         \
diff --git a/src/LLVM/Internal/FFI/BitcodeC.cpp b/src/LLVM/Internal/FFI/BitcodeC.cpp
--- a/src/LLVM/Internal/FFI/BitcodeC.cpp
+++ b/src/LLVM/Internal/FFI/BitcodeC.cpp
@@ -27,7 +27,7 @@
 }
 
 void LLVM_Hs_WriteBitcode(LLVMModuleRef m, raw_ostream &os) {
-	WriteBitcodeToFile(unwrap(m), os);
+	WriteBitcodeToFile(*unwrap(m), os);
 }
 
 }
diff --git a/src/LLVM/Internal/FFI/GlobalValue.h b/src/LLVM/Internal/FFI/GlobalValue.h
--- a/src/LLVM/Internal/FFI/GlobalValue.h
+++ b/src/LLVM/Internal/FFI/GlobalValue.h
@@ -45,14 +45,7 @@
 	macro(LocalExecTLSModel)
 
 #define LLVM_HS_FOR_EACH_UNNAMED_ADDR(macro) \
-	macro(None)                                   \
-	macro(Local)                                  \
+	macro(No)                                \
+	macro(Local)                             \
 	macro(Global)
-
-typedef enum {
-#define ENUM_CASE(x) LLVMUnnamedAddr ## x,
-LLVM_HS_FOR_EACH_UNNAMED_ADDR(ENUM_CASE)
-#undef ENUM_CASE
-} LLVMUnnamedAddr;
-
 #endif
diff --git a/src/LLVM/Internal/FFI/GlobalValue.hs b/src/LLVM/Internal/FFI/GlobalValue.hs
--- a/src/LLVM/Internal/FFI/GlobalValue.hs
+++ b/src/LLVM/Internal/FFI/GlobalValue.hs
@@ -64,10 +64,10 @@
 foreign import ccall unsafe "LLVMSetAlignment" setAlignment ::
   Ptr GlobalValue -> CUInt -> IO ()
 
-foreign import ccall unsafe "LLVM_Hs_GetUnnamedAddr" getUnnamedAddr ::
+foreign import ccall unsafe "LLVMGetUnnamedAddress" getUnnamedAddr ::
   Ptr GlobalValue -> IO UnnamedAddr
 
-foreign import ccall unsafe "LLVM_Hs_SetUnnamedAddr" setUnnamedAddr ::
+foreign import ccall unsafe "LLVMSetUnnamedAddress" setUnnamedAddr ::
   Ptr GlobalValue -> UnnamedAddr -> IO ()
 
 foreign import ccall unsafe "LLVM_Hs_GetThreadLocalMode" getThreadLocalMode ::
diff --git a/src/LLVM/Internal/FFI/GlobalValueC.cpp b/src/LLVM/Internal/FFI/GlobalValueC.cpp
--- a/src/LLVM/Internal/FFI/GlobalValueC.cpp
+++ b/src/LLVM/Internal/FFI/GlobalValueC.cpp
@@ -45,32 +45,6 @@
   comdat.setSelectionKind(Comdat::SelectionKind(csk));
 }
 
-static LLVMUnnamedAddr unwrap(GlobalValue::UnnamedAddr a) {
-    switch (a) {
-#define ENUM_CASE(x) case GlobalValue::UnnamedAddr::x: return LLVMUnnamedAddr ## x;
-LLVM_HS_FOR_EACH_UNNAMED_ADDR(ENUM_CASE)
-#undef ENUM_CASE
-    default: return LLVMUnnamedAddrNone;
-    }
-}
-
-static GlobalValue::UnnamedAddr wrap(LLVMUnnamedAddr a) {
-    switch (a) {
-#define ENUM_CASE(x) case LLVMUnnamedAddr ## x: return GlobalValue::UnnamedAddr::x;
-LLVM_HS_FOR_EACH_UNNAMED_ADDR(ENUM_CASE)
-#undef ENUM_CASE
-    default: return GlobalValue::UnnamedAddr::None;
-    }
-}
-
-LLVMUnnamedAddr LLVM_Hs_GetUnnamedAddr(LLVMValueRef globalVal) {
-    return unwrap(unwrap<GlobalValue>(globalVal)->getUnnamedAddr());
-}
-
-void LLVM_Hs_SetUnnamedAddr(LLVMValueRef globalVal, LLVMUnnamedAddr attr) {
-    unwrap<GlobalValue>(globalVal)->setUnnamedAddr(wrap(attr));
-}
-
 #define ENUM_CASE(n)                                                           \
     static_assert(unsigned(GlobalValue::n) == unsigned(LLVM##n),               \
                   "TLS Model Enum mismatch");
diff --git a/src/LLVM/Internal/FFI/LLVMCTypes.hsc b/src/LLVM/Internal/FFI/LLVMCTypes.hsc
--- a/src/LLVM/Internal/FFI/LLVMCTypes.hsc
+++ b/src/LLVM/Internal/FFI/LLVMCTypes.hsc
@@ -165,7 +165,7 @@
 
 newtype UnnamedAddr = UnnamedAddr CUInt
   deriving (Eq, Show, Typeable, Data, Generic)
-#define UA_Rec(n) { #n, LLVMUnnamedAddr ## n },
+#define UA_Rec(n) { #n, LLVM ## n ## UnnamedAddr },
 #{inject UNNAMED_ADDR, UnnamedAddr, UnnamedAddr, unnamedAddr, UA_Rec}
 
 newtype SynchronizationScope = SynchronizationScope CUInt
diff --git a/src/LLVM/Internal/FFI/Metadata.hs b/src/LLVM/Internal/FFI/Metadata.hs
--- a/src/LLVM/Internal/FFI/Metadata.hs
+++ b/src/LLVM/Internal/FFI/Metadata.hs
@@ -120,11 +120,14 @@
 -- DIEnumerator
 
 foreign import ccall unsafe "LLVM_Hs_Get_DIEnumerator" getDIEnumerator ::
-  Ptr Context -> Int64 -> Ptr MDString -> IO (Ptr DIEnumerator)
+  Ptr Context -> Int64 -> LLVMBool -> Ptr MDString -> IO (Ptr DIEnumerator)
 
 foreign import ccall unsafe "LLVM_Hs_DIEnumerator_GetValue" getDIEnumeratorValue ::
   Ptr DIEnumerator -> IO Int64
 
+foreign import ccall unsafe "LLVM_Hs_DIEnumerator_GetIsUnsigned" getDIEnumeratorIsUnsigned ::
+  Ptr DIEnumerator -> IO LLVMBool
+
 foreign import ccall unsafe "LLVM_Hs_DIEnumerator_GetName" getDIEnumeratorName ::
   Ptr DIEnumerator -> IO (Ptr MDString)
 
@@ -135,7 +138,7 @@
 foreign import ccall unsafe "LLVM_Hs_DIFileGetDirectory" getFileDirectory ::
   Ptr DIFile -> IO (Ptr MDString)
 
-foreign import ccall unsafe "LLVM_Hs_DIFileGetChecksum" getFileChecksum ::
+foreign import ccall unsafe "LLVM_Hs_DIFileGetChecksum" getFileChecksumValue ::
   Ptr DIFile -> IO (Ptr MDString)
 
 foreign import ccall unsafe "LLVM_Hs_DIFileGetChecksumKind" getFileChecksumKind ::
@@ -282,10 +285,19 @@
   Ptr Context -> Ptr MDString -> Ptr MDString -> ChecksumKind -> Ptr MDString -> IO (Ptr DIFile)
 
 -- DISubrange
-foreign import ccall unsafe "LLVM_Hs_Get_DISubrange" getDISubrange ::
+foreign import ccall unsafe "LLVM_Hs_Get_DISubrangeConstantCount" getDISubrangeConstantCount ::
   Ptr Context -> Int64 -> Int64 -> IO (Ptr DISubrange)
 
-foreign import ccall unsafe "LLVM_Hs_DISubrange_GetCount" getDISubrangeCount ::
+foreign import ccall unsafe "LLVM_Hs_Get_DISubrangeVariableCount" getDISubrangeVariableCount ::
+  Ptr Context -> Ptr DIVariable -> Int64 -> IO (Ptr DISubrange)
+
+foreign import ccall unsafe "LLVM_Hs_DISubrange_HasConstantCount" getDISubrangeHasConstantCount ::
+  Ptr DISubrange -> IO LLVMBool
+
+foreign import ccall unsafe "LLVM_Hs_DISubrange_GetVariableCount" getDISubrangeCountVariable ::
+  Ptr DISubrange -> IO (Ptr DIVariable)
+
+foreign import ccall unsafe "LLVM_Hs_DISubrange_GetConstantCount" getDISubrangeCountConstant ::
   Ptr DISubrange -> IO Int64
 
 foreign import ccall unsafe "LLVM_Hs_DISubrange_GetLowerBound" getDISubrangeLowerBound ::
@@ -335,7 +347,7 @@
 foreign import ccall unsafe "LLVM_Hs_DISubprogram_GetTemplateParams" getDISubprogramTemplateParams ::
   Ptr DISubprogram -> IO (TupleArray DITemplateParameter)
 
-foreign import ccall unsafe "LLVM_Hs_DISubprogram_GetVariables" getDISubprogramVariables ::
+foreign import ccall unsafe "LLVM_Hs_DISubprogram_GetRetainedNodes" getDISubprogramRetainedNodes ::
   Ptr DISubprogram -> IO (TupleArray DILocalVariable)
 
 foreign import ccall unsafe "LLVM_Hs_DISubprogram_GetThrownTypes" getDISubprogramThrownTypes ::
diff --git a/src/LLVM/Internal/FFI/MetadataC.cpp b/src/LLVM/Internal/FFI/MetadataC.cpp
--- a/src/LLVM/Internal/FFI/MetadataC.cpp
+++ b/src/LLVM/Internal/FFI/MetadataC.cpp
@@ -195,15 +195,19 @@
 
 // DIEnumerator
 
-DIEnumerator* LLVM_Hs_Get_DIEnumerator(LLVMContextRef cxt, int64_t value, MDString* name) {
+DIEnumerator* LLVM_Hs_Get_DIEnumerator(LLVMContextRef cxt, int64_t value, LLVMBool isUnsigned, MDString* name) {
     LLVMContext& c = *unwrap(cxt);
-    return DIEnumerator::get(c, value, name);
+    return DIEnumerator::get(c, value, isUnsigned, name);
 }
 
 int64_t LLVM_Hs_DIEnumerator_GetValue(DIEnumerator* md) {
     return md->getValue();
 }
 
+LLVMBool LLVM_Hs_DIEnumerator_GetIsUnsigned(DIEnumerator* md) {
+    return md->isUnsigned();
+}
+
 MDString* LLVM_Hs_DIEnumerator_GetName(DIEnumerator* md) {
     return md->getRawName();
 }
@@ -217,11 +221,19 @@
 }
 
 MDString* LLVM_Hs_DIFileGetChecksum(DIFile *di) {
-    return di->getRawChecksum();
+    auto checksumInfo = di->getRawChecksum();
+    if (checksumInfo.hasValue()) {
+        return checksumInfo->Value;
+    }
+    return nullptr;
 }
 
 llvm::DIFile::ChecksumKind LLVM_Hs_DIFileGetChecksumKind(DIFile *di) {
-    return di->getChecksumKind();
+    auto checksumInfo = di->getRawChecksum();
+    if (checksumInfo.hasValue()) {
+        return checksumInfo->Kind;
+    }
+    return static_cast<llvm::DIFile::ChecksumKind>(0);
 }
 
 // DIScope
@@ -293,17 +305,33 @@
 
 DIFile* LLVM_Hs_Get_DIFile(LLVMContextRef ctx, MDString* filename, MDString* directory, unsigned checksumKind, MDString* checksum) {
     LLVMContext& c = *unwrap(ctx);
-    return DIFile::get(c, filename, directory, static_cast<DIFile::ChecksumKind>(checksumKind), checksum);
+    if (!checksum) {
+        return DIFile::get(c, filename, directory);
+    }
+    return DIFile::get(c, filename, directory, DIFile::ChecksumInfo<MDString*>(static_cast<llvm::DIFile::ChecksumKind>(checksumKind), checksum));
 }
 
-DISubrange* LLVM_Hs_Get_DISubrange(LLVMContextRef ctx, int64_t count, int64_t lowerBound) {
+DISubrange* LLVM_Hs_Get_DISubrangeConstantCount(LLVMContextRef ctx, int64_t count, int64_t lowerBound) {
     return DISubrange::get(*unwrap(ctx), count, lowerBound);
 }
 
-int64_t LLVM_Hs_DISubrange_GetCount(DISubrange* range) {
-    return range->getCount();
+DISubrange* LLVM_Hs_Get_DISubrangeVariableCount(LLVMContextRef ctx, DIVariable* count, int64_t lowerBound) {
+    return DISubrange::get(*unwrap(ctx), count, lowerBound);
 }
 
+LLVMBool LLVM_Hs_DISubrange_HasConstantCount(DISubrange* range) {
+    return range->getCount().is<ConstantInt*>();
+}
+
+int64_t LLVM_Hs_DISubrange_GetConstantCount(DISubrange* range) {
+    return range->getCount().dyn_cast<ConstantInt*>()->getSExtValue();
+}
+
+DIVariable* LLVM_Hs_DISubrange_GetVariableCount(DISubrange* range) {
+    return range->getCount().dyn_cast<DIVariable*>();
+}
+
+
 int64_t LLVM_Hs_DISubrange_GetLowerBound(DISubrange* range) {
     return range->getLowerBound();
 }
@@ -490,8 +518,8 @@
     return cast_or_null<MDTuple>(p->getRawTemplateParams());
 }
 
-MDTuple* LLVM_Hs_DISubprogram_GetVariables(DISubprogram* p) {
-    return cast_or_null<MDTuple>(p->getRawVariables());
+MDTuple* LLVM_Hs_DISubprogram_GetRetainedNodes(DISubprogram* p) {
+    return cast_or_null<MDTuple>(p->getRawRetainedNodes());
 }
 
 MDTuple* LLVM_Hs_DISubprogram_GetThrownTypes(DISubprogram* p) {
diff --git a/src/LLVM/Internal/FFI/OrcJIT.hs b/src/LLVM/Internal/FFI/OrcJIT.hs
--- a/src/LLVM/Internal/FFI/OrcJIT.hs
+++ b/src/LLVM/Internal/FFI/OrcJIT.hs
@@ -10,8 +10,12 @@
 import LLVM.Internal.FFI.DataLayout
 import LLVM.Internal.FFI.LLVMCTypes
 
+-- | Abstract type used as the identifier for a module.
+newtype ModuleKey = ModuleKey Word64 deriving (Eq, Ord, Show)
+
 data JITSymbol
-data LambdaResolver
+data SymbolResolver
+data ExecutionSession
 
 newtype TargetAddress = TargetAddress Word64
 
@@ -24,12 +28,12 @@
   Ptr JITSymbol -> IO ()
 
 foreign import ccall safe "LLVM_Hs_createLambdaResolver" createLambdaResolver ::
-  FunPtr SymbolResolverFn ->
+  Ptr ExecutionSession ->
   FunPtr SymbolResolverFn ->
-  IO (Ptr LambdaResolver)
+  IO (Ptr SymbolResolver)
 
-foreign import ccall safe "LLVM_Hs_disposeLambdaResolver" disposeLambdaResolver ::
-  Ptr LambdaResolver -> IO ()
+foreign import ccall safe "LLVM_Hs_disposeSymbolResolver" disposeSymbolResolver ::
+  Ptr SymbolResolver -> IO ()
 
 foreign import ccall safe "LLVM_Hs_JITSymbol_getAddress" getAddress ::
   Ptr JITSymbol -> Ptr (OwnerTransfered CString) -> IO TargetAddress
@@ -48,3 +52,21 @@
 
 foreign import ccall safe "LLVM_Hs_disposeMangledSymbol" disposeMangledSymbol ::
   CString -> IO ()
+
+foreign import ccall safe "LLVM_Hs_createExecutionSession" createExecutionSession ::
+  IO (Ptr ExecutionSession)
+
+foreign import ccall safe "LLVM_Hs_disposeExecutionSession" disposeExecutionSession ::
+  Ptr ExecutionSession -> IO ()
+
+foreign import ccall safe "LLVM_Hs_allocateVModule" allocateVModule ::
+  Ptr ExecutionSession -> IO ModuleKey
+
+foreign import ccall safe "LLVM_Hs_releaseVModule" releaseVModule ::
+  Ptr ExecutionSession -> ModuleKey -> IO ()
+
+foreign import ccall "wrapper" wrapGetSymbolResolver ::
+  (ModuleKey -> IO (Ptr SymbolResolver)) -> IO (FunPtr (ModuleKey -> IO (Ptr SymbolResolver)))
+
+foreign import ccall "wrapper" wrapSetSymbolResolver ::
+  (ModuleKey -> Ptr SymbolResolver -> IO ()) -> IO (FunPtr (ModuleKey -> Ptr SymbolResolver -> IO ()))
diff --git a/src/LLVM/Internal/FFI/OrcJIT/CompileLayer.hs b/src/LLVM/Internal/FFI/OrcJIT/CompileLayer.hs
--- a/src/LLVM/Internal/FFI/OrcJIT/CompileLayer.hs
+++ b/src/LLVM/Internal/FFI/OrcJIT/CompileLayer.hs
@@ -13,25 +13,22 @@
 
 data CompileLayer
 
--- | Abstract type representing a module in a 'LLVM.OrcJIT.CompileLayer'.
-newtype ModuleHandle = ModuleHandle Word
-
 foreign import ccall safe "LLVM_Hs_CompileLayer_dispose" disposeCompileLayer ::
   Ptr CompileLayer -> IO ()
 
 foreign import ccall safe "LLVM_Hs_CompileLayer_addModule" addModule ::
   Ptr CompileLayer ->
   Ptr DataLayout ->
+  ModuleKey ->
   Ptr Module ->
-  Ptr LambdaResolver ->
   Ptr (OwnerTransfered CString) ->
-  IO ModuleHandle
+  IO ()
 
 foreign import ccall safe "LLVM_Hs_CompileLayer_removeModule" removeModule ::
-  Ptr CompileLayer -> ModuleHandle -> IO ()
+  Ptr CompileLayer -> ModuleKey -> IO ()
 
 foreign import ccall safe "LLVM_Hs_CompileLayer_findSymbol" findSymbol ::
   Ptr CompileLayer -> CString -> LLVMBool -> IO (Ptr JITSymbol)
 
 foreign import ccall safe "LLVM_Hs_CompileLayer_findSymbolIn" findSymbolIn ::
-  Ptr CompileLayer -> ModuleHandle -> CString -> LLVMBool -> IO (Ptr JITSymbol)
+  Ptr CompileLayer -> ModuleKey -> CString -> LLVMBool -> IO (Ptr JITSymbol)
diff --git a/src/LLVM/Internal/FFI/OrcJIT/CompileOnDemandLayer.hs b/src/LLVM/Internal/FFI/OrcJIT/CompileOnDemandLayer.hs
--- a/src/LLVM/Internal/FFI/OrcJIT/CompileOnDemandLayer.hs
+++ b/src/LLVM/Internal/FFI/OrcJIT/CompileOnDemandLayer.hs
@@ -26,7 +26,7 @@
   IO () -> IO (FunPtr (IO ()))
 
 foreign import ccall safe "LLVM_Hs_createLocalCompileCallbackManager" createLocalCompileCallbackManager ::
-  CString -> TargetAddress -> IO (Ptr JITCompileCallbackManager)
+  Ptr ExecutionSession -> CString -> TargetAddress -> IO (Ptr JITCompileCallbackManager)
 
 foreign import ccall safe "LLVM_Hs_disposeCallbackManager" disposeCallbackManager ::
   Ptr JITCompileCallbackManager -> IO ()
@@ -41,7 +41,10 @@
   Ptr (Set (Ptr Function)) -> Ptr Function -> IO ()
 
 foreign import ccall safe "LLVM_Hs_createCompileOnDemandLayer" createCompileOnDemandLayer ::
+  Ptr ExecutionSession ->
   Ptr CompileLayer ->
+  FunPtr (ModuleKey -> IO (Ptr SymbolResolver)) ->
+  FunPtr (ModuleKey -> Ptr SymbolResolver -> IO ()) ->
   FunPtr PartitioningFn ->
   Ptr JITCompileCallbackManager ->
   Ptr IndirectStubsManagerBuilder ->
diff --git a/src/LLVM/Internal/FFI/OrcJIT/LinkingLayer.hs b/src/LLVM/Internal/FFI/OrcJIT/LinkingLayer.hs
--- a/src/LLVM/Internal/FFI/OrcJIT/LinkingLayer.hs
+++ b/src/LLVM/Internal/FFI/OrcJIT/LinkingLayer.hs
@@ -16,20 +16,18 @@
 data ObjectLinkingLayer
 instance ChildOf LinkingLayer ObjectLinkingLayer
 
-newtype ObjectHandle = ObjectHandle Word
-
 foreign import ccall safe "LLVM_Hs_createObjectLinkingLayer" createObjectLinkingLayer ::
-  IO (Ptr ObjectLinkingLayer)
+  Ptr ExecutionSession -> FunPtr (ModuleKey -> IO (Ptr SymbolResolver)) -> IO (Ptr ObjectLinkingLayer)
 
 foreign import ccall safe "LLVM_Hs_LinkingLayer_dispose" disposeLinkingLayer ::
   Ptr LinkingLayer -> IO ()
 
 foreign import ccall safe "LLVM_Hs_LinkingLayer_addObject" addObjectFile ::
   Ptr LinkingLayer ->
+  ModuleKey ->
   Ptr ObjectFile ->
-  Ptr LambdaResolver ->
   Ptr (OwnerTransfered CString) ->
-  IO ObjectHandle
+  IO ()
 
 foreign import ccall safe "LLVM_Hs_LinkingLayer_findSymbol" findSymbol ::
   Ptr LinkingLayer ->
@@ -39,7 +37,7 @@
 
 foreign import ccall safe "LLVM_Hs_LinkingLayer_findSymbolIn" findSymbolIn ::
   Ptr LinkingLayer ->
-  ObjectHandle ->
+  ModuleKey ->
   CString ->
   LLVMBool ->
   IO (Ptr JITSymbol)
diff --git a/src/LLVM/Internal/FFI/OrcJITC.cpp b/src/LLVM/Internal/FFI/OrcJITC.cpp
--- a/src/LLVM/Internal/FFI/OrcJITC.cpp
+++ b/src/LLVM/Internal/FFI/OrcJITC.cpp
@@ -22,16 +22,16 @@
 using namespace llvm;
 using namespace orc;
 
-#define SYMBOL_CASE(x) static_assert((unsigned)LLVMJITSymbolFlag ## x == (unsigned) llvm::JITSymbolFlags::FlagNames::x, "JITSymbolFlag values should agree");
+static_assert(std::is_same<VModuleKey, uint64_t>::value,
+              "VModuleKey should be uint64_t");
+
+#define SYMBOL_CASE(x)                                                         \
+    static_assert((unsigned)LLVMJITSymbolFlag##x ==                            \
+                      (unsigned)llvm::JITSymbolFlags::FlagNames::x,            \
+                  "JITSymbolFlag values should agree");
 LLVM_HS_FOR_EACH_JIT_SYMBOL_FLAG(SYMBOL_CASE)
 
-typedef unsigned LLVMModuleHandle;
-typedef unsigned LLVMObjectHandle;
-typedef llvm::orc::LambdaResolver<
-    std::function<JITSymbol(const std::string &name)>,
-    std::function<JITSymbol(const std::string &name)>>
-    LLVMLambdaResolver;
-typedef std::shared_ptr<LLVMLambdaResolver> *LLVMLambdaResolverRef;
+typedef std::shared_ptr<SymbolResolver> *LLVMSymbolResolverRef;
 
 // We want to allow users to choose themselves which layers they want to use.
 // However, the LLVM API requires that this is selected statically via template
@@ -39,84 +39,48 @@
 // creating an LinkingLayer and a CompileLayer class which use virtual dispatch
 // to select the concrete layer.
 
-template <typename T> class HandleSet {
-  public:
-    unsigned insert(T t) {
-        unsigned handle = handles.insert({nextFree, t}).first->first;
-        ++nextFree;
-        return handle;
-    }
-    T &lookup(unsigned i) { return handles.at(i); }
-
-    void remove(unsigned i) { handles.erase(i); }
-
-  private:
-    std::unordered_map<unsigned, T> handles;
-    unsigned nextFree = 0;
-};
-
 class LinkingLayer {
   public:
+    using ObjectPtr = std::unique_ptr<MemoryBuffer>;
     virtual ~LinkingLayer(){};
-    typedef unsigned ObjHandleT;
-    virtual Expected<ObjHandleT>
-    addObject(std::shared_ptr<object::OwningBinary<object::ObjectFile>> object,
-              std::shared_ptr<JITSymbolResolver> resolver) = 0;
-    virtual Error removeObject(ObjHandleT H) = 0;
+    virtual Error addObject(VModuleKey k, ObjectPtr objBuffer) = 0;
+    virtual Error removeObject(VModuleKey k) = 0;
     virtual JITSymbol findSymbol(StringRef name, bool exportedSymbolsOnly) = 0;
-    virtual JITSymbol findSymbolIn(ObjHandleT h, StringRef name,
+    virtual JITSymbol findSymbolIn(VModuleKey k, StringRef name,
                                    bool exportedSymbolsOnly) = 0;
-    virtual void emitAndFinalize(ObjHandleT h) = 0;
+    virtual Error emitAndFinalize(VModuleKey k) = 0;
 };
 
 template <typename T> class LinkingLayerT : public LinkingLayer {
   public:
     LinkingLayerT(T data_) : data(std::move(data_)) {}
-    Expected<ObjHandleT>
-    addObject(std::shared_ptr<object::OwningBinary<object::ObjectFile>> object,
-              std::shared_ptr<JITSymbolResolver> resolver) override {
-        if (auto handleOrErr =
-                data.addObject(std::move(object), std::move(resolver))) {
-            return handles.insert(*handleOrErr);
-        } else {
-            return handleOrErr.takeError();
-        }
-    }
-    Error removeObject(ObjHandleT h) override {
-        if (auto err = data.removeObject(handles.lookup(h))) {
-            return err;
-        } else {
-            handles.remove(h);
-            return err;
-        }
+    Error addObject(VModuleKey k, ObjectPtr objBuffer) override {
+        return data.addObject(k, std::move(objBuffer));
     }
+    Error removeObject(VModuleKey k) override { return data.removeObject(k); }
     JITSymbol findSymbol(StringRef name, bool exportedSymbolsOnly) override {
         return data.findSymbol(name, exportedSymbolsOnly);
     }
-    JITSymbol findSymbolIn(ObjHandleT h, StringRef name,
+    JITSymbol findSymbolIn(VModuleKey k, StringRef name,
                            bool exportedSymbolsOnly) override {
-        return data.findSymbolIn(handles.lookup(h), name, exportedSymbolsOnly);
+        return data.findSymbolIn(k, name, exportedSymbolsOnly);
     }
-    void emitAndFinalize(ObjHandleT h) override {
-        data.emitAndFinalize(handles.lookup(h));
+    Error emitAndFinalize(VModuleKey k) override {
+        return data.emitAndFinalize(k);
     }
 
   private:
     T data;
-    HandleSet<typename T::ObjHandleT> handles;
 };
 
 class CompileLayer {
   public:
-    typedef LLVMModuleHandle ModuleHandleT;
     virtual ~CompileLayer(){};
     virtual JITSymbol findSymbol(StringRef Name, bool ExportedSymbolsOnly) = 0;
-    virtual JITSymbol findSymbolIn(ModuleHandleT H, StringRef Name,
+    virtual JITSymbol findSymbolIn(VModuleKey K, StringRef Name,
                                    bool ExportedSymbolsOnly) = 0;
-    virtual Expected<ModuleHandleT>
-    addModule(std::shared_ptr<Module> Modules,
-              std::shared_ptr<JITSymbolResolver> Resolver) = 0;
-    virtual Error removeModule(ModuleHandleT H) = 0;
+    virtual Error addModule(VModuleKey K, std::unique_ptr<Module> Module) = 0;
+    virtual Error removeModule(VModuleKey K) = 0;
 };
 
 template <typename T> class CompileLayerT : public CompileLayer {
@@ -126,29 +90,17 @@
     JITSymbol findSymbol(StringRef Name, bool ExportedSymbolsOnly) override {
         return data.findSymbol(Name, ExportedSymbolsOnly);
     }
-    JITSymbol findSymbolIn(ModuleHandleT H, StringRef Name,
+    JITSymbol findSymbolIn(VModuleKey K, StringRef Name,
                            bool ExportedSymbolsOnly) override {
-        return data.findSymbolIn(handles.lookup(H), Name, ExportedSymbolsOnly);
-    }
-    Expected<ModuleHandleT>
-    addModule(std::shared_ptr<Module> Module,
-              std::shared_ptr<JITSymbolResolver> Resolver) override {
-        if (auto handleOrErr =
-                data.addModule(std::move(Module), std::move(Resolver))) {
-            return handles.insert(*handleOrErr);
-        } else {
-            return handleOrErr.takeError();
-        }
+        return data.findSymbolIn(K, Name, ExportedSymbolsOnly);
     }
-    Error removeModule(ModuleHandleT H) override {
-        auto handle = handles.lookup(H);
-        handles.remove(H);
-        return data.removeModule(handle);
+    Error addModule(VModuleKey K, std::unique_ptr<Module> Module) override {
+        return data.addModule(K, std::move(Module));
     }
+    Error removeModule(VModuleKey K) override { return data.removeModule(K); }
 
   private:
     T data;
-    HandleSet<typename T::ModuleHandleT> handles;
 };
 
 typedef llvm::orc::CompileOnDemandLayer<CompileLayer> LLVMCompileOnDemandLayer;
@@ -156,7 +108,7 @@
 
 typedef llvm::orc::IRTransformLayer<
     CompileLayer,
-    std::function<std::shared_ptr<Module>(std::shared_ptr<Module>)>>
+    std::function<std::unique_ptr<Module>(std::unique_ptr<Module>)>>
     LLVMIRTransformLayer;
 
 typedef llvm::orc::JITCompileCallbackManager *LLVMJITCompileCallbackManagerRef;
@@ -205,6 +157,20 @@
 
 extern "C" {
 
+ExecutionSession *LLVM_Hs_createExecutionSession() {
+    return new ExecutionSession();
+}
+
+void LLVM_Hs_disposeExecutionSession(ExecutionSession *es) { delete es; }
+
+VModuleKey LLVM_Hs_allocateVModule(ExecutionSession *es) {
+    return es->allocateVModule();
+}
+
+void LLVM_Hs_releaseVModule(ExecutionSession *es, VModuleKey k) {
+    es->releaseVModule(k);
+}
+
 /* Constructor functions for the different compile layers */
 
 CompileLayer *LLVM_Hs_createIRCompileLayer(LinkingLayer *linkingLayer,
@@ -216,13 +182,24 @@
 }
 
 CompileLayer *LLVM_Hs_createCompileOnDemandLayer(
-    CompileLayer *compileLayer,
+    ExecutionSession *es, CompileLayer *compileLayer,
+    LLVMSymbolResolverRef (*getSymbolResolver)(VModuleKey k),
+    void (*setSymbolResolver)(VModuleKey k, LLVMSymbolResolverRef r),
     void (*partitioningFtor)(llvm::Function *, std::set<llvm::Function *> *set),
     LLVMJITCompileCallbackManagerRef callbackManager,
     LLVMIndirectStubsManagerBuilderRef stubsManager,
     LLVMBool cloneStubsIntoPartitions) {
+    std::function<std::shared_ptr<SymbolResolver>(VModuleKey)>
+        getSymbolResolverFn =
+            [getSymbolResolver](VModuleKey k) { return *getSymbolResolver(k); };
+    std::function<void(VModuleKey, std::shared_ptr<SymbolResolver>)>
+        setSymbolResolverFn =
+            [setSymbolResolver](VModuleKey k,
+                                std::shared_ptr<SymbolResolver> r) {
+                setSymbolResolver(k, new std::shared_ptr<SymbolResolver>(r));
+            };
     return new CompileLayerT<LLVMCompileOnDemandLayer>(
-        *compileLayer,
+        *es, *compileLayer, getSymbolResolverFn, setSymbolResolverFn,
         [partitioningFtor](llvm::Function &f) -> std::set<llvm::Function *> {
             std::set<llvm::Function *> result;
             partitioningFtor(&f, &result);
@@ -234,9 +211,9 @@
 
 CompileLayer *LLVM_Hs_createIRTransformLayer(CompileLayer *compileLayer,
                                              Module *(*transform)(Module *)) {
-    std::function<std::shared_ptr<Module>(std::shared_ptr<Module>)> transform_ =
-        [transform](std::shared_ptr<Module> module) {
-            return std::shared_ptr<Module>(transform(module.get()));
+    std::function<std::unique_ptr<Module>(std::unique_ptr<Module>)> transform_ =
+        [transform](std::unique_ptr<Module> module) {
+            return std::unique_ptr<Module>(transform(module.release()));
         };
     return new CompileLayerT<LLVMIRTransformLayer>(*compileLayer, transform_);
 }
@@ -255,48 +232,46 @@
 }
 
 LLVMJITSymbolRef
-LLVM_Hs_CompileLayer_findSymbolIn(CompileLayer *compileLayer,
-                                  LLVMModuleHandle handle, const char *name,
+LLVM_Hs_CompileLayer_findSymbolIn(CompileLayer *compileLayer, VModuleKey k,
+                                  const char *name,
                                   LLVMBool exportedSymbolsOnly) {
-    JITSymbol symbol =
-        compileLayer->findSymbolIn(handle, name, exportedSymbolsOnly);
+    JITSymbol symbol = compileLayer->findSymbolIn(k, name, exportedSymbolsOnly);
     return new JITSymbol(std::move(symbol));
 }
 
-LLVMModuleHandle LLVM_Hs_CompileLayer_addModule(CompileLayer *compileLayer,
-                                                LLVMTargetDataRef dataLayout,
-                                                LLVMModuleRef module,
-                                                LLVMLambdaResolverRef resolver,
-                                                char **errorMessage) {
-    std::shared_ptr<Module> mod{unwrap(module), [](Module *) {}};
+void LLVM_Hs_CompileLayer_addModule(CompileLayer *compileLayer,
+                                    LLVMTargetDataRef dataLayout, VModuleKey k,
+                                    LLVMModuleRef module, char **errorMessage) {
+    std::unique_ptr<Module> mod{unwrap(module)};
     if (mod->getDataLayout().isDefault()) {
         mod->setDataLayout(*unwrap(dataLayout));
     }
-    if (auto handleOrErr = compileLayer->addModule(std::move(mod), *resolver)) {
-        *errorMessage = nullptr;
-        return *handleOrErr;
-    } else {
-        std::string errString = toString(handleOrErr.takeError());
+    if (auto err = compileLayer->addModule(k, std::move(mod))) {
+        std::string errString = toString(std::move(err));
         *errorMessage = strdup(errString.c_str());
-        return 0;
     }
+    *errorMessage = nullptr;
 }
 
 void LLVM_Hs_CompileLayer_removeModule(CompileLayer *compileLayer,
-                                       LLVMModuleHandle moduleSetHandle) {
-    if (compileLayer->removeModule(moduleSetHandle)) {
+                                       VModuleKey k) {
+    if (compileLayer->removeModule(k)) {
         // TODO handle failure
     }
 }
 
 /* Constructor functions for the different object layers */
 
-LinkingLayer *LLVM_Hs_createObjectLinkingLayer() {
-    return new LinkingLayerT<RTDyldObjectLinkingLayer>(RTDyldObjectLinkingLayer(
-        []() { return std::make_shared<SectionMemoryManager>(); }));
+LinkingLayer *LLVM_Hs_createObjectLinkingLayer(
+    ExecutionSession *es, LLVMSymbolResolverRef (*symbolResolver)(VModuleKey)) {
+    return new LinkingLayerT<RTDyldObjectLinkingLayer>(
+        RTDyldObjectLinkingLayer(*es, [symbolResolver](VModuleKey k) {
+            return RTDyldObjectLinkingLayer::Resources{
+                std::make_shared<SectionMemoryManager>(), *symbolResolver(k)};
+        }));
 }
 
-/* Fuctions that work on all object layers */
+// /* Fuctions that work on all object layers */
 
 void LLVM_Hs_LinkingLayer_dispose(LinkingLayer *linkingLayer) {
     delete linkingLayer;
@@ -305,57 +280,50 @@
 void LLVM_Hs_disposeJITSymbol(LLVMJITSymbolRef symbol) { delete symbol; }
 
 LLVMJITSymbolRef LLVM_Hs_LinkingLayer_findSymbol(LinkingLayer *linkingLayer,
-                                     const char *name,
-                                     LLVMBool exportedSymbolsOnly) {
-  JITSymbol symbol = linkingLayer->findSymbol(name, exportedSymbolsOnly);
-  return new JITSymbol(std::move(symbol));
+                                                 const char *name,
+                                                 LLVMBool exportedSymbolsOnly) {
+    JITSymbol symbol = linkingLayer->findSymbol(name, exportedSymbolsOnly);
+    return new JITSymbol(std::move(symbol));
 }
 
-LLVMJITSymbolRef LLVM_Hs_LinkingLayer_findSymbolIn(LinkingLayer *linkingLayer,
-                                                   LLVMObjectHandle handle,
-                                                   const char *name,
-                                                   LLVMBool exportedSymbolsOnly) {
-  JITSymbol symbol = linkingLayer->findSymbolIn(handle, name, exportedSymbolsOnly);
-  return new JITSymbol(std::move(symbol));
+LLVMJITSymbolRef
+LLVM_Hs_LinkingLayer_findSymbolIn(LinkingLayer *linkingLayer, VModuleKey k,
+                                  const char *name,
+                                  LLVMBool exportedSymbolsOnly) {
+    JITSymbol symbol = linkingLayer->findSymbolIn(k, name, exportedSymbolsOnly);
+    return new JITSymbol(std::move(symbol));
 }
 
-LLVMLambdaResolverRef LLVM_Hs_createLambdaResolver(
-    void (*dylibResolver)(const char *, LLVMJITSymbolRef),
-    void (*externalResolver)(const char *, LLVMJITSymbolRef)) {
-    std::function<JITSymbol(const std::string &name)> dylibResolverFun =
-        [dylibResolver](const std::string &name) -> JITSymbol {
-        JITSymbol symbol(nullptr);
-        dylibResolver(name.c_str(), &symbol);
-        return symbol;
-    };
-    std::function<JITSymbol(const std::string &name)> externalResolverFun =
-        [externalResolver](const std::string &name) -> JITSymbol {
+LLVMSymbolResolverRef LLVM_Hs_createLambdaResolver(
+    ExecutionSession *es,
+    void (*rawResolverFn)(const char *, LLVMJITSymbolRef)) {
+    std::function<JITSymbol(const std::string &name)> resolverFn =
+        [rawResolverFn](const std::string &name) -> JITSymbol {
         JITSymbol symbol(nullptr);
-        externalResolver(name.c_str(), &symbol);
+        rawResolverFn(name.c_str(), &symbol);
         return symbol;
     };
-    return new std::shared_ptr<LLVMLambdaResolver>(
-        createLambdaResolver(dylibResolverFun, externalResolverFun));
+    return new std::shared_ptr<SymbolResolver>(
+        createLegacyLookupResolver(*es, resolverFn, [](Error err) {
+            cantFail(std::move(err), "lookupFlags failed");
+        }));
 }
 
-void LLVM_Hs_disposeLambdaResolver(LLVMLambdaResolverRef resolver) {
+void LLVM_Hs_disposeSymbolResolver(LLVMSymbolResolverRef resolver) {
     delete resolver;
 }
 
-LLVMObjectHandle LLVM_Hs_LinkingLayer_addObject(LinkingLayer *linkLayer,
-                                                LLVMObjectFileRef objRef,
-                                                LLVMLambdaResolverRef resolver,
-                                                char **errorMessage) {
+void LLVM_Hs_LinkingLayer_addObject(LinkingLayer *linkLayer, VModuleKey k,
+                                    LLVMObjectFileRef objRef,
+                                    char **errorMessage) {
 
-    std::shared_ptr<object::OwningBinary<object::ObjectFile>> obj{
-        unwrap(objRef), [](object::OwningBinary<object::ObjectFile> *) {}};
-    if (auto handleOrErr = linkLayer->addObject(std::move(obj), *resolver)) {
-        *errorMessage = nullptr;
-        return *handleOrErr;
-    } else {
-        std::string errString = toString(handleOrErr.takeError());
-        *errorMessage = strdup(errString.c_str());
-        return 0;
+    std::unique_ptr<MemoryBuffer> objBuffer =
+        unwrap(objRef)->takeBinary().second;
+    *errorMessage = nullptr;
+    if (auto err = linkLayer->addObject(k, std::move(objBuffer))) {
+        std::string error = toString(std::move(err));
+        *errorMessage = strdup(error.c_str());
+        return;
     }
 }
 
@@ -375,7 +343,7 @@
     return wrap(symbol->getFlags());
 }
 
-const char* LLVM_Hs_JITSymbol_getErrorMsg(LLVMJITSymbolRef symbol) {
+const char *LLVM_Hs_JITSymbol_getErrorMsg(LLVMJITSymbolRef symbol) {
     if (!symbol) {
         Error err = symbol->takeError();
         return strdup(toString(std::move(err)).c_str());
@@ -399,13 +367,12 @@
     delete[] mangledSymbol;
 }
 
-LLVMJITCompileCallbackManagerRef
-LLVM_Hs_createLocalCompileCallbackManager(const char *triple,
-                                          JITTargetAddress errorHandler) {
+LLVMJITCompileCallbackManagerRef LLVM_Hs_createLocalCompileCallbackManager(
+    ExecutionSession *es, const char *triple, JITTargetAddress errorHandler) {
     // We copy the string so that it can be freed on the Haskell side.
     std::string tripleStr(triple);
     return llvm::orc::createLocalCompileCallbackManager(
-               Triple(std::move(tripleStr)), errorHandler)
+               Triple(std::move(tripleStr)), *es, errorHandler)
         .release();
 }
 
diff --git a/src/LLVM/Internal/FFI/PassManagerC.cpp b/src/LLVM/Internal/FFI/PassManagerC.cpp
--- a/src/LLVM/Internal/FFI/PassManagerC.cpp
+++ b/src/LLVM/Internal/FFI/PassManagerC.cpp
@@ -10,6 +10,7 @@
 #include "llvm/Transforms/Vectorize.h"
 #include "llvm/Transforms/Instrumentation.h"
 #include "llvm/Transforms/Instrumentation/BoundsChecking.h"
+#include "llvm/Transforms/Utils.h"
 #include "llvm/CodeGen/Passes.h"
 #include "llvm-c/Target.h"
 #include "llvm-c/Transforms/PassManagerBuilder.h"
diff --git a/src/LLVM/Internal/FFI/RawOStreamC.cpp b/src/LLVM/Internal/FFI/RawOStreamC.cpp
--- a/src/LLVM/Internal/FFI/RawOStreamC.cpp
+++ b/src/LLVM/Internal/FFI/RawOStreamC.cpp
@@ -4,9 +4,6 @@
 #include "llvm/Support/raw_ostream.h"
 
 using namespace llvm;
-using sys::fs::F_None;
-using sys::fs::F_Excl;
-using sys::fs::F_Text;
 
 extern "C" {
 
@@ -16,7 +13,9 @@
                                 void (&callback)(raw_pwrite_stream &ostream)) {
     std::error_code e;
     raw_fd_ostream os(filename, e,
-                      (excl ? F_Excl : F_None) | (text ? F_Text : F_None));
+                      excl ? sys::fs::CD_CreateNew : sys::fs::CD_OpenAlways,
+                      sys::fs::FA_Write,
+                      text ? sys::fs::OF_Text : sys::fs::OF_None);
     if (e) {
         *error = strdup(e.message().c_str());
         return false;
diff --git a/src/LLVM/Internal/FFI/TargetC.cpp b/src/LLVM/Internal/FFI/TargetC.cpp
--- a/src/LLVM/Internal/FFI/TargetC.cpp
+++ b/src/LLVM/Internal/FFI/TargetC.cpp
@@ -12,11 +12,11 @@
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/LegacyPassManager.h"
 #include "llvm/IR/Module.h"
-#include "llvm/Support/CodeGenCWrappers.h"
 #include "llvm/Support/FormattedStream.h"
 #include "llvm/Support/Host.h"
 #include "llvm/Support/TargetRegistry.h"
 #include "llvm/Support/TargetSelect.h"
+#include "llvm/Target/CodeGenCWrappers.h"
 #include "llvm/Target/TargetMachine.h"
 
 using namespace llvm;
@@ -568,7 +568,7 @@
         ft = TargetMachine::CGFT_ObjectFile;
         break;
     }
-    if (TM->addPassesToEmitFile(pass, *OS, ft)) {
+    if (TM->addPassesToEmitFile(pass, *OS, nullptr, ft)) {
         error = "TargetMachine can't emit a file of this type";
         *ErrorMessage = strdup(error.c_str());
         return true;
diff --git a/src/LLVM/Internal/Global.hs b/src/LLVM/Internal/Global.hs
--- a/src/LLVM/Internal/Global.hs
+++ b/src/LLVM/Internal/Global.hs
@@ -142,7 +142,7 @@
 setThreadLocalMode g m = liftIO . FFI.setThreadLocalMode (FFI.upCast g) =<< encodeM m
 
 genCodingInstance [t| Maybe A.G.UnnamedAddr |] ''FFI.UnnamedAddr [
-  (FFI.unnamedAddrNone, Nothing),
+  (FFI.unnamedAddrNo, Nothing),
   (FFI.unnamedAddrLocal, Just A.G.LocalAddr),
   (FFI.unnamedAddrGlobal, Just A.G.GlobalAddr)
  ]
diff --git a/src/LLVM/Internal/Operand.hs b/src/LLVM/Internal/Operand.hs
--- a/src/LLVM/Internal/Operand.hs
+++ b/src/LLVM/Internal/Operand.hs
@@ -182,25 +182,37 @@
 instance EncodeM EncodeAST A.DISubrange (Ptr FFI.DISubrange) where
   encodeM (A.Subrange {..}) = do
     Context c <- gets encodeStateContext
-    liftIO (FFI.getDISubrange c count lowerBound)
+    case count of
+      A.DICountConstant i -> liftIO (FFI.getDISubrangeConstantCount c i lowerBound)
+      A.DICountVariable v -> do
+        v' <- encodeM v
+        liftIO (FFI.getDISubrangeVariableCount c v' lowerBound)
 
 instance DecodeM DecodeAST A.DISubrange (Ptr FFI.DISubrange) where
   decodeM r = do
-    count <- liftIO (FFI.getDISubrangeCount r)
     lowerBound <- liftIO (FFI.getDISubrangeLowerBound r)
-    pure (A.Subrange count lowerBound)
+    hasConstantCount <- decodeM =<< liftIO (FFI.getDISubrangeHasConstantCount r)
+    if hasConstantCount
+      then do
+        count <- liftIO (FFI.getDISubrangeCountConstant r)
+        pure (A.Subrange (A.DICountConstant count) lowerBound)
+      else do
+        count <- decodeM =<< liftIO (FFI.getDISubrangeCountVariable r)
+        pure (A.Subrange (A.DICountVariable count) lowerBound)
 
 instance EncodeM EncodeAST A.DIEnumerator (Ptr FFI.DIEnumerator) where
   encodeM (A.Enumerator {..}) = do
     name <- encodeM name
+    isUnsigned <- encodeM isUnsigned
     Context c <- gets encodeStateContext
-    liftIO (FFI.getDIEnumerator c value name)
+    liftIO (FFI.getDIEnumerator c value isUnsigned name)
 
 instance DecodeM DecodeAST A.DIEnumerator (Ptr FFI.DIEnumerator) where
   decodeM e = do
     value <- liftIO (FFI.getDIEnumeratorValue e)
+    isUnsigned <- decodeM =<< liftIO (FFI.getDIEnumeratorIsUnsigned e)
     name <- decodeM =<< liftIO (FFI.getDIEnumeratorName e)
-    pure (A.Enumerator value name)
+    pure (A.Enumerator value isUnsigned name)
 
 instance EncodeM EncodeAST A.DINode (Ptr FFI.DINode) where
   encodeM (A.DISubrange r) = do
@@ -375,18 +387,24 @@
   decodeM diF = do
     fname <- decodeM =<< liftIO (FFI.getFileFilename diF)
     dir   <- decodeM =<< liftIO (FFI.getFileDirectory diF)
-    cksum <- decodeM =<< liftIO (FFI.getFileChecksum diF)
-    csk   <- decodeM =<< liftIO (FFI.getFileChecksumKind diF)
-    return $ A.File fname dir cksum csk
+    mayChecksum <- decodeM =<< liftIO (FFI.getFileChecksumValue diF)
+    case mayChecksum of
+      Nothing -> pure (A.File fname dir Nothing)
+      Just checksum -> do
+        csk <- decodeM =<< liftIO (FFI.getFileChecksumKind diF)
+        pure (A.File fname dir (Just (A.ChecksumInfo csk checksum)))
 
 instance EncodeM EncodeAST A.DIFile (Ptr FFI.DIFile) where
-  encodeM (A.File {A.filename, A.directory, A.checksum, A.checksumKind}) = do
+  encodeM (A.File {..}) = do
     filename <- encodeM filename
     directory <- encodeM directory
-    checksum <- encodeM checksum
-    checksumKind <- encodeM checksumKind
     Context c <- gets encodeStateContext
-    liftIO (FFI.getDIFile c filename directory checksumKind checksum)
+    case checksum of
+      Nothing -> liftIO (FFI.getDIFile c filename directory (FFI.ChecksumKind 0) nullPtr)
+      Just (A.ChecksumInfo {..}) -> do
+        checksumValue <- encodeM checksumValue
+        checksumKind <- encodeM checksumKind
+        liftIO (FFI.getDIFile c filename directory checksumKind checksumValue)
 
 instance EncodeM EncodeAST (Maybe A.Encoding) FFI.Encoding where
   encodeM Nothing = pure (FFI.Encoding 0)
@@ -407,8 +425,7 @@
   ]
 
 genCodingInstance [t|A.ChecksumKind|] ''FFI.ChecksumKind
-  [ (FFI.ChecksumKind 0, A.None)
-  , (FFI.ChecksumKind 1, A.MD5)
+  [ (FFI.ChecksumKind 1, A.MD5)
   , (FFI.ChecksumKind 2, A.SHA1)
   ]
 
@@ -769,7 +786,7 @@
     containingType <- decodeM =<< liftIO (FFI.getDISubprogramContainingType p)
     unit <- decodeM =<< liftIO (FFI.getDISubprogramUnit p)
     templateParams <- decodeM =<< liftIO (FFI.getDISubprogramTemplateParams p)
-    variables <- decodeM =<< liftIO (FFI.getDISubprogramVariables p)
+    retainedNodes <- decodeM =<< liftIO (FFI.getDISubprogramRetainedNodes p)
     thrownTypes <- decodeM =<< liftIO (FFI.getDISubprogramThrownTypes p)
     decl <- decodeM =<< liftIO (FFI.getDISubprogramDeclaration p)
     pure A.Subprogram
@@ -789,7 +806,7 @@
       , A.unit = unit
       , A.templateParams = templateParams
       , A.declaration = decl
-      , A.variables = variables
+      , A.retainedNodes = retainedNodes
       , A.thrownTypes = thrownTypes
       , A.localToUnit = localToUnit
       , A.thisAdjustment = thisAdjustment
@@ -814,7 +831,7 @@
     unit <- encodeM unit
     templateParams <- encodeM templateParams
     declaration <- encodeM declaration
-    variables <- encodeM variables
+    retainedNodes <- encodeM retainedNodes
     thrownTypes <- encodeM thrownTypes
     Context c <- gets encodeStateContext
     FFI.upCast <$> liftIO
@@ -825,7 +842,7 @@
         containingType virtuality virtualityIndex
         thisAdjustment flags optimized
         unit templateParams declaration
-        variables thrownTypes)
+        retainedNodes thrownTypes)
 
 instance DecodeM DecodeAST A.DILocalScope (Ptr FFI.DILocalScope) where
   decodeM ls = do
diff --git a/src/LLVM/Internal/OrcJIT.hs b/src/LLVM/Internal/OrcJIT.hs
--- a/src/LLVM/Internal/OrcJIT.hs
+++ b/src/LLVM/Internal/OrcJIT.hs
@@ -30,6 +30,8 @@
 instance MonadIO m => DecodeM m MangledSymbol CString where
   decodeM str = liftIO $ MangledSymbol <$> packCString str
 
+newtype ExecutionSession = ExecutionSession (Ptr FFI.ExecutionSession)
+
 -- | Contrary to the C++ interface, we do not store the HasError flag
 -- here. Instead decoding a JITSymbol produces a sumtype based on
 -- whether that flag is set or not.
@@ -60,20 +62,22 @@
 data JITSymbolError = JITSymbolError ShortByteString
   deriving (Show, Eq)
 
-type SymbolResolverFn = MangledSymbol -> IO (Either JITSymbolError JITSymbol)
-
 -- | Specifies how external symbols in a module added to a
--- 'CompielLayer' should be resolved.
-data SymbolResolver =
-  SymbolResolver {
-    -- | This is used to find symbols in the same logical dynamic
-    -- library as the module referencing them.
-    dylibResolver :: !SymbolResolverFn,
-    -- | When 'dylibResolver' fails to resolve a symbol,
-    -- 'externalResolver' is used as a fallback to find external symbols.
-    externalResolver :: !SymbolResolverFn
-  }
+-- 'CompileLayer' should be resolved.
+newtype SymbolResolver =
+  SymbolResolver (MangledSymbol -> IO (Either JITSymbolError JITSymbol))
 
+-- | Create a `FFI.SymbolResolver` that can be used with the JIT.
+withSymbolResolver :: ExecutionSession -> SymbolResolver -> (Ptr FFI.SymbolResolver -> IO a) -> IO a
+withSymbolResolver (ExecutionSession es) (SymbolResolver resolverFn) f =
+  bracket (FFI.wrapSymbolResolverFn resolverFn') freeHaskellFunPtr $ \resolverPtr ->
+    bracket (FFI.createLambdaResolver es resolverPtr) FFI.disposeSymbolResolver $ \resolver ->
+      f resolver
+  where
+    resolverFn' symbol result = do
+      setSymbol <- encodeM =<< resolverFn =<< decodeM symbol
+      setSymbol result
+
 instance Monad m => EncodeM m JITSymbolFlags FFI.JITSymbolFlags where
   encodeM f = return $ foldr1 (.|.) [
       if a f
@@ -117,13 +121,12 @@
         pure (Right (JITSymbol (fromIntegral addr) flags))
 
 instance MonadIO m =>
-  EncodeM m SymbolResolver (IORef [IO ()] -> IO (Ptr FFI.LambdaResolver)) where
-  encodeM (SymbolResolver dylib external) = return $ \cleanups -> do
-    dylib' <- allocFunPtr cleanups (encodeM dylib)
-    external' <- allocFunPtr cleanups (encodeM external)
-    allocWithCleanup cleanups (FFI.createLambdaResolver dylib' external') FFI.disposeLambdaResolver
+  EncodeM m SymbolResolver (IORef [IO ()] -> Ptr FFI.ExecutionSession -> IO (Ptr FFI.SymbolResolver)) where
+  encodeM (SymbolResolver resolverFn) = return $ \cleanups es -> do
+    resolverFn' <- allocFunPtr cleanups (encodeM resolverFn)
+    allocWithCleanup cleanups (FFI.createLambdaResolver es resolverFn') FFI.disposeSymbolResolver
 
-instance MonadIO m => EncodeM m SymbolResolverFn (FunPtr FFI.SymbolResolverFn) where
+instance MonadIO m => EncodeM m (MangledSymbol -> IO (Either JITSymbolError JITSymbol)) (FunPtr FFI.SymbolResolverFn) where
   encodeM callback =
     liftIO $ FFI.wrapSymbolResolverFn
       (\symbol result -> do
@@ -148,3 +151,31 @@
         modifyIORef' cleanups (FFI.disposeDataLayout dl :)
         pure dl
   in anyContToM $ bracketOnError createDataLayout FFI.disposeDataLayout
+
+-- | Create a new `ExecutionSession`.
+createExecutionSession :: IO ExecutionSession
+createExecutionSession = ExecutionSession <$> FFI.createExecutionSession
+
+-- | Dispose of an `ExecutionSession`. This should be called when the
+-- `ExecutionSession` is not needed anymore.
+disposeExecutionSession :: ExecutionSession -> IO ()
+disposeExecutionSession (ExecutionSession es) = FFI.disposeExecutionSession es
+
+-- | `bracket`-style wrapper around `createExecutionSession` and
+-- `disposeExecutionSession`.
+withExecutionSession :: (ExecutionSession -> IO a) -> IO a
+withExecutionSession = bracket createExecutionSession disposeExecutionSession
+
+-- | Allocate a module key for a new module to add to the JIT.
+allocateModuleKey :: ExecutionSession -> IO FFI.ModuleKey
+allocateModuleKey (ExecutionSession es) = FFI.allocateVModule es
+
+-- | Return a module key to the `ExecutionSession` so that it can be
+-- re-used.
+releaseModuleKey :: ExecutionSession -> FFI.ModuleKey -> IO ()
+releaseModuleKey (ExecutionSession es) k = FFI.releaseVModule es k
+
+-- | `bracket`-style wrapper around `allocateModuleKey` and
+-- `releaseModuleKey`.
+withModuleKey :: ExecutionSession -> (FFI.ModuleKey -> IO a) -> IO a
+withModuleKey es = bracket (allocateModuleKey es) (releaseModuleKey es)
diff --git a/src/LLVM/Internal/OrcJIT/CompileLayer.hs b/src/LLVM/Internal/OrcJIT/CompileLayer.hs
--- a/src/LLVM/Internal/OrcJIT/CompileLayer.hs
+++ b/src/LLVM/Internal/OrcJIT/CompileLayer.hs
@@ -1,6 +1,6 @@
 module LLVM.Internal.OrcJIT.CompileLayer
   ( module LLVM.Internal.OrcJIT.CompileLayer
-  , FFI.ModuleHandle
+  , FFI.ModuleKey
   ) where
 
 import LLVM.Prelude
@@ -54,7 +54,7 @@
 -- | @'findSymbolIn' layer handle symbol exportedSymbolsOnly@ searches for
 -- @symbol@ in the context of the module represented by @handle@. If
 -- @exportedSymbolsOnly@ is 'True' only exported symbols are searched.
-findSymbolIn :: CompileLayer l => l -> FFI.ModuleHandle -> MangledSymbol -> Bool -> IO (Either JITSymbolError JITSymbol)
+findSymbolIn :: CompileLayer l => l -> FFI.ModuleKey -> MangledSymbol -> Bool -> IO (Either JITSymbolError JITSymbol)
 findSymbolIn compileLayer handle symbol exportedSymbolsOnly = flip runAnyContT return $ do
   symbol' <- encodeM symbol
   exportedSymbolsOnly' <- encodeM exportedSymbolsOnly
@@ -67,10 +67,8 @@
 --
 -- /Note:/ This function consumes the module passed to it and it must
 -- not be used after calling this method.
-addModule :: CompileLayer l => l -> Module -> SymbolResolver -> IO FFI.ModuleHandle
-addModule compileLayer mod resolver = flip runAnyContT return $ do
-  resolverAct <- encodeM resolver
-  resolver' <- liftIO $ resolverAct (getCleanups compileLayer)
+addModule :: CompileLayer l => l -> FFI.ModuleKey -> Module -> IO ()
+addModule compileLayer k mod = flip runAnyContT return $ do
   mod' <- liftIO $ readModule mod
   liftIO $ deleteModule mod
   errMsg <- alloca
@@ -78,12 +76,12 @@
     FFI.addModule
       (getCompileLayer compileLayer)
       (getDataLayout compileLayer)
+      k
       mod'
-      resolver'
       errMsg
 
 -- | Remove a previously added module.
-removeModule :: CompileLayer l => l -> FFI.ModuleHandle -> IO ()
+removeModule :: CompileLayer l => l -> FFI.ModuleKey -> IO ()
 removeModule compileLayer handle =
   FFI.removeModule (getCompileLayer compileLayer) handle
 
@@ -91,11 +89,11 @@
 --
 -- /Note:/ This function consumes the module passed to it and it must
 -- not be used after calling this method.
-withModule :: CompileLayer l => l -> Module -> SymbolResolver -> (FFI.ModuleHandle -> IO a) -> IO a
-withModule compileLayer mod resolver =
-  bracket
-    (addModule compileLayer mod resolver)
-    (removeModule compileLayer)
+withModule :: CompileLayer l => l -> FFI.ModuleKey -> Module -> IO a -> IO a
+withModule compileLayer k mod =
+  bracket_
+    (addModule compileLayer k mod)
+    (removeModule compileLayer k)
 
 -- | Dispose of a 'CompileLayer'. This should called when the
 -- 'CompileLayer' is not needed anymore.
diff --git a/src/LLVM/Internal/OrcJIT/CompileOnDemandLayer.hs b/src/LLVM/Internal/OrcJIT/CompileOnDemandLayer.hs
--- a/src/LLVM/Internal/OrcJIT/CompileOnDemandLayer.hs
+++ b/src/LLVM/Internal/OrcJIT/CompileOnDemandLayer.hs
@@ -101,13 +101,14 @@
 -- When the callback manager is no longer needed, it should be freed
 -- using 'disposeJITCompileCallbackManager'.
 newJITCompileCallbackManager ::
+  ExecutionSession ->
   ShortByteString {- ^ target triple -} ->
   Maybe (IO ()) {- ^ called on compilation errors -} ->
   IO JITCompileCallbackManager
-newJITCompileCallbackManager triple errorHandler = flip runAnyContT return $ do
+newJITCompileCallbackManager (ExecutionSession es) triple errorHandler = flip runAnyContT return $ do
   triple' <- encodeM triple
   (errorHandler', cleanup) <- encodeM errorHandler
-  callbackMgr <- liftIO (FFI.createLocalCompileCallbackManager triple' errorHandler')
+  callbackMgr <- liftIO (FFI.createLocalCompileCallbackManager es triple' errorHandler')
   return (CallbackMgr callbackMgr cleanup)
 
 -- | Dispose of a 'JITCompileCallbackManager'.
@@ -117,13 +118,14 @@
 
 -- | Execute a computation using a new 'JITCompileCallbackManager'.
 withJITCompileCallbackManager ::
+  ExecutionSession ->
   ShortByteString {- ^ target triple -} ->
   Maybe (IO ()) {- ^ called on compilation errors -} ->
   (JITCompileCallbackManager -> IO a) ->
   IO a
-withJITCompileCallbackManager triple errorHandler =
+withJITCompileCallbackManager es triple errorHandler =
   bracket
-    (newJITCompileCallbackManager triple errorHandler)
+    (newJITCompileCallbackManager es triple errorHandler)
     disposeJITCompileCallbackManager
 
 -- | Create a new 'CompileOnDemandLayer'. The partitioning function
@@ -132,24 +134,32 @@
 --
 -- When the layer is no longer needed, it should be disposed using 'disposeCompileLayer'.
 newCompileOnDemandLayer :: CompileLayer l =>
+  ExecutionSession ->
   l ->
   TargetMachine ->
+  (ModuleKey -> IO (Ptr FFI.SymbolResolver)) ->
+  (ModuleKey -> Ptr FFI.SymbolResolver -> IO ()) ->
   (Ptr FFI.Function -> IO [Ptr FFI.Function]) {- ^ partitioning function -} ->
   JITCompileCallbackManager ->
   IndirectStubsManagerBuilder ->
   Bool {- ^ clone stubs into partitions -} ->
   IO (CompileOnDemandLayer l)
-newCompileOnDemandLayer baseLayer tm partition (CallbackMgr callbackMgr _) (StubsMgr stubsMgr) cloneStubs =
+newCompileOnDemandLayer (ExecutionSession es) baseLayer tm getSymbolResolver setSymbolResolver partition (CallbackMgr callbackMgr _) (StubsMgr stubsMgr) cloneStubs =
   flip runAnyContT return $ do
     cleanups <- liftIO (newIORef [])
     dl <- createRegisteredDataLayout tm cleanups
+    getSymbolResolver' <- liftIO (allocFunPtr cleanups (FFI.wrapGetSymbolResolver getSymbolResolver))
+    setSymbolResolver' <- liftIO (allocFunPtr cleanups (FFI.wrapSetSymbolResolver setSymbolResolver))
     partitionAct <- encodeM partition
     partition' <- liftIO $ partitionAct cleanups
     cloneStubs' <- encodeM cloneStubs
     cl <-
       liftIO
         (FFI.createCompileOnDemandLayer
+           es
            (getCompileLayer baseLayer)
+           getSymbolResolver'
+           setSymbolResolver'
            partition'
            callbackMgr
            stubsMgr
@@ -159,15 +169,18 @@
 -- | 'bracket'-style wrapper around 'newCompileOnDemandLayer' and 'disposeCompileLayer'.
 withCompileOnDemandLayer ::
   CompileLayer l =>
+  ExecutionSession ->
   l ->
   TargetMachine ->
+  (ModuleKey -> IO (Ptr FFI.SymbolResolver)) ->
+  (ModuleKey -> Ptr FFI.SymbolResolver -> IO ()) ->
   (Ptr FFI.Function -> IO [Ptr FFI.Function]) {- ^ partitioning function -} ->
   JITCompileCallbackManager ->
   IndirectStubsManagerBuilder ->
   Bool {- ^ clone stubs into partitions -} ->
   (CompileOnDemandLayer l -> IO a) ->
   IO a
-withCompileOnDemandLayer l tm partition callbackMgr stubsMgr cloneStubs =
+withCompileOnDemandLayer es l tm getSymbolResolver setSymbolResolver partition callbackMgr stubsMgr cloneStubs =
   bracket
-    (newCompileOnDemandLayer l tm partition callbackMgr stubsMgr cloneStubs)
+    (newCompileOnDemandLayer es l tm getSymbolResolver setSymbolResolver partition callbackMgr stubsMgr cloneStubs)
     disposeCompileLayer
diff --git a/src/LLVM/Internal/OrcJIT/LinkingLayer.hs b/src/LLVM/Internal/OrcJIT/LinkingLayer.hs
--- a/src/LLVM/Internal/OrcJIT/LinkingLayer.hs
+++ b/src/LLVM/Internal/OrcJIT/LinkingLayer.hs
@@ -28,19 +28,15 @@
   FFI.disposeLinkingLayer (getLinkingLayer l)
   sequence_ =<< readIORef (getCleanups l)
 
--- | Add an object file to the 'LinkingLayer'. The 'SymbolResolver' is used
--- to resolve external symbols in the module.
-addObjectFile :: LinkingLayer l => l -> ObjectFile -> SymbolResolver
-              -> IO FFI.ObjectHandle
-addObjectFile linkingLayer (ObjectFile obj) resolver = flip runAnyContT return $ do
-  resolverAct <- encodeM resolver
-  resolver'   <- liftIO $ resolverAct (getCleanups linkingLayer)
+-- | Add an object file to the 'LinkingLayer'.
+addObjectFile :: LinkingLayer l => l -> FFI.ModuleKey -> ObjectFile -> IO ()
+addObjectFile linkingLayer k (ObjectFile obj) = flip runAnyContT return $ do
   errMsg <- alloca
   liftIO $
     FFI.addObjectFile
       (getLinkingLayer linkingLayer)
+      k
       obj
-      resolver'
       errMsg
 
 -- | Bare bones implementation of a 'LinkingLayer'.
@@ -55,15 +51,16 @@
 
 -- | Create a new 'ObjectLinkingLayer'. This should be disposed using
 -- 'disposeLinkingLayer' when it is no longer needed.
-newObjectLinkingLayer :: IO ObjectLinkingLayer
-newObjectLinkingLayer = do
-  linkingLayer <- FFI.createObjectLinkingLayer
+newObjectLinkingLayer :: ExecutionSession -> (FFI.ModuleKey -> IO (Ptr FFI.SymbolResolver)) -> IO ObjectLinkingLayer
+newObjectLinkingLayer (ExecutionSession es) getResolver = do
   cleanups <- liftIO (newIORef [])
+  getResolver' <- allocFunPtr cleanups (FFI.wrapGetSymbolResolver getResolver)
+  linkingLayer <- FFI.createObjectLinkingLayer es getResolver'
   return $ ObjectLinkingLayer linkingLayer cleanups
 
 -- | 'bracket'-style wrapper around 'newObjectLinkingLayer' and 'disposeLinkingLayer'.
-withObjectLinkingLayer :: (ObjectLinkingLayer -> IO a) -> IO a
-withObjectLinkingLayer = bracket newObjectLinkingLayer disposeLinkingLayer
+withObjectLinkingLayer :: ExecutionSession -> (FFI.ModuleKey -> IO (Ptr FFI.SymbolResolver)) -> (ObjectLinkingLayer -> IO a) -> IO a
+withObjectLinkingLayer es resolver = bracket (newObjectLinkingLayer es resolver) disposeLinkingLayer
 
 -- | @'findSymbol' layer symbol exportedSymbolsOnly@ searches for
 -- @symbol@ in all modules added to @layer@. If @exportedSymbolsOnly@
@@ -80,7 +77,7 @@
 -- | @'findSymbolIn' layer handle symbol exportedSymbolsOnly@ searches for
 -- @symbol@ in the context of the module represented by @handle@. If
 -- @exportedSymbolsOnly@ is 'True' only exported symbols are searched.
-findSymbolIn :: LinkingLayer l => l -> FFI.ObjectHandle -> ShortByteString -> Bool -> IO (Either JITSymbolError JITSymbol)
+findSymbolIn :: LinkingLayer l => l -> FFI.ModuleKey -> ShortByteString -> Bool -> IO (Either JITSymbolError JITSymbol)
 findSymbolIn linkingLayer handle symbol exportedSymbolsOnly =
   SBS.useAsCString symbol $ \symbol' ->
     flip runAnyContT return $ do
diff --git a/src/LLVM/OrcJIT.hs b/src/LLVM/OrcJIT.hs
--- a/src/LLVM/OrcJIT.hs
+++ b/src/LLVM/OrcJIT.hs
@@ -2,7 +2,7 @@
     -- * CompileLayer
     CompileLayer,
     -- ** Add/remove modules
-    ModuleHandle,
+    ModuleKey,
     addModule,
     removeModule,
     withModule,
@@ -12,9 +12,18 @@
     JITSymbolFlags(..),
     defaultJITSymbolFlags,
     SymbolResolver(..),
+    withSymbolResolver,
     -- ** Symbol mangling
     MangledSymbol,
     mangleSymbol,
+    -- ** ExecutionSession
+    ExecutionSession,
+    createExecutionSession,
+    disposeExecutionSession,
+    withExecutionSession,
+    allocateModuleKey,
+    releaseModuleKey,
+    withModuleKey,
     -- ** IRCompileLayer
     IRCompileLayer,
     newIRCompileLayer,
diff --git a/test/LLVM/Test/Constants.hs b/test/LLVM/Test/Constants.hs
--- a/test/LLVM/Test/Constants.hs
+++ b/test/LLVM/Test/Constants.hs
@@ -80,8 +80,8 @@
     ), (
       "x86_fp80",
       x86_fp80,
-      C.Float (F.X86_FP80 0x0004 0x0003000200010000),
-      "global x86_fp80 0xK00040003000200010000"
+      C.Float (F.X86_FP80 0x0004 0x8003000200010000),
+      "global x86_fp80 0xK00048003000200010000"
 {- don't know how to test this - LLVM's handling of this weird type is even weirder
     ), (
       "ppc_fp128",
diff --git a/test/LLVM/Test/Metadata.hs b/test/LLVM/Test/Metadata.hs
--- a/test/LLVM/Test/Metadata.hs
+++ b/test/LLVM/Test/Metadata.hs
@@ -84,8 +84,12 @@
       , UnsignedCharEncoding
       ]
 
-instance Arbitrary ChecksumKind where
-  arbitrary = QC.elements [None, MD5, SHA1]
+instance Arbitrary ChecksumInfo where
+  arbitrary =
+    oneof
+      [ ChecksumInfo MD5 . BSS.pack <$> QC.vector 32
+      , ChecksumInfo SHA1 . BSS.pack <$> QC.vector 40
+      ]
 
 instance Arbitrary BasicTypeTag where
   arbitrary = QC.elements [BaseType, UnspecifiedType]
@@ -244,13 +248,17 @@
 
 instance Arbitrary DIFile where
   arbitrary =
-    O.File <$> arbitrarySbs <*> arbitrarySbs <*> arbitrarySbs <*> arbitrary
+    O.File <$> arbitrarySbs <*> arbitrarySbs <*> arbitrary
 
 instance Arbitrary DISubrange where
   arbitrary = Subrange <$> arbitrary <*> arbitrary
 
+instance Arbitrary DICount where
+  -- TODO Include DICountVariable
+  arbitrary = DICountConstant <$> arbitrary
+
 instance Arbitrary DIEnumerator where
-  arbitrary = Enumerator <$> arbitrary <*> arbitrarySbs
+  arbitrary = Enumerator <$> arbitrary <*> arbitrary <*> arbitrarySbs
 
 instance Arbitrary DINode where
   arbitrary =
@@ -856,7 +864,7 @@
                       , unit = Just (MDRef (MetadataNodeID 2))
                       , O.templateParams = []
                       , declaration = Nothing
-                      , variables = []
+                      , retainedNodes = []
                       , thrownTypes = []
                       }
                   , MetadataNodeDefinition (MetadataNodeID 1)
@@ -887,7 +895,7 @@
                       }
                   , MetadataNodeDefinition (MetadataNodeID 3) $
                     DINode . DIScope . DIFile $
-                    O.File "main.c" "/" "" None
+                    O.File "main.c" "/" Nothing
                   ]
           s =
             "; ModuleID = '<string>'\n\
@@ -963,7 +971,7 @@
                   , MetadataNodeDefinition (MetadataNodeID 4) (DIExpression (Expression []))
                   , MetadataNodeDefinition (MetadataNodeID 5) $
                     DINode . DIScope . DIFile $
-                    O.File "main.c" "/" "" None
+                    O.File "main.c" "/" Nothing
                   , MetadataNodeDefinition (MetadataNodeID 6) $
                     DINode . DIScope . DIType . DIBasicType $
                     BasicType "" 0 0 Nothing BaseType
diff --git a/test/LLVM/Test/OrcJIT.hs b/test/LLVM/Test/OrcJIT.hs
--- a/test/LLVM/Test/OrcJIT.hs
+++ b/test/LLVM/Test/OrcJIT.hs
@@ -6,6 +6,7 @@
 
 import LLVM.Test.Support
 
+import qualified Data.Map.Strict as Map
 import Control.Applicative
 import Data.ByteString (ByteString)
 import Data.Foldable
@@ -50,17 +51,18 @@
 foreign import ccall "dynamic"
   mkMain :: FunPtr (IO Word32) -> IO Word32
 
-nullResolver :: MangledSymbol -> IO (Either JITSymbolError JITSymbol)
-nullResolver s = putStrLn "nullresolver" >> return (Left (JITSymbolError "unknown symbol"))
-
 resolver :: CompileLayer l => MangledSymbol -> l -> MangledSymbol -> IO (Either JITSymbolError JITSymbol)
-resolver testFunc compileLayer symbol
-  | symbol == testFunc = do
+resolver testFunc compileLayer symbol = do
+  if symbol /= testFunc
+    then CL.findSymbol compileLayer symbol True
+    else do
       funPtr <- wrapTestFunc myTestFuncImpl
       let addr = ptrToWordPtr (castFunPtrToPtr funPtr)
       return (Right (JITSymbol addr defaultJITSymbolFlags))
-  | otherwise = CL.findSymbol compileLayer symbol True
 
+nullResolver :: MangledSymbol -> IO (Either JITSymbolError JITSymbol)
+nullResolver s = putStrLn "nullresolver" >> return (Left (JITSymbolError "unknown symbol"))
+
 moduleTransform :: IORef Bool -> Ptr FFI.Module -> IO (Ptr FFI.Module)
 moduleTransform passmanagerSuccessful modulePtr = do
   withPassManager defaultCuratedPassSetSpec { optLevel = Just 2 } $ \(PassManager pm) -> do
@@ -72,80 +74,87 @@
 tests =
   testGroup "OrcJit" [
     testCase "eager compilation" $ do
+      resolvers <- newIORef Map.empty
       withTestModule $ \mod ->
         withHostTargetMachine $ \tm ->
-          withObjectLinkingLayer $ \linkingLayer ->
-            withIRCompileLayer linkingLayer tm $ \compileLayer -> do
-              testFunc <- mangleSymbol compileLayer "testFunc"
-              withModule
-                compileLayer
-                mod
-                (SymbolResolver (resolver testFunc compileLayer) nullResolver) $
-                \moduleHandle -> do
-                  mainSymbol <- mangleSymbol compileLayer "main"
-                  Right (JITSymbol mainFn _) <- CL.findSymbol compileLayer mainSymbol True
-                  result <- mkMain (castPtrToFunPtr (wordPtrToPtr mainFn))
-                  result @?= 42
-                  Right (JITSymbol mainFn _) <- CL.findSymbolIn compileLayer moduleHandle mainSymbol True
-                  result <- mkMain (castPtrToFunPtr (wordPtrToPtr mainFn))
-                  result @?= 42
-                  unknownSymbol <- mangleSymbol compileLayer "unknownSymbol"
-                  unknownSymbolRes <- CL.findSymbol compileLayer unknownSymbol True
-                  unknownSymbolRes @?= Left (JITSymbolError mempty),
+        withExecutionSession $ \es ->
+        withObjectLinkingLayer es (\k -> fmap (\rs -> rs Map.! k) (readIORef resolvers)) $ \linkingLayer ->
+        withIRCompileLayer linkingLayer tm $ \compileLayer -> do
+          testFunc <- mangleSymbol compileLayer "testFunc"
+          withModuleKey es $ \k ->
+            withSymbolResolver es (SymbolResolver (resolver testFunc compileLayer)) $ \resolver -> do
+              modifyIORef' resolvers (Map.insert k resolver)
+              withModule compileLayer k mod $ do
+                mainSymbol <- mangleSymbol compileLayer "main"
+                Right (JITSymbol mainFn _) <- CL.findSymbol compileLayer mainSymbol True
+                result <- mkMain (castPtrToFunPtr (wordPtrToPtr mainFn))
+                result @?= 42
+                Right (JITSymbol mainFn _) <- CL.findSymbolIn compileLayer k mainSymbol True
+                result <- mkMain (castPtrToFunPtr (wordPtrToPtr mainFn))
+                result @?= 42
+                unknownSymbol <- mangleSymbol compileLayer "unknownSymbol"
+                unknownSymbolRes <- CL.findSymbol compileLayer unknownSymbol True
+                unknownSymbolRes @?= Left (JITSymbolError mempty),
 
     testCase "IRTransformLayer" $ do
       passmanagerSuccessful <- newIORef False
+      resolvers <- newIORef Map.empty
       withTestModule $ \mod ->
         withHostTargetMachine $ \tm ->
-          withObjectLinkingLayer $ \linkingLayer ->
-            withIRCompileLayer linkingLayer tm $ \compileLayer -> do
-              withIRTransformLayer compileLayer tm (moduleTransform passmanagerSuccessful) $ \compileLayer -> do
-                testFunc <- mangleSymbol compileLayer "testFunc"
-                withModule
-                  compileLayer
-                  mod
-                  (SymbolResolver (resolver testFunc compileLayer) nullResolver) $
-                  \moduleHandle -> do
-                    mainSymbol <- mangleSymbol compileLayer "main"
-                    Right (JITSymbol mainFn _) <- CL.findSymbol compileLayer mainSymbol True
-                    result <- mkMain (castPtrToFunPtr (wordPtrToPtr mainFn))
-                    result @?= 42
-                    readIORef passmanagerSuccessful @? "passmanager failed",
+        withExecutionSession $ \es ->
+        withObjectLinkingLayer es (\k -> fmap (\rs -> rs Map.! k) (readIORef resolvers)) $ \linkingLayer ->
+        withIRCompileLayer linkingLayer tm $ \compileLayer ->
+        withIRTransformLayer compileLayer tm (moduleTransform passmanagerSuccessful) $ \compileLayer ->
+        withModuleKey es $ \k -> do
+          testFunc <- mangleSymbol compileLayer "testFunc"
+          withSymbolResolver es (SymbolResolver (resolver testFunc compileLayer)) $ \resolver -> do
+            modifyIORef' resolvers (Map.insert k resolver)
+            withModule compileLayer k mod $ do
+              mainSymbol <- mangleSymbol compileLayer "main"
+              Right (JITSymbol mainFn _) <- CL.findSymbol compileLayer mainSymbol True
+              result <- mkMain (castPtrToFunPtr (wordPtrToPtr mainFn))
+              result @?= 42
+              readIORef passmanagerSuccessful @? "passmanager failed",
 
     testCase "lazy compilation" $ do
+      resolvers <- newIORef Map.empty
+      let getResolver k = fmap (Map.! k) (readIORef resolvers)
+          setResolver k r = modifyIORef' resolvers (Map.insert k r)
       withTestModule $ \mod ->
         withHostTargetMachine $ \tm -> do
           triple <- getTargetMachineTriple tm
-          withObjectLinkingLayer $ \linkingLayer ->
+          withExecutionSession $ \es ->
+            withObjectLinkingLayer es getResolver $ \linkingLayer ->
             withIRCompileLayer linkingLayer tm $ \baseLayer ->
-              withIndirectStubsManagerBuilder triple $ \stubsMgr ->
-                withJITCompileCallbackManager triple Nothing $ \callbackMgr ->
-                  withCompileOnDemandLayer baseLayer tm (\x -> return [x]) callbackMgr stubsMgr False $ \compileLayer -> do
-                    testFunc <- mangleSymbol compileLayer "testFunc"
-                    withModule
-                      compileLayer
-                      mod
-                      (SymbolResolver (resolver testFunc compileLayer) nullResolver) $
-                      \moduleHandle -> do
-                        mainSymbol <- mangleSymbol compileLayer "main"
-                        Right (JITSymbol mainFn _) <- CL.findSymbol compileLayer mainSymbol True
-                        result <- mkMain (castPtrToFunPtr (wordPtrToPtr mainFn))
-                        result @?= 42,
+            withIndirectStubsManagerBuilder triple $ \stubsMgr ->
+            withJITCompileCallbackManager es triple Nothing $ \callbackMgr ->
+            withCompileOnDemandLayer es baseLayer tm getResolver setResolver (\x -> return [x]) callbackMgr stubsMgr False $ \compileLayer -> do
+              testFunc <- mangleSymbol compileLayer "testFunc"
+              withModuleKey es $ \k ->
+                withSymbolResolver es (SymbolResolver (resolver testFunc baseLayer)) $ \resolver -> do
+                  setResolver k resolver
+                  withModule compileLayer k mod $ do
+                    mainSymbol <- mangleSymbol compileLayer "main"
+                    Right (JITSymbol mainFn _) <- CL.findSymbol compileLayer mainSymbol True
+                    result <- mkMain (castPtrToFunPtr (wordPtrToPtr mainFn))
+                    result @?= 42,
 
-    testCase "finding symbols in linking layer" $ do
-      withObjectLinkingLayer $ \linkingLayer -> do
+    testCase "finding symbols in linking layer" $
+      withExecutionSession $ \es ->
+      withModuleKey es $ \k ->
+      withSymbolResolver es (SymbolResolver nullResolver) $ \resolver ->
+      withObjectLinkingLayer es (\_ -> pure resolver) $ \linkingLayer -> do
         let inputPath = "./test/main_return_38.c"
         withSystemTempFile "main.o" $ \outputPath _ -> do
           callProcess "gcc" ["-c", inputPath, "-o", outputPath]
           withObjectFile outputPath $ \objFile -> do
-            let resl = SymbolResolver nullResolver nullResolver
-            objectHandle <- addObjectFile linkingLayer objFile resl
+            addObjectFile linkingLayer k objFile
             -- Find main symbol by looking into global linking context
             Right (JITSymbol mainFn _) <- LL.findSymbol linkingLayer "main" True
             result <- mkMain (castPtrToFunPtr (wordPtrToPtr mainFn))
             result @?= 38
             -- Find main symbol by specificly using object handle for given object file
-            Right (JITSymbol mainFn _) <- LL.findSymbolIn linkingLayer objectHandle "main" True
+            Right (JITSymbol mainFn _) <- LL.findSymbolIn linkingLayer k "main" True
             result <- mkMain (castPtrToFunPtr (wordPtrToPtr mainFn))
             result @?= 38
     ]
diff --git a/test/LLVM/Test/Regression.hs b/test/LLVM/Test/Regression.hs
--- a/test/LLVM/Test/Regression.hs
+++ b/test/LLVM/Test/Regression.hs
@@ -100,6 +100,82 @@
       ]
   }
 
+duplicateDefinitions :: AST.Module
+duplicateDefinitions =
+  defaultModule
+    { moduleName = "clashingModule"
+    , moduleDefinitions =
+        [ GlobalDefinition
+            functionDefaults
+              { name = "clashy"
+              , returnType = i64
+              , basicBlocks =
+                  [ BasicBlock
+                      "entry"
+                      [ mkName "c" :=
+                        Add
+                          False
+                          False
+                          (ConstantOperand (C.Int 64 1))
+                          (ConstantOperand (C.Int 64 2))
+                          []
+                      ]
+                      (Do (Br "next" []))
+                  , BasicBlock
+                      "next"
+                      [ mkName "c" :=
+                        Phi i64 [(LocalReference i64 "c", "entry")] []
+                      ]
+                      (Do (Ret (Just (LocalReference i64 "c")) []))
+                  ]
+              }
+        ]
+    }
+
+reuseAcrossFunctions :: AST.Module
+reuseAcrossFunctions =
+  defaultModule
+    { moduleName = "<string>"
+    , moduleDefinitions =
+        [ GlobalDefinition
+            functionDefaults
+              { name = "f"
+              , returnType = i64
+              , basicBlocks =
+                  [ BasicBlock
+                      "entry"
+                      [ "c" :=
+                        Add
+                          False
+                          False
+                          (ConstantOperand (C.Int 64 1))
+                          (ConstantOperand (C.Int 64 2))
+                          []
+                      ]
+                      (Do (Ret (Just (LocalReference i64 "c")) []))
+                  ]
+              }
+        , GlobalDefinition
+            functionDefaults
+              { name = "g"
+              , returnType = i64
+              , basicBlocks =
+                  [ BasicBlock
+                      "entry"
+                      [ "c" :=
+                        Add
+                          False
+                          False
+                          (ConstantOperand (C.Int 64 1))
+                          (ConstantOperand (C.Int 64 2))
+                          []
+                      ]
+                      (Do (Ret (Just (LocalReference i64 "c")) []))
+                  ]
+              }
+        ]
+    }
+
 shouldThrowEncodeException :: AST.Module -> String -> IO ()
 shouldThrowEncodeException ast errMsg = do
   result <- try $ withContext $ \context -> do
@@ -108,6 +184,11 @@
     Left (EncodeException actualErrMsg) -> actualErrMsg @?= errMsg
     Right _ -> assertFailure ("Expected serialization to fail with: \"" ++ errMsg ++ "\"")
 
+shouldNotThrow :: AST.Module -> IO ()
+shouldNotThrow ast = do
+  withContext $ \context -> do
+    withModuleFromAST context ast (\_ -> return ())
+
 tests :: TestTree
 tests =
   testGroup
@@ -124,4 +205,11 @@
         "null constants must have pointer type"
         (example3 `shouldThrowEncodeException`
           "Null pointer constant must have pointer type but has type IntegerType {typeBits = 32}.")
+    , testCase
+        "Duplicate definitions are not allowed"
+        (duplicateDefinitions `shouldThrowEncodeException`
+          "Duplicate definition of local variable: Name \"c\".")
+    , testCase
+        "Reusing variable names across functions is allowed"
+        (shouldNotThrow reuseAcrossFunctions)
     ]
diff --git a/test/debug_metadata_2.ll b/test/debug_metadata_2.ll
--- a/test/debug_metadata_2.ll
+++ b/test/debug_metadata_2.ll
@@ -31,7 +31,7 @@
 !6 = !{i32 7, !"PIC Level", i32 2}
 !7 = !{i32 7, !"PIE Level", i32 2}
 !8 = !{!"clang version 5.0.1 (tags/RELEASE_501/final)"}
-!9 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 1, type: !10, isLocal: false, isDefinition: true, scopeLine: 1, flags: DIFlagPrototyped, isOptimized: false, unit: !0, variables: !2)
+!9 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 1, type: !10, isLocal: false, isDefinition: true, scopeLine: 1, flags: DIFlagPrototyped, isOptimized: false, unit: !0, retainedNodes: !2)
 !10 = !DISubroutineType(types: !11)
 !11 = !{!12, !12}
 !12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
