Index: tools/clang/include/clang/AST/FunctionCache.h
==================================================================
--- tools/clang/include/clang/AST/FunctionCache.h
+++ tools/clang/include/clang/AST/FunctionCache.h
@@ -191,10 +191,15 @@
   }
 
   /// \brief Discard FunctionCache object (must be done if an evaluated result
   /// is not stored in the cache).
   void discard(CacheTable &LocalCache);
+
+  /// \brief Create a diagnostic 'note' message containing statistics for
+  /// test-cases.
+  static void DiagnosticStats(DiagnosticsEngine &Diags,
+                              SourceLocation Loc = SourceLocation());
 
   /// \brief Dump this entry, optionally with Result.
   void dump(ASTContext &Ctx, ArrayRef<const Expr*> OrigArgs,
             const APValue *Result = 0) const {
     CurrentEntry.first.dump(Ctx, OrigArgs, Result);

Index: tools/clang/include/clang/Basic/DiagnosticASTKinds.td
==================================================================
--- tools/clang/include/clang/Basic/DiagnosticASTKinds.td
+++ tools/clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -105,10 +105,12 @@
 def note_constexpr_calls_suppressed : Note<
   "(skipping %0 call%s0 in backtrace; use -fconstexpr-backtrace-limit=0 to "
   "see all)">;
 def note_constexpr_call_here : Note<"in call to '%0'">;
 def note_constexpr_circular_dependency : Note<"circular dependency detected">;
+def note_constexpr_cache_stats : Note<
+  "constexpr cache stats: %0 hits from %1 cached calls (%2kB/%3kB)">;
 
 // inline asm related.
 let CategoryName = "Inline Assembly Issue" in {
   def err_asm_invalid_escape : Error<
     "invalid %% escape in inline assembly string">;

Index: tools/clang/lib/AST/FunctionCache.cpp
==================================================================
--- tools/clang/lib/AST/FunctionCache.cpp
+++ tools/clang/lib/AST/FunctionCache.cpp
@@ -562,5 +562,13 @@
     llvm::errs() << "[[result]] ";
     Result->dump();
   }
   llvm::errs() << '\n';
 }
+
+void FunctionCache::DiagnosticStats(DiagnosticsEngine &Diags,
+                                    SourceLocation Loc) {
+  Diags.Report(Loc, diag::note_constexpr_cache_stats).setForceEmit()
+    << StatsHits << StatsCached
+    << (unsigned)(GlobalCache.getMemoryStats() / 1024)
+    << (unsigned)(StatsLocalCacheMaxMem / 1024);
+}

Index: tools/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
==================================================================
--- tools/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
+++ tools/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
@@ -9,10 +9,11 @@
 //
 // This is a concrete diagnostic client, which buffers the diagnostic messages.
 //
 //===----------------------------------------------------------------------===//
 
+#include "clang/AST/FunctionCache.h"
 #include "clang/Basic/FileManager.h"
 #include "clang/Frontend/VerifyDiagnosticConsumer.h"
 #include "clang/Frontend/FrontendDiagnostic.h"
 #include "clang/Frontend/TextDiagnosticBuffer.h"
 #include "clang/Lex/HeaderSearch.h"
@@ -221,10 +222,38 @@
       return false;
     PEnd = P;
     N = TMP;
     return true;
   }
+
+  enum tok { tok_none, tok_expected, tok_verify };
+
+  // Find the next directive token.
+  tok SearchToken() {
+    for (const char *T = C; T != End; ++T) {
+      switch (*T) {
+        case 'e':
+          if (StringRef(T, End-T).startswith("expected")) {
+            P = T;
+            C = PEnd = P + 8;
+            return tok_expected;
+          }
+          continue;
+
+        case 'v':
+          if (StringRef(T, End-T).startswith("verify")) {
+            P = T;
+            C = PEnd = P + 6;
+            return tok_verify;
+          }
+          continue;
+      }
+    }
+
+    C = P = PEnd = End;
+    return tok_none;
+  }
 
   // Return true if string literal is found.
   // When true, P marks begin-position of S in content.
   bool Search(StringRef S) {
     P = std::search(C, End, S.begin(), S.end());
@@ -262,20 +291,31 @@
 } // namespace anonymous
 
 /// ParseDirective - Go through the comment and see if it indicates expected
 /// diagnostics. If so, then put them in the appropriate directive list.
 ///
-/// Returns true if any valid directives were found.
+/// Returns true if any valid expected-* directives were found.
 static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
                            SourceLocation Pos, DiagnosticsEngine &Diags) {
   // A single comment may contain multiple directives.
-  bool FoundDirective = false;
+  bool FoundExpectedDirective = false;
   for (ParseHelper PH(S); !PH.Done();) {
-    // Search for token: expected
-    if (!PH.Search("expected"))
-      break;
-    PH.Advance();
+    // Search for next valid token.
+    switch (PH.SearchToken()) {
+      case ParseHelper::tok_expected:
+        break;
+
+      case ParseHelper::tok_verify:
+        if (PH.Next("-fncache-stats")) {
+          PH.Advance();
+          FunctionCache::DiagnosticStats(Diags, Pos);
+        }
+        continue;
+
+      case ParseHelper::tok_none:
+        return FoundExpectedDirective;
+    }
 
     // Next token: -
     if (!PH.Next("-"))
       continue;
     PH.Advance();
@@ -409,19 +449,19 @@
     Directive *D = Directive::create(RegexKind, Pos, ExpectedLoc, Text,
                                      Min, Max);
     std::string Error;
     if (D->isValid(Error)) {
       DL->push_back(D);
-      FoundDirective = true;
+      FoundExpectedDirective = true;
     } else {
       Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin),
                    diag::err_verify_invalid_content)
         << KindStr << Error;
     }
   }
 
-  return FoundDirective;
+  return FoundExpectedDirective;
 }
 
 /// HandleComment - Hook into the preprocessor and extract comments containing
 ///  expected errors and warnings.
 bool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP,

