[llvm] r181827 - Add 'CHECK-DAG' support

Michael Liao michael.liao at intel.com
Tue May 14 13:34:13 PDT 2013


Author: hliao
Date: Tue May 14 15:34:12 2013
New Revision: 181827

URL: http://llvm.org/viewvc/llvm-project?rev=181827&view=rev
Log:
Add 'CHECK-DAG' support

Refer to 'FileCheck.rst'f for details of 'CHECK-DAG'.


Added:
    llvm/trunk/test/FileCheck/check-dag-xfails.txt
    llvm/trunk/test/FileCheck/check-dag.txt
Modified:
    llvm/trunk/docs/CommandGuide/FileCheck.rst
    llvm/trunk/utils/FileCheck/FileCheck.cpp

Modified: llvm/trunk/docs/CommandGuide/FileCheck.rst
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/docs/CommandGuide/FileCheck.rst?rev=181827&r1=181826&r2=181827&view=diff
==============================================================================
--- llvm/trunk/docs/CommandGuide/FileCheck.rst (original)
+++ llvm/trunk/docs/CommandGuide/FileCheck.rst Tue May 14 15:34:12 2013
@@ -194,6 +194,55 @@ can be used:
    ; CHECK: ret i8
    }
 
+The "CHECK-DAG:" directive
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If it's necessary to match strings that don't occur in a strictly sequential
+order, "``CHECK-DAG:``" could be used to verify them between two matches (or
+before the first match, or after the last match). For example, clang emits
+vtable globals in reverse order. Using ``CHECK-DAG:``, we can keep the checks
+in the natural order:
+
+.. code-block:: c++
+
+    // RUN: %clang_cc1 %s -emit-llvm -o - | FileCheck %s
+
+    struct Foo { virtual void method(); };
+    Foo f;  // emit vtable
+    // CHECK-DAG: @_ZTV3Foo =
+
+    struct Bar { virtual void method(); };
+    Bar b;
+    // CHECK-DAG: @_ZTV3Bar =
+
+
+With captured variables, ``CHECK-DAG:`` is able to match valid topological
+orderings of a DAG with edges from the definition of a variable to its use.
+It's useful, e.g., when your test cases need to match different output
+sequences from the instruction scheduler. For example,
+
+.. code-block:: llvm
+
+   ; CHECK-DAG: add [[REG1:r[0-9]+]], r1, r2
+   ; CHECK-DAG: add [[REG2:r[0-9]+]], r3, r4
+   ; CHECK:     mul r5, [[REG1]], [[REG2]]
+
+In this case, any order of that two ``add`` instructions will be allowed.
+
+``CHECK-NOT:`` directives could be mixed with ``CHECK-DAG:`` directives to
+exclude strings between the surrounding ``CHECK-DAG:`` directives. As a result,
+the surrounding ``CHECK-DAG:`` directives cannot be reordered, i.e. all
+occurrences matching ``CHECK-DAG:`` before ``CHECK-NOT:`` must not fall behind
+occurrences matching ``CHECK-DAG:`` after ``CHECK-NOT:``. For example,
+
+.. code-block:: llvm
+
+   ; CHECK-DAG: BEFORE
+   ; CHECK-NOT: NOT
+   ; CHECK-DAG: AFTER
+
+This case will reject input strings where ``BEFORE`` occurs after ``AFTER``.
+
 FileCheck Pattern Matching Syntax
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

