diff --git a/cmake/modules/RootNewMacros.cmake b/cmake/modules/RootNewMacros.cmake
index 4b1a5090ef5e7df148b0391dd3be70593f9cd7c1..03f4838e6a7ada933f2cfecb48f303fcd6d927f2 100644
--- a/cmake/modules/RootNewMacros.cmake
+++ b/cmake/modules/RootNewMacros.cmake
@@ -358,24 +358,27 @@ function(ROOT_GENERATE_DICTIONARY dictionary)
   #---Set the library output directory-----------------------
   ROOT_GET_LIBRARY_OUTPUT_DIR(library_output_dir)
   if(ARG_MODULE)
+    set(cpp_module)
     set(library_name ${libprefix}${ARG_MODULE}${libsuffix})
+    set(newargs -s ${library_output_dir}/${library_name})
+    set(master_pcm_name ${library_output_dir}/${libprefix}${ARG_MODULE}_rdict.pcm)
     if(ARG_MULTIDICT)
-      set(newargs -s ${library_output_dir}/${library_name} -multiDict)
+      set(newargs ${newargs} -multiDict)
       set(pcm_name ${library_output_dir}/${libprefix}${ARG_MODULE}_${dictionary}_rdict.pcm)
       set(rootmap_name ${library_output_dir}/${libprefix}${deduced_arg_module}.rootmap)
     else()
-      set(newargs -s ${library_output_dir}/${library_name})
-      set(pcm_name ${library_output_dir}/${libprefix}${ARG_MODULE}_rdict.pcm)
-      set(rootmap_name ${library_output_dir}/${libprefix}${ARG_MODULE}.rootmap)
       set(cpp_module ${ARG_MODULE})
-      if(runtime_cxxmodules)
-        set(cpp_module_file ${library_output_dir}/${cpp_module}.pcm)
+      set(pcm_name ${master_pcm_name})
+      set(rootmap_name ${library_output_dir}/${libprefix}${ARG_MODULE}.rootmap)
+    endif(ARG_MULTIDICT)
+
+    if(runtime_cxxmodules AND cpp_module)
+      set(cpp_module_file ${library_output_dir}/${cpp_module}.pcm)
         if (APPLE)
           if (${cpp_module} MATCHES "(GCocoa|GQuartz)")
             set(cpp_module_file)
           endif()
         endif(APPLE)
-      endif()
     endif()
   else()
     set(library_name ${libprefix}${deduced_arg_module}${libsuffix})
@@ -445,6 +448,14 @@ function(ROOT_GENERATE_DICTIONARY dictionary)
   #---roottest compability
   add_custom_target(${dictname} DEPENDS ${dictionary}.cxx ${pcm_name} ${rootmap_name} ${cpp_module_file})
 
+  if (runtime_cxxmodules AND ARG_MULTIDICT)
+    set (main_pcm_target "G__${ARG_MODULE}")
+    if (NOT TARGET ${main_pcm_target})
+      message(FATAL_ERROR "Target ${main_pcm_target} not found! Please move ${main_pcm_target} before specifying MULTIDICT.")
+    endif()
+    add_dependencies(${main_pcm_target} ${dictname})
+  endif()
+
   if(NOT ARG_NOINSTALL AND NOT CMAKE_ROOTTEST_DICT AND DEFINED CMAKE_LIBRARY_OUTPUT_DIRECTORY)
     set_property(GLOBAL APPEND PROPERTY ROOT_DICTIONARY_TARGETS ${dictname})
     set_property(GLOBAL APPEND PROPERTY ROOT_DICTIONARY_FILES ${CMAKE_CURRENT_BINARY_DIR}/${dictionary}.cxx)
diff --git a/core/dictgen/src/LinkdefReader.cxx b/core/dictgen/src/LinkdefReader.cxx
index 1d389a06ae3079a11c38d32b5855b14413a545fe..49f2ece2f37b0dfe83c5e37701c61d24d7037ae2 100644
--- a/core/dictgen/src/LinkdefReader.cxx
+++ b/core/dictgen/src/LinkdefReader.cxx
@@ -1014,7 +1014,8 @@ bool LinkdefReader::Parse(SelectionRules &sr, llvm::StringRef code, const std::v
    // Extract all #pragmas
    std::unique_ptr<llvm::MemoryBuffer> memBuf = llvm::MemoryBuffer::getMemBuffer(code, "CLING #pragma extraction");
    clang::CompilerInstance *pragmaCI = cling::CIFactory::createCI(std::move(memBuf), parserArgsC.size(),
-                                                                  &parserArgsC[0], llvmdir, nullptr, true /*OnlyLex*/);
+                                                                  &parserArgsC[0], llvmdir, nullptr /*Consumer*/,
+                                                                  {} /*ModuleFileExtension*/, true /*OnlyLex*/);
 
    clang::Preprocessor &PP = pragmaCI->getPreprocessor();
    clang::DiagnosticConsumer &DClient = pragmaCI->getDiagnosticClient();
diff --git a/core/metacling/src/CMakeLists.txt b/core/metacling/src/CMakeLists.txt
index 97a395136e2775b6f762f2b9071dfbae675dc004..c6015d49ceb696b8a58d5de08a0ca2fb0bbde445 100644
--- a/core/metacling/src/CMakeLists.txt
+++ b/core/metacling/src/CMakeLists.txt
@@ -21,8 +21,10 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CLING_CXXFLAGS}")
 
 if(MSVC)
   set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/TClingCallbacks.cxx COMPILE_FLAGS -GR-)
+  set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/TClingRdictModuleFileExtension.cxx COMPILE_FLAGS -GR-)
 else()
   set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/TClingCallbacks.cxx COMPILE_FLAGS -fno-rtti)
