Merge tag 'swift-DEVELOPMENT-SNAPSHOT-2019-10-08-a' into tensorflow

Tag build swift-DEVELOPMENT-SNAPSHOT-2019-10-08-a
diff --git a/include/clang/Basic/InMemoryOutputFileSystem.h b/include/clang/Basic/InMemoryOutputFileSystem.h
new file mode 100644
index 0000000..dfa53e7
--- /dev/null
+++ b/include/clang/Basic/InMemoryOutputFileSystem.h
@@ -0,0 +1,106 @@
+//===-- InMemoryOutputFileSystem.h - Collects outputs in memory -*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_BASIC_INMEMORYOUTPUTFILESYSTEM_H_
+#define LLVM_CLANG_BASIC_INMEMORYOUTPUTFILESYSTEM_H_
+
+#include <memory>
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Mutex.h"
+#include "llvm/Support/MutexGuard.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/VirtualFileSystem.h"
+
+namespace clang {
+
+/// Collects output files in memory, and provides a `llvm::vfs::FileSystem`
+/// interface for accessing those files.
+///
+/// This class is threadsafe. Unsynchronized calls from multiple threads will
+/// not corrupt the internal state, and operations occur atomically and
+/// sequentially consistently from the point of view of all threads.
+class InMemoryOutputFileSystem : public llvm::vfs::FileSystem {
+ public:
+  InMemoryOutputFileSystem() : OutputFiles(new llvm::vfs::InMemoryFileSystem())
+  {}
+
+  /// Creates a temporary buffer that collects data for a file that may
+  /// eventually appear on the `llvm::vfs::FileSystem` interface.
+  /// `InMemoryOutputFileSystem` owns the buffer, which will not be released
+  /// until `DeleteTemporaryFile` or `FinalizeTemporaryFile` is called.
+  /// \param OutputPath the path of the file that may eventually be created.
+  /// \param TemporaryPath must be non-null. Pointee will be set to a unique
+  //         string identifying this particular temporary buffer.
+  //  \returns A stream that can be used to write to the buffer.
+  std::unique_ptr<llvm::raw_pwrite_stream> CreateTemporaryBuffer(
+      llvm::StringRef OutputPath,
+      std::string *TemporaryPath);
+
+  /// Releases the buffer underlying the temporary file.
+  /// \param TemporaryPath the unique string from `CreateTemporaryFile`.
+  void DeleteTemporaryBuffer(llvm::StringRef TemporaryPath);
+
+  /// Makes the contents of the specified temporary buffer visible on the
+  /// `llvm::vfs::FileSystem` interface, and releases the temporary buffer. If
+  /// the file already exists on the `llvm::vfs::FileSystem` interface, then
+  /// the new contents is silently ignored.
+  /// \param OutputPath the path of the file to create.
+  /// \param TemporaryPath the unique string from `CreateTemporaryFile`.
+  void FinalizeTemporaryBuffer(llvm::StringRef OutputPath,
+                               llvm::StringRef TemporaryPath);
+
+  // MARK: - `llvm::vfs::FileSystem` overrides
+
+  llvm::ErrorOr<llvm::vfs::Status> status(const llvm::Twine& relpath) override {
+    llvm::MutexGuard locked(Mu);
+    return OutputFiles->status(relpath);
+  }
+
+  llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> openFileForRead(
+      const llvm::Twine& relpath) override {
+    llvm::MutexGuard locked(Mu);
+    return OutputFiles->openFileForRead(relpath);
+  }
+
+  llvm::vfs::directory_iterator dir_begin(const llvm::Twine& reldir,
+                                          std::error_code& err) override {
+    llvm::MutexGuard locked(Mu);
+    return OutputFiles->dir_begin(reldir, err);
+  }
+
+  std::error_code setCurrentWorkingDirectory(const llvm::Twine& path) override {
+    llvm::MutexGuard locked(Mu);
+    return OutputFiles->setCurrentWorkingDirectory(path);
+  }
+
+  llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
+    llvm::MutexGuard locked(Mu);
+    return OutputFiles->getCurrentWorkingDirectory();
+  }
+
+  std::error_code getRealPath(
+      const llvm::Twine& path,
+      llvm::SmallVectorImpl<char>& output) const override {
+    llvm::MutexGuard locked(Mu);
+    return OutputFiles->getRealPath(path, output);
+  }
+
+ private:
+  mutable llvm::sys::Mutex Mu;
+  llvm::StringMap<llvm::SmallVector<char, 0>> TemporaryBuffers;
+  llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> OutputFiles;
+};
+
+}  // namespace clang
+
+#endif  // LLVM_CLANG_BASIC_INMEMORYOUTPUTFILESYSTEM_H_
diff --git a/include/clang/Frontend/CompilerInstance.h b/include/clang/Frontend/CompilerInstance.h
index 90714e2..836875c 100644
--- a/include/clang/Frontend/CompilerInstance.h
+++ b/include/clang/Frontend/CompilerInstance.h
@@ -11,6 +11,8 @@
 
 #include "clang/AST/ASTConsumer.h"
 #include "clang/Basic/Diagnostic.h"