Added: llvm/trunk/test/FileCheck/check-dag-xfails.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/FileCheck/check-dag-xfails.txt?rev=181827&view=auto
==============================================================================
--- llvm/trunk/test/FileCheck/check-dag-xfails.txt (added)
+++ llvm/trunk/test/FileCheck/check-dag-xfails.txt Tue May 14 15:34:12 2013
@@ -0,0 +1,85 @@
+; RUN: not FileCheck -check-prefix=X1 -input-file %s %s
+; RUN: not FileCheck -check-prefix=X2 -input-file %s %s
+; RUN: not FileCheck -check-prefix=X3 -input-file %s %s
+; RUN: not FileCheck -check-prefix=X4 -input-file %s %s
+; RUN: not FileCheck -check-prefix=X5 -input-file %s %s
+; RUN: not FileCheck -check-prefix=X6 -input-file %s %s
+
+__x1
+add r10, r1, r2
+add r11, r3, r4
+mul r5, r10, r12
+__x1
+
+; X1: __x1
+; X1-DAG: add [[REG1:r[0-9]+]], r1, r2
+; X1-DAG: add [[REG2:r[0-9]+]], r3, r4
+; X1: mul r5, [[REG1]], [[REG2]]
+; X1: __x1
+
+__x2
+mul r11, r3, r4
+mul r10, r1, r2
+add r5, r11, r11
+__x2
+
+; X2: __x2
+; X2-DAG: mul [[REG1:r[0-9]+]], r1, r2
+; X2-DAG: mul [[REG2:r[0-9]+]], r3, r4
+; X2: add r5, [[REG1]], [[REG2]]
+; X2: __x2
+
+__x3
+add r11, r3, r4
+add r12, r1, r2
+mul r5, r10, r11
+__x3
+
+; X3: __x3
+; X3-DAG: add [[REG1:r[0-9]+]], r1, r2
+; X3-DAG: add [[REG2:r[0-9]+]], r3, r4
+; X3-DAG: mul r5, [[REG1]], [[REG2]]
+; X3: __x3
+
+__x4
+add r11, r3, r4
+add r12, r1, r2
+not
+mul r5, r12, r11
+__x4
+
+; X4: __x4
+; X4-DAG: add [[REG1:r[0-9]+]], r1, r2
+; X4-DAG: add [[REG2:r[0-9]+]], r3, r4
+; X4-NOT: not
+; X4-DAG: mul r5, [[REG1]], [[REG2]]
+; X4: __x4
+
+__x5
+mul r5, r12, r11
+add r11, r3, r4
+add r12, r1, r2
+not
+__x5
+
+; X5: __x5
+; X5-DAG: add [[REG1:r[0-9]+]], r1, r2
+; X5-DAG: add [[REG2:r[0-9]+]], r3, r4
+; X5-NOT: not
+; X5-DAG: mul r5, [[REG1]], [[REG2]]
+; X5: __x5
+
+__x6
+add r11, r3, r4
+mul r6, r12, r11
+add r12, r1, r2
+mul r5, r12, r11
+__x6
+
+; X6: __x6
+; X6-DAG: add [[REG1:r[0-9]+]], r1, r2
+; X6-DAG: add [[REG2:r[0-9]+]], r3, r4
+; X6-NOT: not
+; X6-DAG: mul r5, [[REG1]], [[REG2]]
+; X6-DAG: mul r6, [[REG1]], [[REG2]]
+; X6: __x6

Added: llvm/trunk/test/FileCheck/check-dag.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/FileCheck/check-dag.txt?rev=181827&view=auto
==============================================================================
--- llvm/trunk/test/FileCheck/check-dag.txt (added)
+++ llvm/trunk/test/FileCheck/check-dag.txt Tue May 14 15:34:12 2013
@@ -0,0 +1,25 @@
+; RUN: FileCheck -input-file %s %s
+
+add r10, r1, r2
+add r11, r3, r4
+mul r5, r10, r11
+
+mul r11, r3, r4
+mul r10, r1, r2
+add r5, r10, r11
+
+add r11, r3, r4
+add r10, r1, r2
+mul r5, r10, r11
+
+; CHECK-DAG: add [[REG1:r[0-9]+]], r1, r2
+; CHECK-DAG: add [[REG2:r[0-9]+]], r3, r4
+; CHECK: mul r5, [[REG1]], [[REG2]]
+
+; CHECK-DAG: mul [[REG1:r[0-9]+]], r1, r2
+; CHECK-DAG: mul [[REG2:r[0-9]+]], r3, r4
+; CHECK: add r5, [[REG1]], [[REG2]]
+
+; CHECK-DAG: add [[REG1:r[0-9]+]], r1, r2
+; CHECK-DAG: add [[REG2:r[0-9]+]], r3, r4
+; CHECK-DAG: mul r5, [[REG1]], [[REG2]]