+  set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/TClingRdictModuleFileExtension.cxx COMPILE_FLAGS -fno-rtti)
 endif()
 
 # This to avoid warning coming from llvm/src/tools/clang/include/clang/Sema/Lookup.h:441
@@ -47,6 +49,7 @@ ROOT_OBJECT_LIBRARY(MetaCling
   TClingDeclInfo.cxx
   TClingMethodArgInfo.cxx
   TClingMethodInfo.cxx
+  TClingRdictModuleFileExtension.cxx
   TClingTypedefInfo.cxx
   TClingTypeInfo.cxx
   TClingValue.cxx
diff --git a/core/metacling/src/TCling.cxx b/core/metacling/src/TCling.cxx
index b915fb4bdd02d8623ed6d02660d1d531acea6579..715b234040b42938294a8cd5dcfdc355be1d3e85 100644
--- a/core/metacling/src/TCling.cxx
+++ b/core/metacling/src/TCling.cxx
@@ -25,6 +25,7 @@ clang/LLVM technology.
 #include "TClingDataMemberInfo.h"
 #include "TClingMethodArgInfo.h"
 #include "TClingMethodInfo.h"
+#include "TClingRdictModuleFileExtension.h"
 #include "TClingTypedefInfo.h"
 #include "TClingTypeInfo.h"
 #include "TClingValue.h"
@@ -1340,9 +1341,12 @@ TCling::TCling(const char *name, const char *title, const char* const argv[])
       interpArgs.push_back(arg.c_str());
    }
 
+   // Add the Rdict module file extension.
+   extensions.push_back(std::make_shared<TClingRdictModuleFileExtension>(""));
+
    fInterpreter = new cling::Interpreter(interpArgs.size(),
                                          &(interpArgs[0]),
-                                         llvmResourceDir);
+                                         llvmResourceDir, extensions);
 
    if (!fromRootCling) {
       fInterpreter->installLazyFunctionCreator(llvmLazyFunctionCreator);
@@ -1493,6 +1497,122 @@ static bool R__InitStreamerInfoFactory()
    return doneFactory; // avoid unused variable warning.
 }
 
+////////////////////////////////////////////////////////////////////////////////
+/// Tries to load a PCM from TFile; returns true on success.
+bool TCling::LoadPCMImpl(TFile *pcmFile)
+{
+
+   auto listOfKeys = pcmFile->GetListOfKeys();
+
+   // This is an empty pcm
+   if (listOfKeys && ((listOfKeys->GetSize() == 0) ||                           // Nothing here, or
+                      ((listOfKeys->GetSize() == 1) &&                          // only one, and
+                       !strcmp(((TKey *)listOfKeys->At(0))->GetName(), "EMPTY") // name is EMPTY
+                       ))) {
+      return kTRUE;
+   }
+
+   TObjArray *protoClasses;
+   if (gDebug > 1)
+      ::Info("TCling::LoadPCM", "reading protoclasses for %s \n", pcmFile->GetName());
+
+   pcmFile->GetObject("__ProtoClasses", protoClasses);
+
+   if (protoClasses) {
+      for (auto obj : *protoClasses) {
+         TProtoClass *proto = (TProtoClass *)obj;
+         TClassTable::Add(proto);
+      }
+      // Now that all TClass-es know how to set them up we can update
+      // existing TClasses, which might cause the creation of e.g. TBaseClass
+      // objects which in turn requires the creation of TClasses, that could
+      // come from the PCH, but maybe later in the loop. Instead of resolving
+      // a dependency graph the addition to the TClassTable above allows us
+      // to create these dependent TClasses as needed below.
+      for (auto proto : *protoClasses) {
+         if (TClass *existingCl = (TClass *)gROOT->GetListOfClasses()->FindObject(proto->GetName())) {
+            // We have an existing TClass object. It might be emulated
+            // or interpreted; we now have more information available.
+            // Make that available.
+            if (existingCl->GetState() != TClass::kHasTClassInit) {
+               DictFuncPtr_t dict = gClassTable->GetDict(proto->GetName());
+               if (!dict) {
+                  ::Error("TCling::LoadPCM", "Inconsistent TClassTable for %s", proto->GetName());
+               } else {
+                  // This will replace the existing TClass.
+                  TClass *ncl = (*dict)();
+                  if (ncl)
+                     ncl->PostLoadCheck();
+               }
+            }
+         }
+      }
+
+      protoClasses->Clear(); // Ownership was transfered to TClassTable.
+      delete protoClasses;
+   }
+
+   TObjArray *dataTypes;
+   pcmFile->GetObject("__Typedefs", dataTypes);
+   if (dataTypes) {
+      for (auto typedf : *dataTypes)
+         gROOT->GetListOfTypes()->Add(typedf);
+      dataTypes->Clear(); // Ownership was transfered to TListOfTypes.
+      delete dataTypes;
+   }
+
+   TObjArray *enums;
+   pcmFile->GetObject("__Enums", enums);
+   if (enums) {
+      // Cache the pointers
+      auto listOfGlobals = gROOT->GetListOfGlobals();
+      auto listOfEnums = dynamic_cast<THashList *>(gROOT->GetListOfEnums());
+      // Loop on enums and then on enum constants
+      for (auto selEnum : *enums) {
+         const char *enumScope = selEnum->GetTitle();
+         const char *enumName = selEnum->GetName();
+         if (strcmp(enumScope, "") == 0) {
+            // This is a global enum and is added to the
+            // list of enums and its constants to the list of globals
+            if (!listOfEnums->THashList::FindObject(enumName)) {
+               ((TEnum *)selEnum)->SetClass(nullptr);
+               listOfEnums->Add(selEnum);
+            }
+            for (auto enumConstant : *static_cast<TEnum *>(selEnum)->GetConstants()) {
+               if (!listOfGlobals->FindObject(enumConstant)) {
+                  listOfGlobals->Add(enumConstant);
+               }
+            }
+         } else {
+            // This enum is in a namespace. A TClass entry is bootstrapped if
+            // none exists yet and the enum is added to it
+            TClass *nsTClassEntry = TClass::GetClass(enumScope);
+            if (!nsTClassEntry) {
+               nsTClassEntry = new TClass(enumScope, 0, TClass::kNamespaceForMeta, true);
+            }
+            auto listOfEnums = nsTClassEntry->fEnums.load();
+            if (!listOfEnums) {
+               if ((kIsClass | kIsStruct | kIsUnion) & nsTClassEntry->Property()) {
+                  // For this case, the list will be immutable once constructed
+                  // (i.e. in this case, by the end of this routine).
+                  listOfEnums = nsTClassEntry->fEnums = new TListOfEnums(nsTClassEntry);
+               } else {
+                  // namespaces can have enums added to them
+                  listOfEnums = nsTClassEntry->fEnums = new TListOfEnumsWithLock(nsTClassEntry);
+               }
+            }
+            if (listOfEnums && !listOfEnums->THashList::FindObject(enumName)) {
+               ((TEnum *)selEnum)->SetClass(nsTClassEntry);
+               listOfEnums->Add(selEnum);
+            }
+         }
+      }
+      enums->Clear();
+      delete enums;
+   }
+
+   return kTRUE;
+}
 
 ////////////////////////////////////////////////////////////////////////////////
 /// Tries to load a PCM; returns true on success.
