Index: include/clang/Driver/Options.td
===================================================================
--- include/clang/Driver/Options.td	(revision 200973)
+++ include/clang/Driver/Options.td	(working copy)
@@ -580,6 +580,10 @@
 def fmodules_prune_after : Joined<["-"], "fmodules-prune-after=">, Group<i_Group>,
   Flags<[CC1Option]>, MetaVarName<"<seconds>">,
   HelpText<"Specify the interval (in seconds) after which a module file will be considered unused">;
+def fmodules_skip_verify_if_validated_after : Joined<["-"], "fmodules-skip-verify-if-validated-after=">,
+  Group<i_Group>, Flags<[CC1Option]>, MetaVarName<"<time since Epoch in milliseconds>">,
+  HelpText<"Don't verify input files for the modules if the module has been "
+           "successfully validate or loaded after the specified POSIX time">;
 def fmodules : Flag <["-"], "fmodules">, Group<f_Group>,
   Flags<[DriverOption, CC1Option]>,
   HelpText<"Enable the 'modules' language feature">;
Index: include/clang/Frontend/Utils.h
===================================================================
--- include/clang/Frontend/Utils.h	(revision 200973)
+++ include/clang/Frontend/Utils.h	(working copy)
@@ -123,6 +123,17 @@
   return getLastArgIntValue(Args, Id, Default, &Diags);
 }
 
+uint64_t getLastArgUInt64Value(const llvm::opt::ArgList &Args,
+                               llvm::opt::OptSpecifier Id, uint64_t Default,
+                               DiagnosticsEngine *Diags = 0);
+
+inline uint64_t getLastArgUInt64Value(const llvm::opt::ArgList &Args,
+                                      llvm::opt::OptSpecifier Id,
+                                      uint64_t Default,
+                                      DiagnosticsEngine &Diags) {
+  return getLastArgUInt64Value(Args, Id, Default, &Diags);
+}
+
 // When Clang->getFrontendOpts().DisableFree is set we don't delete some of the
 // global objects, but we don't want LeakDetectors to complain, so we bury them
 // in a globally visible array.
Index: include/clang/Lex/HeaderSearchOptions.h
===================================================================
--- include/clang/Lex/HeaderSearchOptions.h	(revision 200973)
+++ include/clang/Lex/HeaderSearchOptions.h	(working copy)
@@ -116,6 +116,12 @@
   /// regenerated often.
   unsigned ModuleCachePruneAfter;
 
+  /// \brief If non-zero, skip verifying input files used by modules if the
+  /// module was already verified after the specified time.
+  ///
+  /// The time is specified in milliseconds since the start of the Epoch.
+  uint64_t SkipVerifyIfValidatedAfter;
+
   /// \brief The set of macro names that should be ignored for the purposes
   /// of computing the module hash.
   llvm::SetVector<std::string> ModulesIgnoreMacros;
@@ -143,6 +149,7 @@
     : Sysroot(_Sysroot), DisableModuleHash(0), ModuleMaps(0),
       ModuleCachePruneInterval(7*24*60*60),
       ModuleCachePruneAfter(31*24*60*60),
+      SkipVerifyIfValidatedAfter(0),
       UseBuiltinIncludes(true),
       UseStandardSystemIncludes(true), UseStandardCXXIncludes(true),
       UseLibcxx(false), Verbose(false) {}
Index: include/clang/Serialization/Module.h
===================================================================
--- include/clang/Serialization/Module.h	(revision 200973)
+++ include/clang/Serialization/Module.h	(working copy)
@@ -115,6 +115,10 @@
   /// \brief The file name of the module file.
   std::string FileName;
 
+  std::string getTimestampFilename() const {
+    return FileName + ".timestamp";
+  }
+
   /// \brief The original source file name that was used to build the
   /// primary AST file, which may have been modified for
   /// relocatable-pch support.
@@ -185,6 +189,12 @@
   /// \brief The input files that have been loaded from this AST file.
   std::vector<InputFile> InputFilesLoaded;
 
+  /// \brief If non-zero, specifies the time when we last validated input
+  /// files.  Zero means we never validated them.
+  ///
+  /// The time is specified in milliseconds since the start of the Epoch.
+  uint64_t InputFilesValidationTimestamp;
+
   // === Source Locations ===
 
   /// \brief Cursor used to read source location entries.
Index: lib/Driver/Tools.cpp
===================================================================
--- lib/Driver/Tools.cpp	(revision 200973)
+++ lib/Driver/Tools.cpp	(working copy)
@@ -3286,6 +3286,8 @@
   Args.AddAllArgs(CmdArgs, options::OPT_fmodules_ignore_macro);
   Args.AddLastArg(CmdArgs, options::OPT_fmodules_prune_interval);
   Args.AddLastArg(CmdArgs, options::OPT_fmodules_prune_after);