Modified: llvm/trunk/utils/FileCheck/FileCheck.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/utils/FileCheck/FileCheck.cpp?rev=181827&r1=181826&r2=181827&view=diff
==============================================================================
--- llvm/trunk/utils/FileCheck/FileCheck.cpp (original)
+++ llvm/trunk/utils/FileCheck/FileCheck.cpp Tue May 14 15:34:12 2013
@@ -60,6 +60,12 @@ class Pattern {
   /// used for trailing CHECK-NOTs.
   bool MatchEOF;
 
+  /// MatchNot
+  bool MatchNot;
+
+  /// MatchDag
+  bool MatchDag;
+
   /// FixedStr - If non-empty, this pattern is a fixed string match with the
   /// specified fixed string.
   StringRef FixedStr;
@@ -83,7 +89,8 @@ class Pattern {
 
 public:
 
-  Pattern(bool matchEOF = false) : MatchEOF(matchEOF) { }
+  Pattern(bool matchEOF = false)
+    : MatchEOF(matchEOF), MatchNot(false), MatchDag(false) { }
 
   /// getLoc - Return the location in source code.
   SMLoc getLoc() const { return PatternLoc; }
@@ -108,6 +115,12 @@ public:
   void PrintFailureInfo(const SourceMgr &SM, StringRef Buffer,
                         const StringMap<StringRef> &VariableTable) const;
 
+  void setMatchNot(bool Not) { MatchNot = Not; }
+  bool getMatchNot() const { return MatchNot; }
+
+  void setMatchDag(bool Dag) { MatchDag = Dag; }
+  bool getMatchDag() const { return MatchDag; }
+
 private:
   static void AddFixedStringToRegEx(StringRef FixedStr, std::string &TheStr);
   bool AddRegExToRegEx(StringRef RS, unsigned &CurParen, SourceMgr &SM);
@@ -581,15 +594,15 @@ struct CheckString {
   /// to a CHECK: directive.
   bool IsCheckNext;
 
-  /// NotStrings - These are all of the strings that are disallowed from
+  /// DagNotStrings - These are all of the strings that are disallowed from
   /// occurring between this match string and the previous one (or start of
   /// file).
-  std::vector<Pattern> NotStrings;
+  std::vector<Pattern> DagNotStrings;
 
   CheckString(const Pattern &P, SMLoc L, bool isCheckNext)
     : Pat(P), Loc(L), IsCheckNext(isCheckNext) {}
 
-  /// Check - Match check string and its "not strings".
+  /// Check - Match check string and its "not strings" and/or "dag strings".
   size_t Check(const SourceMgr &SM, StringRef Buffer, size_t &MatchLen,
                StringMap<StringRef> &VariableTable) const;
 
@@ -598,7 +611,13 @@ struct CheckString {
 
   /// CheckNot - Verify there's no "not strings" in the given buffer.
   bool CheckNot(const SourceMgr &SM, StringRef Buffer,
+                const std::vector<const Pattern *> &NotStrings,
                 StringMap<StringRef> &VariableTable) const;
+
+  /// CheckDag - Match "dag strings" and their mixed "not strings".
+  size_t CheckDag(const SourceMgr &SM, StringRef Buffer,
+                  std::vector<const Pattern *> &NotStrings,
+                  StringMap<StringRef> &VariableTable) const;
 };
 
 /// Canonicalize whitespaces in the input file. Line endings are replaced
@@ -663,7 +682,7 @@ static bool ReadCheckFile(SourceMgr &SM,
 
   // Find all instances of CheckPrefix followed by : in the file.
   StringRef Buffer = F->getBuffer();
-  std::vector<Pattern> NotMatches;
+  std::vector<Pattern> DagNotMatches;
 
   // LineNumber keeps track of the line on which CheckPrefix instances are
   // found.
@@ -684,7 +703,7 @@ static bool ReadCheckFile(SourceMgr &SM,
 
     // When we find a check prefix, keep track of whether we find CHECK: or
     // CHECK-NEXT:
-    bool IsCheckNext = false, IsCheckNot = false;
+    bool IsCheckNext = false, IsCheckNot = false, IsCheckDag = false;
 
     // Verify that the : is present after the prefix.
     if (Buffer[CheckPrefix.size()] == ':') {
@@ -697,6 +716,10 @@ static bool ReadCheckFile(SourceMgr &SM,
                memcmp(Buffer.data()+CheckPrefix.size(), "-NOT:", 5) == 0) {
       Buffer = Buffer.substr(CheckPrefix.size()+5);
       IsCheckNot = true;
+    } else if (Buffer.size() > CheckPrefix.size()+5 &&
+               memcmp(Buffer.data()+CheckPrefix.size(), "-DAG:", 5) == 0) {
+      Buffer = Buffer.substr(CheckPrefix.size()+5);
+      IsCheckDag = true;
     } else {
       Buffer = Buffer.substr(1);
       continue;
@@ -717,6 +740,9 @@ static bool ReadCheckFile(SourceMgr &SM,
     if (P.ParsePattern(Buffer.substr(0, EOL), SM, LineNumber))
       return true;
 
+    P.setMatchNot(IsCheckNot);
+    P.setMatchDag(IsCheckDag);
+
     Buffer = Buffer.substr(EOL);
 
     // Verify that CHECK-NEXT lines have at least one CHECK line before them.
@@ -728,9 +754,9 @@ static bool ReadCheckFile(SourceMgr &SM,
       return true;
     }
 
-    // Handle CHECK-NOT.
-    if (IsCheckNot) {
-      NotMatches.push_back(P);
+    // Handle CHECK-DAG/-NOT.
+    if (IsCheckDag || IsCheckNot) {
+      DagNotMatches.push_back(P);
       continue;
     }
 
@@ -738,15 +764,15 @@ static bool ReadCheckFile(SourceMgr &SM,
     CheckStrings.push_back(CheckString(P,
                                        PatternLoc,
                                        IsCheckNext));
-    std::swap(NotMatches, CheckStrings.back().NotStrings);
+    std::swap(DagNotMatches, CheckStrings.back().DagNotStrings);
   }
 
-  // Add an EOF pattern for any trailing CHECK-NOTs.
-  if (!NotMatches.empty()) {
+  // Add an EOF pattern for any trailing CHECK-DAG/-NOTs.
+  if (!DagNotMatches.empty()) {
     CheckStrings.push_back(CheckString(Pattern(true),
                                        SMLoc::getFromPointer(Buffer.data()),
                                        false));
-    std::swap(NotMatches, CheckStrings.back().NotStrings);
+    std::swap(DagNotMatches, CheckStrings.back().DagNotStrings);
   }
 
   if (CheckStrings.empty()) {
@@ -758,11 +784,11 @@ static bool ReadCheckFile(SourceMgr &SM,
   return false;
 }
 
-static void PrintCheckFailed(const SourceMgr &SM, const CheckString &CheckStr,
-                             StringRef Buffer,
+static void PrintCheckFailed(const SourceMgr &SM, const SMLoc &Loc,
+                             const Pattern &Pat, StringRef Buffer,
                              StringMap<StringRef> &VariableTable) {
   // Otherwise, we have an error, emit an error message.
-  SM.PrintMessage(CheckStr.Loc, SourceMgr::DK_Error,
+  SM.PrintMessage(Loc, SourceMgr::DK_Error,
                   "expected string not found in input");
 
   // Print the "scanning from here" line.  If the current position is at the
@@ -773,7 +799,13 @@ static void PrintCheckFailed(const Sourc
                   "scanning from here");
 
   // Allow the pattern to print additional information if desired.
-  CheckStr.Pat.PrintFailureInfo(SM, Buffer, VariableTable);
+  Pat.PrintFailureInfo(SM, Buffer, VariableTable);
+}
+
+static void PrintCheckFailed(const SourceMgr &SM, const CheckString &CheckStr,
+                             StringRef Buffer,
+                             StringMap<StringRef> &VariableTable) {
+  PrintCheckFailed(SM, CheckStr.Loc, CheckStr.Pat, Buffer, VariableTable);
 }
 
 /// CountNumNewlinesBetween - Count the number of newlines in the specified
@@ -799,13 +831,24 @@ static unsigned CountNumNewlinesBetween(
 size_t CheckString::Check(const SourceMgr &SM, StringRef Buffer,
                           size_t &MatchLen,
                           StringMap<StringRef> &VariableTable) const {
-  size_t MatchPos = Pat.Match(Buffer, MatchLen, VariableTable);
+  size_t LastPos = 0;
+  std::vector<const Pattern *> NotStrings;
+
+  // Match "dag strings" (with mixed "not strings" if any).
+  LastPos = CheckDag(SM, Buffer, NotStrings, VariableTable);
+  if (LastPos == StringRef::npos)
+    return StringRef::npos;
+
+  // Match itself from the last position after matching CHECK-DAG.
+  StringRef MatchBuffer = Buffer.substr(LastPos);
+  size_t MatchPos = Pat.Match(MatchBuffer, MatchLen, VariableTable);
   if (MatchPos == StringRef::npos) {
-    PrintCheckFailed(SM, *this, Buffer, VariableTable);
+    PrintCheckFailed(SM, *this, MatchBuffer, VariableTable);
     return StringRef::npos;
   }
+  MatchPos += LastPos;
 
-  StringRef SkippedRegion = Buffer.substr(0, MatchPos);
+  StringRef SkippedRegion = Buffer.substr(LastPos, MatchPos);
 
   // If this check is a "CHECK-NEXT", verify that the previous match was on
   // the previous line (i.e. that there is one newline between them).
@@ -814,7 +857,7 @@ size_t CheckString::Check(const SourceMg
 
   // If this match had "not strings", verify that they don't exist in the
   // skipped region.
-  if (CheckNot(SM, SkippedRegion, VariableTable))
+  if (CheckNot(SM, SkippedRegion, NotStrings, VariableTable))
     return StringRef::npos;
 
   return MatchPos;
@@ -857,18 +900,22 @@ bool CheckString::CheckNext(const Source
 }
 
 bool CheckString::CheckNot(const SourceMgr &SM, StringRef Buffer,
+                           const std::vector<const Pattern *> &NotStrings,
                            StringMap<StringRef> &VariableTable) const {
   for (unsigned ChunkNo = 0, e = NotStrings.size();
        ChunkNo != e; ++ChunkNo) {
+    const Pattern *Pat = NotStrings[ChunkNo];
+    assert(Pat->getMatchNot() && "Expect CHECK-NOT!");
+
     size_t MatchLen = 0;
-    size_t Pos = NotStrings[ChunkNo].Match(Buffer, MatchLen, VariableTable);
+    size_t Pos = Pat->Match(Buffer, MatchLen, VariableTable);
 
     if (Pos == StringRef::npos) continue;
 
     SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()+Pos),
                     SourceMgr::DK_Error,
                     CheckPrefix+"-NOT: string occurred!");
-    SM.PrintMessage(NotStrings[ChunkNo].getLoc(), SourceMgr::DK_Note,
+    SM.PrintMessage(Pat->getLoc(), SourceMgr::DK_Note,
                     CheckPrefix+"-NOT: pattern specified here");
     return true;
   }
@@ -876,6 +923,83 @@ bool CheckString::CheckNot(const SourceM
   return false;
 }
 
+size_t CheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
+                             std::vector<const Pattern *> &NotStrings,
+                             StringMap<StringRef> &VariableTable) const {
+  if (DagNotStrings.empty())
+    return 0;
+
+  size_t LastPos = 0;
+  size_t StartPos = LastPos;
+
+  for (unsigned ChunkNo = 0, e = DagNotStrings.size();
+       ChunkNo != e; ++ChunkNo) {
+    const Pattern &Pat = DagNotStrings[ChunkNo];
+
+    assert((Pat.getMatchDag() ^ Pat.getMatchNot()) &&
+           "Invalid CHECK-DAG or CHECK-NOT!");
+
+    if (Pat.getMatchNot()) {
+      NotStrings.push_back(&Pat);
+      continue;
+    }
+
+    assert(Pat.getMatchDag() && "Expect CHECK-DAG!");
+
+    size_t MatchLen = 0, MatchPos;
+
+    // CHECK-DAG always matches from the start.
+    StringRef MatchBuffer = Buffer.substr(StartPos);
+    MatchPos = Pat.Match(MatchBuffer, MatchLen, VariableTable);
+    // With a group of CHECK-DAGs, a single mismatching means the match on
+    // that group of CHECK-DAGs fails immediately.
+    if (MatchPos == StringRef::npos) {
+      PrintCheckFailed(SM, Pat.getLoc(), Pat, MatchBuffer, VariableTable);
+      return StringRef::npos;
+    }
+    // Re-calc it as the offset relative to the start of the original string.
+    MatchPos += StartPos;
+
+    if (!NotStrings.empty()) {
+      if (MatchPos < LastPos) {
+        // Reordered?
+        SM.PrintMessage(SMLoc::getFromPointer(Buffer.data() + MatchPos),
+                        SourceMgr::DK_Error,
+                        CheckPrefix+"-DAG: found a match of CHECK-DAG"
+                        " reordering across a CHECK-NOT");
+        SM.PrintMessage(SMLoc::getFromPointer(Buffer.data() + LastPos),
+                        SourceMgr::DK_Note,
+                        CheckPrefix+"-DAG: the farthest match of CHECK-DAG"
+                        " is found here");
+        SM.PrintMessage(NotStrings[0]->getLoc(), SourceMgr::DK_Note,
+                        CheckPrefix+"-NOT: the crossed pattern specified"
+                        " here");
+        SM.PrintMessage(Pat.getLoc(), SourceMgr::DK_Note,
+                        CheckPrefix+"-DAG: the reordered pattern specified"
+                        " here");
+        return StringRef::npos;
+      }
+      // All subsequent CHECK-DAGs should be matched from the farthest
+      // position of all precedent CHECK-DAGs (including this one.)
+      StartPos = LastPos;
+      // If there's CHECK-NOTs between two CHECK-DAGs or from CHECK to
+      // CHECK-DAG, verify that there's no 'not' strings occurred in that
+      // region.
+      StringRef SkippedRegion = Buffer.substr(LastPos, MatchPos);
+      size_t Pos = CheckNot(SM, SkippedRegion, NotStrings, VariableTable);
+      if (Pos != StringRef::npos)
+        return StringRef::npos;
+      // Clear "not strings".
+      NotStrings.clear();
+    }
+
+    // Update the last position with CHECK-DAG matches.
+    LastPos = std::max(MatchPos + MatchLen, LastPos);
+  }
+
+  return LastPos;
+}
+
 int main(int argc, char **argv) {
   sys::PrintStackTraceOnErrorSignal();
   PrettyStackTraceProgram X(argc, argv);





More information about the llvm-commits mailing list