@@ -1505,7 +1625,7 @@ bool TCling::LoadPCM(const std::string &pcmFileNameFullPath)
    assert(!pcmFileNameFullPath.empty());
    assert(llvm::sys::path::is_absolute(pcmFileNameFullPath));
    if (!llvm::sys::fs::exists(pcmFileNameFullPath)) {
-     return false;
+      return false;
    }
 
    // Easier to work with the ROOT interfaces.
@@ -1527,131 +1647,15 @@ bool TCling::LoadPCM(const std::string &pcmFileNameFullPath)
 
       TDirectory::TContext ctxt;
 
-      TFile *pcmFile = new TFile(pcmFileName+"?filetype=pcm","READ");
-
-      auto listOfKeys = pcmFile->GetListOfKeys();
-
-      // This is an empty pcm
-      if (
-         listOfKeys &&
-         (
-            (listOfKeys->GetSize() == 0) || // Nothing here, or
-            (
-               (listOfKeys->GetSize() == 1) && // only one, and
-               !strcmp(((TKey*)listOfKeys->At(0))->GetName(), "EMPTY") // name is EMPTY
-            )
-         )
-      ) {
-         delete pcmFile;
-         gDebug = oldDebug;
-         return kTRUE;
-      }
-
-      TObjArray *protoClasses;
-      if (gDebug > 1)
-            ::Info("TCling::LoadPCM","reading protoclasses for %s \n",pcmFileName.Data());
-
-      pcmFile->GetObject("__ProtoClasses", protoClasses);
-
-      if (protoClasses) {
-         for (auto obj : *protoClasses) {
-            TProtoClass * proto = (TProtoClass*)obj;
-            TClassTable::Add(proto);
-         }
-         // Now that all TClass-es know how to set them up we can update
-         // existing TClasses, which might cause the creation of e.g. TBaseClass
-         // objects which in turn requires the creation of TClasses, that could
-         // come from the PCH, but maybe later in the loop. Instead of resolving
-         // a dependency graph the addition to the TClassTable above allows us
-         // to create these dependent TClasses as needed below.
-         for (auto proto : *protoClasses) {
-            if (TClass* existingCl
-                = (TClass*)gROOT->GetListOfClasses()->FindObject(proto->GetName())) {
-               // We have an existing TClass object. It might be emulated
-               // or interpreted; we now have more information available.
-               // Make that available.
-               if (existingCl->GetState() != TClass::kHasTClassInit) {
-                  DictFuncPtr_t dict = gClassTable->GetDict(proto->GetName());
-                  if (!dict) {
-                     ::Error("TCling::LoadPCM", "Inconsistent TClassTable for %s",
-                             proto->GetName());
-                  } else {
-                     // This will replace the existing TClass.
-                     TClass *ncl = (*dict)();
-                     if (ncl) ncl->PostLoadCheck();
-
-                  }
-               }
-            }
-         }
+      TFile *pcmFile = new TFile(pcmFileName + "?filetype=pcm", "READ");
 
-         protoClasses->Clear(); // Ownership was transfered to TClassTable.
-         delete protoClasses;
-      }
-
-      TObjArray *dataTypes;
-      pcmFile->GetObject("__Typedefs", dataTypes);
-      if (dataTypes) {
-         for (auto typedf: *dataTypes)
-            gROOT->GetListOfTypes()->Add(typedf);
-         dataTypes->Clear(); // Ownership was transfered to TListOfTypes.
-         delete dataTypes;
-      }
-
-      TObjArray *enums;
-      pcmFile->GetObject("__Enums", enums);
-      if (enums) {
-         // Cache the pointers
-         auto listOfGlobals = gROOT->GetListOfGlobals();
-         auto listOfEnums = dynamic_cast<THashList*>(gROOT->GetListOfEnums());
-         // Loop on enums and then on enum constants
-         for (auto selEnum: *enums){
-            const char* enumScope = selEnum->GetTitle();
-            const char* enumName = selEnum->GetName();
-            if (strcmp(enumScope,"") == 0){
-               // This is a global enum and is added to the
-               // list of enums and its constants to the list of globals
-               if (!listOfEnums->THashList::FindObject(enumName)){
-                  ((TEnum*) selEnum)->SetClass(nullptr);
-                  listOfEnums->Add(selEnum);
-               }
-               for (auto enumConstant: *static_cast<TEnum*>(selEnum)->GetConstants()){
-                  if (!listOfGlobals->FindObject(enumConstant)){
-                     listOfGlobals->Add(enumConstant);
-                  }
-               }
-            }
-            else {
-               // This enum is in a namespace. A TClass entry is bootstrapped if
-               // none exists yet and the enum is added to it
-               TClass* nsTClassEntry = TClass::GetClass(enumScope);
-               if (!nsTClassEntry){
-                  nsTClassEntry = new TClass(enumScope,0,TClass::kNamespaceForMeta, true);
-               }
-               auto listOfEnums = nsTClassEntry->fEnums.load();
-               if (!listOfEnums) {
-                  if ( (kIsClass | kIsStruct | kIsUnion) & nsTClassEntry->Property() ) {
-                     // For this case, the list will be immutable once constructed
-                     // (i.e. in this case, by the end of this routine).
-                     listOfEnums = nsTClassEntry->fEnums = new TListOfEnums(nsTClassEntry);
-                  } else {
-                     //namespaces can have enums added to them
-                     listOfEnums = nsTClassEntry->fEnums = new TListOfEnumsWithLock(nsTClassEntry);
-                  }
-               }
-               if (listOfEnums && !listOfEnums->THashList::FindObject(enumName)){
-                  ((TEnum*) selEnum)->SetClass(nsTClassEntry);
-                  listOfEnums->Add(selEnum);
-               }
-            }
-         }
-         enums->Clear();
-         delete enums;
-      }
+      bool result = LoadPCMImpl(pcmFile);
 
       delete pcmFile;
 
       gDebug = oldDebug;