+  Args.AddLastArg(CmdArgs,
+                  options::OPT_fmodules_skip_verify_if_validated_after);
 
   // -faccess-control is default.
   if (Args.hasFlag(options::OPT_fno_access_control,
Index: lib/Frontend/ASTUnit.cpp
===================================================================
--- lib/Frontend/ASTUnit.cpp	(revision 200973)
+++ lib/Frontend/ASTUnit.cpp	(working copy)
@@ -1473,7 +1473,7 @@
         }
 
         OverriddenFiles[R->first] = PreambleFileHash::createForFile(
-            Status.getSize(), Status.getLastModificationTime().toEpochTime());
+            Status.getSize(), Status.getLastModificationTime().msec());
       }
       for (PreprocessorOptions::remapped_file_buffer_iterator
                 R = PreprocessorOpts.remapped_file_buffer_begin(),
Index: lib/Frontend/CompilerInstance.cpp
===================================================================
--- lib/Frontend/CompilerInstance.cpp	(revision 200973)
+++ lib/Frontend/CompilerInstance.cpp	(working copy)
@@ -1061,41 +1061,38 @@
       continue;
 
     // Walk all of the files within this directory.
-    bool RemovedAllFiles = true;
     for (llvm::sys::fs::directory_iterator File(Dir->path(), EC), FileEnd;
          File != FileEnd && !EC; File.increment(EC)) {
       // We only care about module and global module index files.
-      if (llvm::sys::path::extension(File->path()) != ".pcm" &&
-          llvm::sys::path::filename(File->path()) != "modules.idx") {
-        RemovedAllFiles = false;
+      StringRef Extension = llvm::sys::path::extension(File->path());
+      if (Extension != ".pcm" && Extension != ".timestamp." &&
+          llvm::sys::path::filename(File->path()) != "modules.idx")
         continue;
-      }
 
       // Look at this file. If we can't stat it, there's nothing interesting
       // there.
-      if (::stat(File->path().c_str(), &StatBuf)) {
-        RemovedAllFiles = false;
+      if (::stat(File->path().c_str(), &StatBuf))
         continue;
-      }
 
       // If the file has been used recently enough, leave it there.
       time_t FileAccessTime = StatBuf.st_atime;
       if (CurrentTime - FileAccessTime <=
               time_t(HSOpts.ModuleCachePruneAfter)) {
-        RemovedAllFiles = false;
         continue;
       }
 
       // Remove the file.
-      bool Existed;
-      if (llvm::sys::fs::remove(File->path(), Existed) || !Existed) {
-        RemovedAllFiles = false;
-      }
+      llvm::sys::fs::remove(File->path());
+
+      // Remove the timestamp file.
+      std::string TimpestampFilename = File->path() + ".timestamp";
+      llvm::sys::fs::remove(TimpestampFilename);
     }
 
     // If we removed all of the files in the directory, remove the directory
     // itself.
-    if (RemovedAllFiles)
+    if (llvm::sys::fs::directory_iterator(Dir->path(), EC) ==
+            llvm::sys::fs::directory_iterator() && !EC)
       llvm::sys::fs::remove(Dir->path());
   }
 }
Index: lib/Frontend/CompilerInvocation.cpp
===================================================================
--- lib/Frontend/CompilerInvocation.cpp	(revision 200973)
+++ lib/Frontend/CompilerInvocation.cpp	(working copy)
@@ -925,6 +925,9 @@
       getLastArgIntValue(Args, OPT_fmodules_prune_interval, 7 * 24 * 60 * 60);
   Opts.ModuleCachePruneAfter =
       getLastArgIntValue(Args, OPT_fmodules_prune_after, 31 * 24 * 60 * 60);