+// SWIFT_ENABLE_TENSORFLOW
+#include "clang/Basic/InMemoryOutputFileSystem.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Frontend/CompilerInvocation.h"
 #include "clang/Frontend/PCHContainerOperations.h"
@@ -186,6 +188,11 @@
   /// Force an output buffer.
   std::unique_ptr<llvm::raw_pwrite_stream> OutputStream;
 
+  // SWIFT_ENABLE_TENSORFLOW
+  /// If defined, outputs will be written here instead of to the real
+  /// filesystem.
+  IntrusiveRefCntPtr<InMemoryOutputFileSystem> InMemoryOutputFileSystem;
+
   CompilerInstance(const CompilerInstance &) = delete;
   void operator=(const CompilerInstance &) = delete;
 public:
@@ -393,6 +400,19 @@
     return getFileManager().getVirtualFileSystem();
   }
 
+  // SWIFT_ENABLE_TENSORFLOW
+  /// }
+  /// @name In-Memory Output File System
+  /// {
+
+  IntrusiveRefCntPtr<clang::InMemoryOutputFileSystem> getInMemoryOutputFileSystem() const {
+    return InMemoryOutputFileSystem;
+  }
+
+  void setInMemoryOutputFileSystem(IntrusiveRefCntPtr<clang::InMemoryOutputFileSystem> FS) {
+    InMemoryOutputFileSystem = std::move(FS);
+  }
+
   /// }
   /// @name File Manager
   /// {
diff --git a/lib/Basic/CMakeLists.txt b/lib/Basic/CMakeLists.txt
index a53b4d9..197e81b 100644
--- a/lib/Basic/CMakeLists.txt
+++ b/lib/Basic/CMakeLists.txt
@@ -49,6 +49,7 @@
   FileSystemStatCache.cpp
   FixedPoint.cpp
   IdentifierTable.cpp
+  InMemoryOutputFileSystem.cpp
   LangOptions.cpp
   LangStandards.cpp
   Module.cpp