+      return result;
+
    } else {
       if (gDebug > 5)
          ::Info("TCling::LoadPCM", "Loading clang PCM %s", pcmFileName.Data());
diff --git a/core/metacling/src/TCling.h b/core/metacling/src/TCling.h
index 92089c67459067bf9e7fbcde50a71457d8caaa2f..27755922448017e00c532a26524c57100808dae8 100644
--- a/core/metacling/src/TCling.h
+++ b/core/metacling/src/TCling.h
@@ -59,6 +59,7 @@ namespace cling {
 
 class TClingCallbacks;
 class TEnv;
+class TFile;
 class THashTable;
 class TInterpreterValue;
 class TMethod;
@@ -579,7 +580,9 @@ private: // Private Utility Functions and Classes
    void AddFriendToClass(clang::FunctionDecl*, clang::CXXRecordDecl*) const;
 
    bool LoadPCM(const std::string &pcmFileNameFullPath);
-   void InitRootmapFile(const char *name);
+   bool LoadPCMImpl(TFile *pcmFile);
+
+  void InitRootmapFile(const char *name);
    int  ReadRootmapFile(const char *rootmapfile, TUniqueString* uniqueString = nullptr);
    Bool_t HandleNewTransaction(const cling::Transaction &T);
    void UnloadClassMembers(TClass* cl, const clang::DeclContext* DC);
diff --git a/core/metacling/src/TClingRdictModuleFileExtension.cxx b/core/metacling/src/TClingRdictModuleFileExtension.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..6127849e56de3022064df6ae43278217d419eb2c
--- /dev/null
+++ b/core/metacling/src/TClingRdictModuleFileExtension.cxx
@@ -0,0 +1,180 @@
+/// \file TClingRdictModuleFileExtension.cxx
+///
+/// \brief The file contains facilities to work with C++ module files extensions
+///        used to store rdict files.
+///
+/// \author Vassil Vassilev <vvasilev@cern.ch>
+///
+/// \date May, 2019
+///
+/*************************************************************************
+ * Copyright (C) 1995-2018, Rene Brun and Fons Rademakers.               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * For the licensing terms see $ROOTSYS/LICENSE.                         *
+ * For the list of contributors see $ROOTSYS/README/CREDITS.             *
+ *************************************************************************/
+
+#include "TClingRdictModuleFileExtension.h"
+
+#include "TClingUtils.h"
+
+#include "clang/Frontend/FrontendDiagnostic.h"
+#include "clang/Lex/HeaderSearchOptions.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Sema/Sema.h"
+#include "clang/Serialization/ASTReader.h"
+#include "clang/Serialization/Module.h"
+
+#include "llvm/ADT/Hashing.h"
+#include "llvm/Bitcode/BitstreamWriter.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+
+/// Rdict module extension block name.
+const std::string ROOT_CLING_RDICT_BLOCK_NAME = "root.cling.rdict";
+
+/// Rdict module extension major version number.
+constexpr uint16_t ROOT_CLING_RDICT_VERSION_MAJOR = 1;
+
+/// Rdict module extension minor version number.
+///
+/// When the format changes IN ANY WAY, this number should be incremented.
+constexpr uint16_t ROOT_CLING_RDICT_VERSION_MINOR = 1;
+
+
+TClingRdictModuleFileExtension::Writer::~Writer() {}
+
+void TClingRdictModuleFileExtension::Writer::writeExtensionContents(clang::Sema &SemaRef, llvm::BitstreamWriter &Stream)
+{
+
+   const clang::LangOptions &Opts = SemaRef.getLangOpts();
+   const clang::Preprocessor &PP = SemaRef.getPreprocessor();
+
+   const llvm::StringRef CachePath = PP.getHeaderSearchInfo().getHeaderSearchOpts().ModuleCachePath;
+   const std::string RdictsStart = "lib" + Opts.CurrentModule + "_";
+   const std::string RdictsEnd = "_rdict.pcm";
+
+   using namespace llvm;
+   using namespace clang;
+   using namespace clang::serialization;
+   // Write an abbreviation for this record.
+   auto Abv = std::make_shared<BitCodeAbbrev>();
+   Abv->Add(BitCodeAbbrevOp(FIRST_EXTENSION_RECORD_ID));
+   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob));
+   auto Abbrev = Stream.EmitAbbrev(std::move(Abv));
+   auto Abv1 = std::make_shared<BitCodeAbbrev>();
+   Abv1->Add(BitCodeAbbrevOp(FIRST_EXTENSION_RECORD_ID + 1));
+   Abv1->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob));
+   auto Abbrev1 = Stream.EmitAbbrev(std::move(Abv1));
+
+   // Write a dict files into the extension block.
+   std::error_code EC;
+   for (llvm::sys::fs::directory_iterator DirIt(CachePath, EC), DirEnd; DirIt != DirEnd && !EC; DirIt.increment(EC)) {
+      StringRef FileName(DirIt->path());
+      if (!llvm::sys::fs::is_directory(FileName) && llvm::sys::path::filename(FileName).startswith(RdictsStart) &&
+          FileName.endswith(RdictsEnd)) {
+
+         uint64_t Record[] = {FIRST_EXTENSION_RECORD_ID};
+         Stream.EmitRecordWithBlob(Abbrev, Record, llvm::sys::path::filename(FileName));
+
+         uint64_t Record1[] = {FIRST_EXTENSION_RECORD_ID + 1};
+         Twine rdictFileName = Twine(FileName);
+         auto MBOrErr = MemoryBuffer::getFile(rdictFileName);
+         MemoryBuffer &MB = *MBOrErr.get();
+         Stream.EmitRecordWithBlob(Abbrev1, Record1, MB.getBuffer());
+
+         llvm::sys::fs::remove(rdictFileName);
+      }
+   }
+}
+
+static void WriteOnDisk(llvm::StringRef PathName, llvm::StringRef CurRdictName, llvm::StringRef CurRdictContent)
+{
+   llvm::SmallString<255> FullRdictName = PathName;
+   llvm::sys::path::append(FullRdictName, CurRdictName);
+   if (llvm::sys::fs::exists(FullRdictName)) {
+      // FIXME: Add diagnostics if we have a file on disk and we enter here...
+      return;
+   }
+   std::error_code EC;
+   // FIXME: Convert to new TMemFile = ...;
+   llvm::raw_fd_ostream os(FullRdictName.str(), EC, llvm::sys::fs::F_None);
+   os << CurRdictContent;
+   os.close();
+   // Add this class as a friend of TCling.
+   // gCling->LoadPCM(FullRdictName);
+}
+
+TClingRdictModuleFileExtension::Reader::Reader(clang::ModuleFileExtension *Ext, clang::ASTReader &Reader,
+                                               clang::serialization::ModuleFile &Mod,
+                                               const llvm::BitstreamCursor &InStream)
+   : ModuleFileExtensionReader(Ext), Stream(InStream)
+{
+   // Read the extension block.
+   llvm::SmallVector<uint64_t, 4> Record;
+   llvm::StringRef CurrentRdictName;
+   while (true) {
+      llvm::BitstreamEntry Entry = Stream.advanceSkippingSubblocks();
+      switch (Entry.Kind) {
+      case llvm::BitstreamEntry::SubBlock:
+      case llvm::BitstreamEntry::EndBlock:
+      case llvm::BitstreamEntry::Error: return;
+
+      case llvm::BitstreamEntry::Record: break;
+      }
+
+      Record.clear();
+      llvm::StringRef Blob;
+      unsigned RecCode = Stream.readRecord(Entry.ID, Record, &Blob);
+      using namespace clang::serialization;
+      switch (RecCode) {
+      case FIRST_EXTENSION_RECORD_ID: {
+         CurrentRdictName = Blob;
+         break;
+      }
+      case FIRST_EXTENSION_RECORD_ID + 1: {
+         llvm::StringRef Path = llvm::sys::path::parent_path(Mod.FileName);
+         WriteOnDisk(Path, CurrentRdictName, Blob);
+         break;
+      }
+      }
+   }
+}
+
+TClingRdictModuleFileExtension::Reader::~Reader() {}
+
+TClingRdictModuleFileExtension::~TClingRdictModuleFileExtension() {}
+
+clang::ModuleFileExtensionMetadata TClingRdictModuleFileExtension::getExtensionMetadata() const
+{
+   const std::string UserInfo = "";
+   return {ROOT_CLING_RDICT_BLOCK_NAME, ROOT_CLING_RDICT_VERSION_MAJOR, ROOT_CLING_RDICT_VERSION_MINOR, UserInfo};
+}
+
+llvm::hash_code TClingRdictModuleFileExtension::hashExtension(llvm::hash_code Code) const
+{
+   Code = llvm::hash_combine(Code, ROOT_CLING_RDICT_BLOCK_NAME);
+   Code = llvm::hash_combine(Code, ROOT_CLING_RDICT_VERSION_MAJOR);
+   Code = llvm::hash_combine(Code, ROOT_CLING_RDICT_VERSION_MINOR);
+   Code = llvm::hash_combine(Code, UserInfo);
+
+   return Code;
+}
+
+std::unique_ptr<clang::ModuleFileExtensionWriter>
+TClingRdictModuleFileExtension::createExtensionWriter(clang::ASTWriter &)
+{
+   return std::unique_ptr<clang::ModuleFileExtensionWriter>(new Writer(this));
+}
+
+std::unique_ptr<clang::ModuleFileExtensionReader>
+TClingRdictModuleFileExtension::createExtensionReader(const clang::ModuleFileExtensionMetadata &Metadata,
+                                                      clang::ASTReader &Reader, clang::serialization::ModuleFile &Mod,
+                                                      const llvm::BitstreamCursor &Stream)
+{
+   return std::unique_ptr<clang::ModuleFileExtensionReader>(
+      new TClingRdictModuleFileExtension::Reader(this, Reader, Mod, Stream));
+}
diff --git a/core/metacling/src/TClingRdictModuleFileExtension.h b/core/metacling/src/TClingRdictModuleFileExtension.h
new file mode 100644
index 0000000000000000000000000000000000000000..0248f17d71aa3493f56c961ce655febe13aba623
--- /dev/null
+++ b/core/metacling/src/TClingRdictModuleFileExtension.h
@@ -0,0 +1,64 @@
+/// \file TClingRdictModuleFileExtension.h
+///
+/// \brief The file contains facilities to work with C++ module files extensions
+///        used to store rdict files.
+///
+/// \author Vassil Vassilev <vvasilev@cern.ch>
+///
+/// \date May, 2019
+///
+/*************************************************************************
+ * Copyright (C) 1995-2018, Rene Brun and Fons Rademakers.               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * For the licensing terms see $ROOTSYS/LICENSE.                         *
+ * For the list of contributors see $ROOTSYS/README/CREDITS.             *
+ *************************************************************************/
+
+#ifndef ROOT_CLING_RDICT_MODULE_FILE_EXTENSION_H
+#define ROOT_CLING_RDICT_MODULE_FILE_EXTENSION_H
+
+#include "clang/Serialization/ModuleFileExtension.h"
+
+#include "llvm/Bitcode/BitstreamReader.h"
+
+/// A module file extension used for testing purposes.
+class TClingRdictModuleFileExtension : public clang::ModuleFileExtension {
+   std::string UserInfo;
+   std::string RdictFileName;
+
+   class Writer : public clang::ModuleFileExtensionWriter {
+   public:
+      Writer(ModuleFileExtension *Ext) : ModuleFileExtensionWriter(Ext) {}
+      ~Writer() override;
+
+      void writeExtensionContents(clang::Sema &SemaRef, llvm::BitstreamWriter &Stream) override;
+   };
+
+   class Reader : public clang::ModuleFileExtensionReader {
+      llvm::BitstreamCursor Stream;
+
+   public:
+      ~Reader() override;
+
+      Reader(clang::ModuleFileExtension *Ext, clang::ASTReader &Reader, clang::serialization::ModuleFile &Mod,
+             const llvm::BitstreamCursor &InStream);
+   };
+
+public:
+   TClingRdictModuleFileExtension(llvm::StringRef UserInfo) : UserInfo(UserInfo) {}
+
+   ~TClingRdictModuleFileExtension() override;
+
+   clang::ModuleFileExtensionMetadata getExtensionMetadata() const override;
+
+   llvm::hash_code hashExtension(llvm::hash_code Code) const override;
+
+   std::unique_ptr<clang::ModuleFileExtensionWriter> createExtensionWriter(clang::ASTWriter &Writer) override;
+
+   std::unique_ptr<clang::ModuleFileExtensionReader>
+   createExtensionReader(const clang::ModuleFileExtensionMetadata &Metadata, clang::ASTReader &Reader,
+                         clang::serialization::ModuleFile &Mod, const llvm::BitstreamCursor &Stream) override;
+};
+
+#endif
diff --git a/interpreter/cling/include/cling/Interpreter/CIFactory.h b/interpreter/cling/include/cling/Interpreter/CIFactory.h
index 236f3324d692af173cb5b8a84df173002d59978a..1a77e22b2edd5cf0d37e3e9b25ef14fd537bdb4c 100644
--- a/interpreter/cling/include/cling/Interpreter/CIFactory.h
+++ b/interpreter/cling/include/cling/Interpreter/CIFactory.h
@@ -20,8 +20,9 @@ namespace llvm {
 }
 
 namespace clang {
-  class DiagnosticsEngine;
   class ASTConsumer;
+  class DiagnosticsEngine;
+  class ModuleFileExtension;
 }
 
 namespace cling {
@@ -29,16 +30,20 @@ namespace cling {
 
   namespace CIFactory {
     typedef std::unique_ptr<llvm::MemoryBuffer> MemBufPtr_t;
+    using ModuleFileExtensions =
+        std::vector<std::shared_ptr<clang::ModuleFileExtension>>;
 
     // TODO: Add overload that takes file not MemoryBuffer
 
     clang::CompilerInstance*
     createCI(llvm::StringRef Code, const InvocationOptions& Opts,
-             const char* LLVMDir, std::unique_ptr<clang::ASTConsumer> consumer);
+             const char* LLVMDir, std::unique_ptr<clang::ASTConsumer> consumer,
+             const ModuleFileExtensions& moduleExtensions);
 
     clang::CompilerInstance*
     createCI(MemBufPtr_t Buffer, int Argc, const char* const* Argv,
              const char* LLVMDir, std::unique_ptr<clang::ASTConsumer> consumer,
+             const ModuleFileExtensions& moduleExtensions,
              bool OnlyLex = false);
   } // namespace CIFactory
 } // namespace cling