+  Opts.SkipVerifyIfValidatedAfter =
+      getLastArgUInt64Value(Args, OPT_fmodules_skip_verify_if_validated_after,
+                            0);
   for (arg_iterator it = Args.filtered_begin(OPT_fmodules_ignore_macro),
                     ie = Args.filtered_end();
        it != ie; ++it) {
@@ -1819,10 +1822,11 @@
 
 namespace clang {
 
-// Declared in clang/Frontend/Utils.h.
-int getLastArgIntValue(const ArgList &Args, OptSpecifier Id, int Default,
-                       DiagnosticsEngine *Diags) {
-  int Res = Default;
+template<typename IntTy>
+static IntTy getLastArgIntValueImpl(const ArgList &Args, OptSpecifier Id,
+                                    IntTy Default,
+                                    DiagnosticsEngine *Diags) {
+  IntTy Res = Default;
   if (Arg *A = Args.getLastArg(Id)) {
     if (StringRef(A->getValue()).getAsInteger(10, Res)) {
       if (Diags)
@@ -1833,6 +1837,19 @@
   return Res;
 }
 
+
+// Declared in clang/Frontend/Utils.h.
+int getLastArgIntValue(const ArgList &Args, OptSpecifier Id, int Default,
+                       DiagnosticsEngine *Diags) {
+  return getLastArgIntValueImpl<int>(Args, Id, Default, Diags);
+}
+
+uint64_t getLastArgUInt64Value(const ArgList &Args, OptSpecifier Id,
+                               uint64_t Default,
+                               DiagnosticsEngine *Diags) {
+  return getLastArgIntValueImpl<uint64_t>(Args, Id, Default, Diags);
+}
+
 void BuryPointer(const void *Ptr) {
   // This function may be called only a small fixed amount of times per each
   // invocation, otherwise we do actually have a leak which we want to report.
Index: lib/Serialization/ASTReader.cpp
===================================================================
--- lib/Serialization/ASTReader.cpp	(revision 200973)
+++ lib/Serialization/ASTReader.cpp	(working copy)
@@ -49,6 +49,7 @@
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/SaveAndRestore.h"
+#include "llvm/Support/raw_ostream.h"
 #include "llvm/Support/system_error.h"
 #include <algorithm>
 #include <cstdio>
@@ -1827,20 +1828,33 @@
     case llvm::BitstreamEntry::Error:
       Error("malformed block record in AST file");
       return Failure;
-    case llvm::BitstreamEntry::EndBlock:
-      // Validate all of the non-system input files.
-      if (!DisableValidation) {
+    case llvm::BitstreamEntry::EndBlock: {
+      // Validate input files.
+      uint64_t SkipVerifyIfValidatedAfter = PP.getHeaderSearchInfo()
+                                                .getHeaderSearchOpts()
+                                                .SkipVerifyIfValidatedAfter;
+      if (!DisableValidation &&
+          (SkipVerifyIfValidatedAfter == 0 ||
+           F.InputFilesValidationTimestamp <= SkipVerifyIfValidatedAfter)) {
         bool Complain = (ClientLoadCapabilities & ARR_OutOfDate) == 0;
         // All user input files reside at the index range [0, Record[1]).
+        // The rest are system input files.
         // Record is the one from INPUT_FILE_OFFSETS.
-        for (unsigned I = 0, N = Record[1]; I < N; ++I) {
+        //
+        // If we are reading a module, we will create a verification timestamp,
+        // so we verify all input files.  Otherwise, verify only user input
+        // files.
+        unsigned LastFileToValidate =
+            F.Kind == MK_Module ? F.InputFilesLoaded.size() : Record[1];
+        for (unsigned I = 0; I < LastFileToValidate; ++I) {
           InputFile IF = getInputFile(F, I+1, Complain);
           if (!IF.getFile() || IF.isOutOfDate())
             return OutOfDate;
         }
       }
       return Success;
-      
+    }
+
     case llvm::BitstreamEntry::SubBlock:
       switch (Entry.ID) {
       case INPUT_FILES_BLOCK_ID:
@@ -2918,6 +2932,16 @@
          !hasGlobalIndex() && TriedLoadingGlobalIndex;
 }
 
+static void updateModuleTimestamp(ModuleFile &MF) {
+  // Overwrite the timestamp file contents so that file's mtime changes.
+  std::string TimestampFilename = MF.getTimestampFilename();
+  std::string ErrorInfo;
+  llvm::raw_fd_ostream OS(TimestampFilename.c_str(), ErrorInfo);
+  if (!ErrorInfo.empty())
+    return;
+  OS << "Timestamp file\n";
+}
+
 ASTReader::ASTReadResult ASTReader::ReadAST(const std::string &FileName,
                                             ModuleKind Type,
                                             SourceLocation ImportLoc,
@@ -3076,6 +3100,21 @@
                        PreviousGeneration);
   }
 
+  if (PP.getHeaderSearchInfo()
+          .getHeaderSearchOpts()
+          .SkipVerifyIfValidatedAfter != 0) {
+    // Now we are certain that the module and all modules it depends on are
+    // up to date.  Create or update timestamp files for modules that are
+    // located in the module cache (not for PCH files that could be anywhere
+    // in the filesystem).
+    for (unsigned I = 0, N = Loaded.size(); I != N; ++I) {
+      ImportedModule &M = Loaded[I];
+      if (M.Mod->Kind == MK_Module) {
+        updateModuleTimestamp(*M.Mod);
+      }
+    }
+  }
+
   return Success;
 }
 
Index: lib/Serialization/ModuleManager.cpp
===================================================================
--- lib/Serialization/ModuleManager.cpp	(revision 200973)
+++ lib/Serialization/ModuleManager.cpp	(working copy)
@@ -86,6 +86,18 @@
     NewModule = true;
     ModuleEntry = New;
 
+    New->InputFilesValidationTimestamp = 0;
+    if (New->Kind == MK_Module) {
+      std::string TimestampFilename = New->getTimestampFilename();
+      llvm::sys::fs::file_status Status;
+      // A cached stat value would be fine as well.
+      if (!FileMgr.getNoncachedStatValue(TimestampFilename, Status)) {
+        llvm::sys::TimeValue MTime = Status.getLastModificationTime();
+        New->InputFilesValidationTimestamp =
+            MTime.toEpochTime() * 1000 + MTime.milliseconds();
+      }
+    }
+
     // Load the contents of the module
     if (llvm::MemoryBuffer *Buffer = lookupBuffer(FileName)) {
       // The buffer was already provided for us.
Index: test/Modules/fmodules-skip-verify-if-validated-after.c
===================================================================
--- test/Modules/fmodules-skip-verify-if-validated-after.c	(revision 0)
+++ test/Modules/fmodules-skip-verify-if-validated-after.c	(working copy)
@@ -0,0 +1,48 @@
+#include "foo.h"
+
+// We need 'find' and 'xargs'.
+// REQUIRES: shell
+
+// Clear the module cache.
+// RUN: rm -rf %t
+// RUN: mkdir -p %t/Inputs
+// RUN: mkdir -p %t/modules-to-compare
+
+// ===
+// Create a module with system headers.
+// RUN: echo 'void meow(void);' > %t/Inputs/foo.h
+// RUN: echo 'module Foo [system] { header "foo.h" }' > %t/Inputs/module.map
+
+// ===
+// Compile the module.
+// RUN: %clang_cc1 -cc1 -fmodules -fmodules-cache-path=%t/modules-cache -fsyntax-only -I %t/Inputs -fmodules-skip-verify-if-validated-after=1390000001000 %s
+// RUN: ls -R %t/modules-cache | grep Foo.pcm.timestamp
+// RUN: find %t/modules-cache -name Foo.pcm | xargs -I {} cp {} %t/modules-to-compare/Foo-before.pcm
+
+// ===
+// Use it, and make sure that we did not recompile it.
+// RUN: %clang_cc1 -cc1 -fmodules -fmodules-cache-path=%t/modules-cache -fsyntax-only -I %t/Inputs -fmodules-skip-verify-if-validated-after=1390000001000 %s
+// RUN: ls -R %t/modules-cache | grep Foo.pcm.timestamp
+// RUN: find %t/modules-cache -name Foo.pcm | xargs -I {} cp {} %t/modules-to-compare/Foo-after.pcm
+
+// RUN: diff %t/modules-to-compare/Foo-before.pcm %t/modules-to-compare/Foo-after.pcm
+
+// ===
+// Change the sources.
+// RUN: echo 'void meow2(void);' > %t/Inputs/foo.h
+
+// ===
+// Use the module, and make sure that we did not recompile it, even though the sources changed.
+// RUN: %clang_cc1 -cc1 -fmodules -fmodules-cache-path=%t/modules-cache -fsyntax-only -I %t/Inputs -fmodules-skip-verify-if-validated-after=1390000001000 %s
+// RUN: ls -R %t/modules-cache | grep Foo.pcm.timestamp
+// RUN: find %t/modules-cache -name Foo.pcm | xargs -I {} cp {} %t/modules-to-compare/Foo-after.pcm
+
+// RUN: diff %t/modules-to-compare/Foo-before.pcm %t/modules-to-compare/Foo-after.pcm
+
+// ===
+// Recompile the module if the today's date is before 01 January 2030.
+// RUN: %clang_cc1 -cc1 -fmodules -fmodules-cache-path=%t/modules-cache -fsyntax-only -I %t/Inputs -fmodules-skip-verify-if-validated-after=1893456000000 %s
+// RUN: ls -R %t/modules-cache | grep Foo.pcm.timestamp
+// RUN: find %t/modules-cache -name Foo.pcm | xargs -I {} cp {} %t/modules-to-compare/Foo-after.pcm
+
+// RUN: not diff %t/modules-to-compare/Foo-before.pcm %t/modules-to-compare/Foo-after.pcm
