Add 'CHECK-DAG' support

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



git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@181827 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Michael Liao 2013-05-14 20:34:12 +00:00
parent 7efbbd61eb
commit 95ab326674
4 changed files with 306 additions and 23 deletions

View File

@ -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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -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

View File

@ -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]]

View File

@ -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 SourceMgr &SM, const CheckString &CheckStr,
"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(StringRef Range) {
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 SourceMgr &SM, StringRef Buffer,
// 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 SourceMgr &SM, StringRef Buffer) const {
}
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 SourceMgr &SM, StringRef Buffer,
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);