diff --git a/interpreter/cling/include/cling/Interpreter/Interpreter.h b/interpreter/cling/include/cling/Interpreter/Interpreter.h
index fe94b7a8cad348bf5428998b096d58a2c0233ede..3dad5066c7431024f77d2b63d93d8e75553b8626 100644
--- a/interpreter/cling/include/cling/Interpreter/Interpreter.h
+++ b/interpreter/cling/include/cling/Interpreter/Interpreter.h
@@ -41,6 +41,7 @@ namespace clang {
   class GlobalDecl;
   class MacroInfo;
   class Module;
+  class ModuleFileExtension;
   class NamedDecl;
   class Parser;
   class Preprocessor;
@@ -137,6 +138,10 @@ namespace cling {
       kNumExeResults
     };
 
+  public:
+    using ModuleFileExtensions =
+        std::vector<std::shared_ptr<clang::ModuleFileExtension>>;
+
   private:
 
     ///\brief Interpreter invocation options.
@@ -303,8 +308,8 @@ namespace cling {
     ///\brief The target constructor to be called from both the delegating
     /// constructors. parentInterp might be nullptr.
     ///
-    Interpreter(int argc, const char* const *argv,
-                const char* llvmdir, bool noRuntime,
+    Interpreter(int argc, const char* const* argv, const char* llvmdir,
+                const ModuleFileExtensions& moduleExtensions, bool noRuntime,
                 const Interpreter* parentInterp);
 
   public:
@@ -315,9 +320,11 @@ namespace cling {
     ///\param[in] llvmdir - ???
     ///\param[in] noRuntime - flag to control the presence of runtime universe
     ///
-    Interpreter(int argc, const char* const *argv, const char* llvmdir = 0,
-                bool noRuntime = false) :
-      Interpreter(argc, argv, llvmdir, noRuntime, nullptr) { }
+    Interpreter(int argc, const char* const* argv, const char* llvmdir = 0,
+                const ModuleFileExtensions& moduleExtensions = {},
+                bool noRuntime = false)
+        : Interpreter(argc, argv, llvmdir, moduleExtensions, noRuntime,
+                      nullptr) {}
 
     ///\brief Constructor for child Interpreter.
     ///\param[in] parentInterpreter - the  parent interpreter of this interpreter
@@ -326,8 +333,10 @@ namespace cling {
     ///\param[in] llvmdir - ???
     ///\param[in] noRuntime - flag to control the presence of runtime universe
     ///
-    Interpreter(const Interpreter &parentInterpreter,int argc, const char* const *argv,
-                const char* llvmdir = 0, bool noRuntime = true);
+    Interpreter(const Interpreter& parentInterpreter, int argc,
+                const char* const* argv, const char* llvmdir = 0,
+                const ModuleFileExtensions& moduleExtensions = {},
+                bool noRuntime = true);
 
     virtual ~Interpreter();
 
diff --git a/interpreter/cling/lib/Interpreter/CIFactory.cpp b/interpreter/cling/lib/Interpreter/CIFactory.cpp
index 80d1b1c52a7dd4b8ae32297f56de9e2ee7581402..951f14005d291e3cb4c7f76a5ef457935d8eec20 100644
--- a/interpreter/cling/lib/Interpreter/CIFactory.cpp
+++ b/interpreter/cling/lib/Interpreter/CIFactory.cpp
@@ -780,8 +780,9 @@ static void stringifyPreprocSetting(PreprocessorOptions& PPOpts,
   static CompilerInstance*
   createCIImpl(std::unique_ptr<llvm::MemoryBuffer> Buffer,
                const CompilerOptions& COpts, const char* LLVMDir,
-               std::unique_ptr<clang::ASTConsumer> customConsumer, bool OnlyLex,
-               bool HasInput = false) {
+               std::unique_ptr<clang::ASTConsumer> customConsumer,
+               const CIFactory::ModuleFileExtensions& moduleExtensions,
+               bool OnlyLex, bool HasInput = false) {
     // Follow clang -v convention of printing version on first line
     if (COpts.Verbose)
       cling::log() << "cling version " << ClingStringify(CLING_VERSION) << '\n';
@@ -1043,6 +1044,12 @@ static void stringifyPreprocSetting(PreprocessorOptions& PPOpts,
     }
 
     FrontendOptions& FrontendOpts = Invocation.getFrontendOpts();
+
+    // Register the externally constructed extensions.
+    assert(FrontendOpts.ModuleFileExtensions.empty() && "Extensions exist!");
+    for (auto& E : moduleExtensions)
+      FrontendOpts.ModuleFileExtensions.push_back(E);
+
     FrontendOpts.DisableFree = true;
 
     // With modules, we now start adding prebuilt module paths to the CI.
@@ -1240,17 +1247,20 @@ namespace cling {
 CompilerInstance*
 CIFactory::createCI(llvm::StringRef Code, const InvocationOptions& Opts,
                     const char* LLVMDir,
-                    std::unique_ptr<clang::ASTConsumer> consumer) {
+                    std::unique_ptr<clang::ASTConsumer> consumer,
+                    const ModuleFileExtensions& moduleExtensions) {
   return createCIImpl(llvm::MemoryBuffer::getMemBuffer(Code), Opts.CompilerOpts,
-                      LLVMDir, std::move(consumer), false /*OnlyLex*/,
+                      LLVMDir, std::move(consumer), moduleExtensions,
+                      false /*OnlyLex*/,
                       !Opts.IsInteractive());
 }
 
 CompilerInstance* CIFactory::createCI(
     MemBufPtr_t Buffer, int argc, const char* const* argv, const char* LLVMDir,
-    std::unique_ptr<clang::ASTConsumer> consumer, bool OnlyLex) {
+    std::unique_ptr<clang::ASTConsumer> consumer,
+    const ModuleFileExtensions& moduleExtensions, bool OnlyLex /*false*/) {
   return createCIImpl(std::move(Buffer), CompilerOptions(argc, argv), LLVMDir,
-                      std::move(consumer), OnlyLex);
+                      std::move(consumer), moduleExtensions, OnlyLex);
 }
 
 } // namespace cling
diff --git a/interpreter/cling/lib/Interpreter/IncrementalParser.cpp b/interpreter/cling/lib/Interpreter/IncrementalParser.cpp
index 02daa3d4ab5a34ad0c5a48b9e7dd37fba1ad815a..8cceb62fb96a7e587b1894995b0abc37cee9d1c5 100644
--- a/interpreter/cling/lib/Interpreter/IncrementalParser.cpp
+++ b/interpreter/cling/lib/Interpreter/IncrementalParser.cpp
@@ -234,12 +234,13 @@ static void HandlePlugins(CompilerInstance& CI,
 }
 
 namespace cling {
-  IncrementalParser::IncrementalParser(Interpreter* interp, const char* llvmdir)
+  IncrementalParser::IncrementalParser(Interpreter* interp, const char* llvmdir,
+                                   const ModuleFileExtensions& moduleExtensions)
       : m_Interpreter(interp) {
     std::unique_ptr<cling::DeclCollector> consumer;
     consumer.reset(m_Consumer = new cling::DeclCollector());
     m_CI.reset(CIFactory::createCI("", interp->getOptions(), llvmdir,
-                                   std::move(consumer)));
+                                   std::move(consumer), moduleExtensions));
 
     if (!m_CI) {
       cling::errs() << "Compiler instance could not be created.\n";
diff --git a/interpreter/cling/lib/Interpreter/IncrementalParser.h b/interpreter/cling/lib/Interpreter/IncrementalParser.h
index 8322de0908e990c34db3ee76bde4f1932c70c6c0..c88ec82e6f1651ed366c8fe073b714e4228f8bc4 100644
--- a/interpreter/cling/lib/Interpreter/IncrementalParser.h
+++ b/interpreter/cling/lib/Interpreter/IncrementalParser.h
@@ -33,6 +33,7 @@ namespace clang {
   class DiagnosticConsumer;
   class Decl;
   class FileID;
+  class ModuleFileExtension;
   class Parser;
 }
 
@@ -100,6 +101,9 @@ namespace cling {
     ///
     std::unique_ptr<IncrementalCUDADeviceCompiler> m_CUDACompiler;
 
+    using ModuleFileExtensions =
+        std::vector<std::shared_ptr<clang::ModuleFileExtension>>;
+
   public:
     enum EParseResult {
       kSuccess,
@@ -108,7 +112,8 @@ namespace cling {
     };
     typedef llvm::PointerIntPair<Transaction*, 2, EParseResult>
       ParseResultTransaction;
-    IncrementalParser(Interpreter* interp, const char* llvmdir);
+    IncrementalParser(Interpreter* interp, const char* llvmdir,
+                      const ModuleFileExtensions& moduleExtensions);
     ~IncrementalParser();
 
     ///\brief Whether the IncrementalParser is valid.
diff --git a/interpreter/cling/lib/Interpreter/Interpreter.cpp b/interpreter/cling/lib/Interpreter/Interpreter.cpp
index 535325c3fb6d529c7a0f878e56dff964423a43d9..568658a09616d3968062017e1b17d2083ec8293f 100644
--- a/interpreter/cling/lib/Interpreter/Interpreter.cpp
+++ b/interpreter/cling/lib/Interpreter/Interpreter.cpp
@@ -217,8 +217,9 @@ namespace cling {
   }
 
   Interpreter::Interpreter(int argc, const char* const *argv,
-                           const char* llvmdir /*= 0*/, bool noRuntime,
-                           const Interpreter* parentInterp) :
+                           const char* llvmdir /*= 0*/,
+                           const ModuleFileExtensions& moduleExtensions,
+                           bool noRuntime, const Interpreter* parentInterp) :
     m_Opts(argc, argv),
     m_UniqueCounter(parentInterp ? parentInterp->m_UniqueCounter + 1 : 0),
     m_PrintDebug(false), m_DynamicLookupDeclared(false),
@@ -230,7 +231,7 @@ namespace cling {
 
     m_LLVMContext.reset(new llvm::LLVMContext);
     m_DyLibManager.reset(new DynamicLibraryManager(getOptions()));
-    m_IncrParser.reset(new IncrementalParser(this, llvmdir));
+    m_IncrParser.reset(new IncrementalParser(this, llvmdir, moduleExtensions));
     if (!m_IncrParser->isValid(false))
       return;
 
@@ -399,8 +400,11 @@ namespace cling {
   ///
   Interpreter::Interpreter(const Interpreter &parentInterpreter, int argc,
                            const char* const *argv,
-                           const char* llvmdir /*= 0*/, bool noRuntime) :
-    Interpreter(argc, argv, llvmdir, noRuntime, &parentInterpreter) {
+                           const char* llvmdir /*= 0*/,
+                           const ModuleFileExtensions& moduleExtensions/*={}*/,
+                           bool noRuntime /*= true*/) :
+    Interpreter(argc, argv, llvmdir, moduleExtensions, noRuntime,
+                &parentInterpreter) {
     // Do the "setup" of the connection between this interpreter and
     // its parent interpreter.
     if (CompilerInstance* CI = getCIOrNull()) {
@@ -1674,7 +1678,8 @@ namespace cling {
     // FIXME: CIFactory appends extra 3 folders to the llvmdir.
     std::string llvmdir
       = getCI()->getHeaderSearchOpts().ResourceDir + "/../../../";
-    cling::Interpreter fwdGen(1, &dummy, llvmdir.c_str(), true);
+    cling::Interpreter fwdGen(1, &dummy, llvmdir.c_str(),
+                              /*moduleExtensions*/ {}, /*noRuntime=*/true);
 
     // Copy the same header search options to the new instance.
     Preprocessor& fwdGenPP = fwdGen.getCI()->getPreprocessor();