diff --git a/lib/Basic/InMemoryOutputFileSystem.cpp b/lib/Basic/InMemoryOutputFileSystem.cpp
new file mode 100644
index 0000000..93a4816
--- /dev/null
+++ b/lib/Basic/InMemoryOutputFileSystem.cpp
@@ -0,0 +1,55 @@
+//=== InMemoryOutputFileSystem.cpp - Collects outputs in memory -*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/InMemoryOutputFileSystem.h"
+
+namespace clang {
+
+std::unique_ptr<llvm::raw_pwrite_stream>
+InMemoryOutputFileSystem::CreateTemporaryBuffer(llvm::StringRef OutputPath,
+                                                std::string *TemporaryPath) {
+  assert(TemporaryPath);
+  llvm::MutexGuard locked(Mu);
+  llvm::StringMap<llvm::SmallVector<char, 0>>::iterator it;
+  bool inserted = false;
+  unsigned suffix = 0;
+  while (!inserted) {
+    *TemporaryPath = "";
+    llvm::raw_string_ostream TemporaryPathOS(*TemporaryPath);
+    TemporaryPathOS << OutputPath << "-" << suffix;
+    TemporaryPathOS.flush();
+    auto result = TemporaryBuffers.try_emplace(*TemporaryPath);
+    it = result.first;
+    inserted = result.second;
+    suffix += 1;
+  }
+  return llvm::make_unique<llvm::raw_svector_ostream>(it->getValue());
+}
+
+void InMemoryOutputFileSystem::DeleteTemporaryBuffer(llvm::StringRef TemporaryPath) {
+  llvm::MutexGuard locked(Mu);
+  auto it = TemporaryBuffers.find(TemporaryPath);
+  assert(it != TemporaryBuffers.end());
+  TemporaryBuffers.erase(it);
+}
+
+void InMemoryOutputFileSystem::FinalizeTemporaryBuffer(llvm::StringRef OutputPath,
+                             llvm::StringRef TemporaryPath) {
+  llvm::MutexGuard locked(Mu);
+  auto it = TemporaryBuffers.find(TemporaryPath);
+  assert(it != TemporaryBuffers.end());
+  auto memoryBuffer = llvm::MemoryBuffer::getMemBufferCopy(
+      llvm::StringRef{it->getValue().data(), it->getValue().size()},
+      OutputPath);
+  OutputFiles->addFile(OutputPath, /*ModificationTime=*/0,
+                       std::move(memoryBuffer));
+  TemporaryBuffers.erase(it);
+}
+
+}  // namespace clang
diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp
index e69a95a..e122995 100644
--- a/lib/Frontend/CompilerInstance.cpp
+++ b/lib/Frontend/CompilerInstance.cpp
@@ -652,6 +652,19 @@
 
 void CompilerInstance::clearOutputFiles(bool EraseFiles) {
   for (OutputFile &OF : OutputFiles) {
+    // SWIFT_ENABLE_TENSORFLOW
+    if (InMemoryOutputFileSystem) {
+      assert(!OF.TempFilename.empty() &&
+             "InMemoryOutputFileSystem requires using temporary files");
+      if (EraseFiles) {
+        InMemoryOutputFileSystem->DeleteTemporaryBuffer(OF.TempFilename);
+      } else {
+        InMemoryOutputFileSystem->FinalizeTemporaryBuffer(OF.Filename,
+                                                        OF.TempFilename);
+      }
+      continue;
+    }
+
     if (!OF.TempFilename.empty()) {
       if (EraseFiles) {
         llvm::sys::fs::remove(OF.TempFilename);
@@ -738,6 +751,18 @@
     OutFile = "-";
   }
 
+  // SWIFT_ENABLE_TENSORFLOW
+  if (InMemoryOutputFileSystem) {
+    assert(UseTemporary && "InMemoryOutputFileSystem requires using temporary files");
+    auto stream = InMemoryOutputFileSystem->CreateTemporaryBuffer(OutFile,
+                                                                &TempFile);
+    if (ResultPathName)
+      *ResultPathName = OutFile;
+    if (TempPathName)
+      *TempPathName = TempFile;
+    return stream;
+  }
+
   std::unique_ptr<llvm::raw_fd_ostream> OS;
   std::string OSFile;
 
@@ -1128,6 +1153,9 @@
                                    ImportingInstance.getDiagnosticClient()),
                              /*ShouldOwnClient=*/true);
 
+  // SWIFT_ENABLE_TENSORFLOW
+  Instance.setInMemoryOutputFileSystem(ImportingInstance.getInMemoryOutputFileSystem());
+
   // Note that this module is part of the module build stack, so that we
   // can detect cycles in the module graph.
   Instance.setFileManager(&ImportingInstance.getFileManager());
@@ -1277,6 +1305,33 @@
         << Module->Name << SourceRange(ImportLoc, ModuleNameLoc);
   };
 
+  // SWIFT_ENABLE_TENSORFLOW
+  // If we're writing to an InMemoryOutputFileSystem, then immediately compile
+  // and read the module, rather than doing all the lockfile based locking logic
+  // below, because the InMemoryOutputFileSystem doesn't support lockfiles. This
+  // is okay because the locks are only necessary for performance, not
+  // correctness.
+  if (ImportingInstance.getInMemoryOutputFileSystem()) {
+    if (!compileModuleImpl(ImportingInstance, ModuleNameLoc, Module,
+                           ModuleFileName)) {
+      diagnoseBuildFailure();
+      return false;
+    }
+
+    // Try to read the module file, now that we've compiled it.
+    ASTReader::ASTReadResult ReadResult =
+        ImportingInstance.getModuleManager()->ReadAST(
+            ModuleFileName, serialization::MK_ImplicitModule, ImportLoc,
+            ASTReader::ARR_None);
+
+    if (ReadResult != ASTReader::Success) {
+      diagnoseBuildFailure();
+      return false;
+    }
+
+    return true;
+  }
+
   // FIXME: have LockFileManager return an error_code so that we can
   // avoid the mkdir when the directory already exists.
   StringRef Dir = llvm::sys::path::parent_path(ModuleFileName);