diff options
author | Stephen Hines <srhines@google.com> | 2014-02-11 20:01:10 -0800 |
---|---|---|
committer | Stephen Hines <srhines@google.com> | 2014-02-11 20:01:10 -0800 |
commit | ce9904c6ea8fd669978a8eefb854b330eb9828ff (patch) | |
tree | 2418ee2e96ea220977c8fb74959192036ab5b133 /utils | |
parent | c27b10b198c1d9e9b51f2303994313ec2778edd7 (diff) | |
parent | dbb832b83351cec97b025b61c26536ef50c3181c (diff) | |
download | external_llvm-ce9904c6ea8fd669978a8eefb854b330eb9828ff.zip external_llvm-ce9904c6ea8fd669978a8eefb854b330eb9828ff.tar.gz external_llvm-ce9904c6ea8fd669978a8eefb854b330eb9828ff.tar.bz2 |
Merge remote-tracking branch 'upstream/release_34' into merge-20140211
Conflicts:
lib/Linker/LinkModules.cpp
lib/Support/Unix/Signals.inc
Change-Id: Ia54f291fa5dc828052d2412736e8495c1282aa64
Diffstat (limited to 'utils')
142 files changed, 2995 insertions, 1955 deletions
diff --git a/utils/FileCheck/CMakeLists.txt b/utils/FileCheck/CMakeLists.txt index fa56f92..d691ceb 100644 --- a/utils/FileCheck/CMakeLists.txt +++ b/utils/FileCheck/CMakeLists.txt @@ -4,7 +4,7 @@ add_llvm_utility(FileCheck target_link_libraries(FileCheck LLVMSupport) if( MINGW ) - target_link_libraries(FileCheck imagehlp psapi) + target_link_libraries(FileCheck imagehlp psapi shell32) endif( MINGW ) if( LLVM_ENABLE_THREADS AND HAVE_LIBPTHREAD ) target_link_libraries(FileCheck pthread) diff --git a/utils/FileCheck/FileCheck.cpp b/utils/FileCheck/FileCheck.cpp index 8d5af58..f2510d7 100644 --- a/utils/FileCheck/FileCheck.cpp +++ b/utils/FileCheck/FileCheck.cpp @@ -20,6 +20,7 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringSet.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/PrettyStackTrace.h" @@ -29,6 +30,7 @@ #include "llvm/Support/raw_ostream.h" #include "llvm/Support/system_error.h" #include <algorithm> +#include <cctype> #include <map> #include <string> #include <vector> @@ -41,30 +43,39 @@ static cl::opt<std::string> InputFilename("input-file", cl::desc("File to check (defaults to stdin)"), cl::init("-"), cl::value_desc("filename")); -static cl::opt<std::string> -CheckPrefix("check-prefix", cl::init("CHECK"), - cl::desc("Prefix to use from check file (defaults to 'CHECK')")); +static cl::list<std::string> +CheckPrefixes("check-prefix", + cl::desc("Prefix to use from check file (defaults to 'CHECK')")); static cl::opt<bool> NoCanonicalizeWhiteSpace("strict-whitespace", cl::desc("Do not treat all horizontal whitespace as equivalent")); +typedef cl::list<std::string>::const_iterator prefix_iterator; + //===----------------------------------------------------------------------===// // Pattern Handling Code. //===----------------------------------------------------------------------===// +namespace Check { + enum CheckType { + CheckNone = 0, + CheckPlain, + CheckNext, + CheckNot, + CheckDAG, + CheckLabel, + + /// MatchEOF - When set, this pattern only matches the end of file. This is + /// used for trailing CHECK-NOTs. + CheckEOF + }; +} + class Pattern { SMLoc PatternLoc; - /// MatchEOF - When set, this pattern only matches the end of file. This is - /// used for trailing CHECK-NOTs. - bool MatchEOF; - - /// MatchNot - bool MatchNot; - - /// MatchDag - bool MatchDag; + Check::CheckType CheckTy; /// FixedStr - If non-empty, this pattern is a fixed string match with the /// specified fixed string. @@ -89,17 +100,21 @@ class Pattern { public: - Pattern(bool matchEOF = false) - : MatchEOF(matchEOF), MatchNot(false), MatchDag(false) { } + Pattern(Check::CheckType Ty) + : CheckTy(Ty) { } /// getLoc - Return the location in source code. SMLoc getLoc() const { return PatternLoc; } - /// ParsePattern - Parse the given string into the Pattern. SM provides the - /// SourceMgr used for error reports, and LineNumber is the line number in - /// the input file from which the pattern string was read. - /// Returns true in case of an error, false otherwise. - bool ParsePattern(StringRef PatternStr, SourceMgr &SM, unsigned LineNumber); + /// ParsePattern - Parse the given string into the Pattern. Prefix provides + /// which prefix is being matched, SM provides the SourceMgr used for error + /// reports, and LineNumber is the line number in the input file from which + /// the pattern string was read. Returns true in case of an error, false + /// otherwise. + bool ParsePattern(StringRef PatternStr, + StringRef Prefix, + SourceMgr &SM, + unsigned LineNumber); /// Match - Match the pattern string against the input buffer Buffer. This /// returns the position that is matched or npos if there is no match. If @@ -118,11 +133,7 @@ public: bool hasVariable() const { return !(VariableUses.empty() && VariableDefs.empty()); } - void setMatchNot(bool Not) { MatchNot = Not; } - bool getMatchNot() const { return MatchNot; } - - void setMatchDag(bool Dag) { MatchDag = Dag; } - bool getMatchDag() const { return MatchDag; } + Check::CheckType getCheckTy() const { return CheckTy; } private: static void AddFixedStringToRegEx(StringRef FixedStr, std::string &TheStr); @@ -148,7 +159,9 @@ private: }; -bool Pattern::ParsePattern(StringRef PatternStr, SourceMgr &SM, +bool Pattern::ParsePattern(StringRef PatternStr, + StringRef Prefix, + SourceMgr &SM, unsigned LineNumber) { this->LineNumber = LineNumber; PatternLoc = SMLoc::getFromPointer(PatternStr.data()); @@ -162,7 +175,7 @@ bool Pattern::ParsePattern(StringRef PatternStr, SourceMgr &SM, if (PatternStr.empty()) { SM.PrintMessage(PatternLoc, SourceMgr::DK_Error, "found empty check string with prefix '" + - CheckPrefix+":'"); + Prefix + ":'"); return true; } @@ -381,7 +394,7 @@ bool Pattern::EvaluateExpression(StringRef Expr, std::string &Value) const { size_t Pattern::Match(StringRef Buffer, size_t &MatchLen, StringMap<StringRef> &VariableTable) const { // If this is the EOF pattern, match it immediately. - if (MatchEOF) { + if (CheckTy == Check::CheckEOF) { MatchLen = 0; return Buffer.size(); } @@ -590,27 +603,30 @@ struct CheckString { /// Pat - The pattern to match. Pattern Pat; + /// Prefix - Which prefix name this check matched. + StringRef Prefix; + /// Loc - The location in the match file that the check string was specified. SMLoc Loc; - /// IsCheckNext - This is true if this is a CHECK-NEXT: directive (as opposed - /// to a CHECK: directive. - bool IsCheckNext; - - /// IsCheckLabel - This is true if this is a CHECK-LABEL: directive (as - /// opposed to a CHECK: directive. - bool IsCheckLabel; + /// CheckTy - Specify what kind of check this is. e.g. CHECK-NEXT: directive, + /// as opposed to a CHECK: directive. + Check::CheckType CheckTy; /// 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> DagNotStrings; - CheckString(const Pattern &P, SMLoc L, bool isCheckNext, bool isCheckLabel) - : Pat(P), Loc(L), IsCheckNext(isCheckNext), IsCheckLabel(isCheckLabel) {} + + CheckString(const Pattern &P, + StringRef S, + SMLoc L, + Check::CheckType Ty) + : Pat(P), Prefix(S), Loc(L), CheckTy(Ty) {} /// Check - Match check string and its "not strings" and/or "dag strings". - size_t Check(const SourceMgr &SM, StringRef Buffer, bool IsLabel, + size_t Check(const SourceMgr &SM, StringRef Buffer, bool IsLabelScanMode, size_t &MatchLen, StringMap<StringRef> &VariableTable) const; /// CheckNext - Verify there is a single line in the given buffer. @@ -666,6 +682,161 @@ static MemoryBuffer *CanonicalizeInputFile(MemoryBuffer *MB, return MB2; } +static bool IsPartOfWord(char c) { + return (isalnum(c) || c == '-' || c == '_'); +} + +// Get the size of the prefix extension. +static size_t CheckTypeSize(Check::CheckType Ty) { + switch (Ty) { + case Check::CheckNone: + return 0; + + case Check::CheckPlain: + return sizeof(":") - 1; + + case Check::CheckNext: + return sizeof("-NEXT:") - 1; + + case Check::CheckNot: + return sizeof("-NOT:") - 1; + + case Check::CheckDAG: + return sizeof("-DAG:") - 1; + + case Check::CheckLabel: + return sizeof("-LABEL:") - 1; + + case Check::CheckEOF: + llvm_unreachable("Should not be using EOF size"); + } + + llvm_unreachable("Bad check type"); +} + +static Check::CheckType FindCheckType(StringRef Buffer, StringRef Prefix) { + char NextChar = Buffer[Prefix.size()]; + + // Verify that the : is present after the prefix. + if (NextChar == ':') + return Check::CheckPlain; + + if (NextChar != '-') + return Check::CheckNone; + + StringRef Rest = Buffer.drop_front(Prefix.size() + 1); + if (Rest.startswith("NEXT:")) + return Check::CheckNext; + + if (Rest.startswith("NOT:")) + return Check::CheckNot; + + if (Rest.startswith("DAG:")) + return Check::CheckDAG; + + if (Rest.startswith("LABEL:")) + return Check::CheckLabel; + + return Check::CheckNone; +} + +// From the given position, find the next character after the word. +static size_t SkipWord(StringRef Str, size_t Loc) { + while (Loc < Str.size() && IsPartOfWord(Str[Loc])) + ++Loc; + return Loc; +} + +// Try to find the first match in buffer for any prefix. If a valid match is +// found, return that prefix and set its type and location. If there are almost +// matches (e.g. the actual prefix string is found, but is not an actual check +// string), but no valid match, return an empty string and set the position to +// resume searching from. If no partial matches are found, return an empty +// string and the location will be StringRef::npos. If one prefix is a substring +// of another, the maximal match should be found. e.g. if "A" and "AA" are +// prefixes then AA-CHECK: should match the second one. +static StringRef FindFirstCandidateMatch(StringRef &Buffer, + Check::CheckType &CheckTy, + size_t &CheckLoc) { + StringRef FirstPrefix; + size_t FirstLoc = StringRef::npos; + size_t SearchLoc = StringRef::npos; + Check::CheckType FirstTy = Check::CheckNone; + + CheckTy = Check::CheckNone; + CheckLoc = StringRef::npos; + + for (prefix_iterator I = CheckPrefixes.begin(), E = CheckPrefixes.end(); + I != E; ++I) { + StringRef Prefix(*I); + size_t PrefixLoc = Buffer.find(Prefix); + + if (PrefixLoc == StringRef::npos) + continue; + + // Track where we are searching for invalid prefixes that look almost right. + // We need to only advance to the first partial match on the next attempt + // since a partial match could be a substring of a later, valid prefix. + // Need to skip to the end of the word, otherwise we could end up + // matching a prefix in a substring later. + if (PrefixLoc < SearchLoc) + SearchLoc = SkipWord(Buffer, PrefixLoc); + + // We only want to find the first match to avoid skipping some. + if (PrefixLoc > FirstLoc) + continue; + // If one matching check-prefix is a prefix of another, choose the + // longer one. + if (PrefixLoc == FirstLoc && Prefix.size() < FirstPrefix.size()) + continue; + + StringRef Rest = Buffer.drop_front(PrefixLoc); + // Make sure we have actually found the prefix, and not a word containing + // it. This should also prevent matching the wrong prefix when one is a + // substring of another. + if (PrefixLoc != 0 && IsPartOfWord(Buffer[PrefixLoc - 1])) + continue; + + FirstLoc = PrefixLoc; + FirstTy = FindCheckType(Rest, Prefix); + FirstPrefix = Prefix; + } + + // If the first prefix is invalid, we should continue the search after it. + if (FirstTy == Check::CheckNone) { + CheckLoc = SearchLoc; + return ""; + } + + CheckTy = FirstTy; + CheckLoc = FirstLoc; + return FirstPrefix; +} + +static StringRef FindFirstMatchingPrefix(StringRef &Buffer, + unsigned &LineNumber, + Check::CheckType &CheckTy, + size_t &CheckLoc) { + while (!Buffer.empty()) { + StringRef Prefix = FindFirstCandidateMatch(Buffer, CheckTy, CheckLoc); + // If we found a real match, we are done. + if (!Prefix.empty()) { + LineNumber += Buffer.substr(0, CheckLoc).count('\n'); + return Prefix; + } + + // We didn't find any almost matches either, we are also done. + if (CheckLoc == StringRef::npos) + return StringRef(); + + LineNumber += Buffer.substr(0, CheckLoc + 1).count('\n'); + + // Advance to the last possible match we found and try again. + Buffer = Buffer.drop_front(CheckLoc + 1); + } + + return StringRef(); +} /// ReadCheckFile - Read the check file, which specifies the sequence of /// expected strings. The strings are added to the CheckStrings vector. @@ -696,49 +867,27 @@ static bool ReadCheckFile(SourceMgr &SM, unsigned LineNumber = 1; while (1) { - // See if Prefix occurs in the memory buffer. - size_t PrefixLoc = Buffer.find(CheckPrefix); - // If we didn't find a match, we're done. - if (PrefixLoc == StringRef::npos) + Check::CheckType CheckTy; + size_t PrefixLoc; + + // See if a prefix occurs in the memory buffer. + StringRef UsedPrefix = FindFirstMatchingPrefix(Buffer, + LineNumber, + CheckTy, + PrefixLoc); + if (UsedPrefix.empty()) break; - LineNumber += Buffer.substr(0, PrefixLoc).count('\n'); - - Buffer = Buffer.substr(PrefixLoc); - - const char *CheckPrefixStart = Buffer.data(); - - // When we find a check prefix, keep track of whether we find CHECK: or - // CHECK-NEXT: - bool IsCheckNext = false, IsCheckNot = false, IsCheckDag = false, - IsCheckLabel = false; - - // Verify that the : is present after the prefix. - if (Buffer[CheckPrefix.size()] == ':') { - Buffer = Buffer.substr(CheckPrefix.size()+1); - } else if (Buffer.size() > CheckPrefix.size()+6 && - memcmp(Buffer.data()+CheckPrefix.size(), "-NEXT:", 6) == 0) { - Buffer = Buffer.substr(CheckPrefix.size()+6); - IsCheckNext = true; - } else if (Buffer.size() > CheckPrefix.size()+5 && - 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 if (Buffer.size() > CheckPrefix.size()+7 && - memcmp(Buffer.data()+CheckPrefix.size(), "-LABEL:", 7) == 0) { - Buffer = Buffer.substr(CheckPrefix.size()+7); - IsCheckLabel = true; - } else { - Buffer = Buffer.substr(1); - continue; - } + Buffer = Buffer.drop_front(PrefixLoc); + + // Location to use for error messages. + const char *UsedPrefixStart = Buffer.data() + (PrefixLoc == 0 ? 0 : 1); - // Okay, we found the prefix, yay. Remember the rest of the line, but - // ignore leading and trailing whitespace. + // PrefixLoc is to the start of the prefix. Skip to the end. + Buffer = Buffer.drop_front(UsedPrefix.size() + CheckTypeSize(CheckTy)); + + // Okay, we found the prefix, yay. Remember the rest of the line, but ignore + // leading and trailing whitespace. Buffer = Buffer.substr(Buffer.find_first_not_of(" \t")); // Scan ahead to the end of line. @@ -748,59 +897,65 @@ static bool ReadCheckFile(SourceMgr &SM, SMLoc PatternLoc = SMLoc::getFromPointer(Buffer.data()); // Parse the pattern. - Pattern P; - if (P.ParsePattern(Buffer.substr(0, EOL), SM, LineNumber)) + Pattern P(CheckTy); + if (P.ParsePattern(Buffer.substr(0, EOL), UsedPrefix, SM, LineNumber)) return true; // Verify that CHECK-LABEL lines do not define or use variables - if (IsCheckLabel && P.hasVariable()) { - SM.PrintMessage(SMLoc::getFromPointer(CheckPrefixStart), + if ((CheckTy == Check::CheckLabel) && P.hasVariable()) { + SM.PrintMessage(SMLoc::getFromPointer(UsedPrefixStart), SourceMgr::DK_Error, - "found '"+CheckPrefix+"-LABEL:' with variable definition" - " or use'"); + "found '" + UsedPrefix + "-LABEL:'" + " with variable definition or use"); 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. - if (IsCheckNext && CheckStrings.empty()) { - SM.PrintMessage(SMLoc::getFromPointer(CheckPrefixStart), + if ((CheckTy == Check::CheckNext) && CheckStrings.empty()) { + SM.PrintMessage(SMLoc::getFromPointer(UsedPrefixStart), SourceMgr::DK_Error, - "found '"+CheckPrefix+"-NEXT:' without previous '"+ - CheckPrefix+ ": line"); + "found '" + UsedPrefix + "-NEXT:' without previous '" + + UsedPrefix + ": line"); return true; } // Handle CHECK-DAG/-NOT. - if (IsCheckDag || IsCheckNot) { + if (CheckTy == Check::CheckDAG || CheckTy == Check::CheckNot) { DagNotMatches.push_back(P); continue; } // Okay, add the string we captured to the output vector and move on. CheckStrings.push_back(CheckString(P, + UsedPrefix, PatternLoc, - IsCheckNext, - IsCheckLabel)); + CheckTy)); std::swap(DagNotMatches, CheckStrings.back().DagNotStrings); } - // Add an EOF pattern for any trailing CHECK-DAG/-NOTs. + // Add an EOF pattern for any trailing CHECK-DAG/-NOTs, and use the first + // prefix as a filler for the error message. if (!DagNotMatches.empty()) { - CheckStrings.push_back(CheckString(Pattern(true), + CheckStrings.push_back(CheckString(Pattern(Check::CheckEOF), + CheckPrefixes[0], SMLoc::getFromPointer(Buffer.data()), - false, - false)); + Check::CheckEOF)); std::swap(DagNotMatches, CheckStrings.back().DagNotStrings); } if (CheckStrings.empty()) { - errs() << "error: no check strings found with prefix '" << CheckPrefix - << ":'\n"; + errs() << "error: no check strings found with prefix" + << (CheckPrefixes.size() > 1 ? "es " : " "); + for (size_t I = 0, N = CheckPrefixes.size(); I != N; ++I) { + StringRef Prefix(CheckPrefixes[I]); + errs() << '\'' << Prefix << ":'"; + if (I != N - 1) + errs() << ", "; + } + + errs() << '\n'; return true; } @@ -852,12 +1007,16 @@ static unsigned CountNumNewlinesBetween(StringRef Range) { } size_t CheckString::Check(const SourceMgr &SM, StringRef Buffer, - bool IsLabel, size_t &MatchLen, + bool IsLabelScanMode, size_t &MatchLen, StringMap<StringRef> &VariableTable) const { size_t LastPos = 0; std::vector<const Pattern *> NotStrings; - if (!IsLabel) { + // IsLabelScanMode is true when we are scanning forward to find CHECK-LABEL + // bounds; we have not processed variable definitions within the bounded block + // yet so cannot handle any final CHECK-DAG yet; this is handled when going + // over the block again (including the last CHECK-LABEL) in normal mode. + if (!IsLabelScanMode) { // Match "dag strings" (with mixed "not strings" if any). LastPos = CheckDag(SM, Buffer, NotStrings, VariableTable); if (LastPos == StringRef::npos) @@ -873,7 +1032,9 @@ size_t CheckString::Check(const SourceMgr &SM, StringRef Buffer, } MatchPos += LastPos; - if (!IsLabel) { + // Similar to the above, in "label-scan mode" we can't yet handle CHECK-NEXT + // or CHECK-NOT + if (!IsLabelScanMode) { StringRef SkippedRegion = Buffer.substr(LastPos, MatchPos); // If this check is a "CHECK-NEXT", verify that the previous match was on @@ -891,7 +1052,7 @@ size_t CheckString::Check(const SourceMgr &SM, StringRef Buffer, } bool CheckString::CheckNext(const SourceMgr &SM, StringRef Buffer) const { - if (!IsCheckNext) + if (CheckTy != Check::CheckNext) return false; // Count the number of newlines between the previous match and this one. @@ -904,7 +1065,7 @@ bool CheckString::CheckNext(const SourceMgr &SM, StringRef Buffer) const { unsigned NumNewLines = CountNumNewlinesBetween(Buffer); if (NumNewLines == 0) { - SM.PrintMessage(Loc, SourceMgr::DK_Error, CheckPrefix+ + SM.PrintMessage(Loc, SourceMgr::DK_Error, Prefix + "-NEXT: is on the same line as previous match"); SM.PrintMessage(SMLoc::getFromPointer(Buffer.end()), SourceMgr::DK_Note, "'next' match was here"); @@ -914,7 +1075,7 @@ bool CheckString::CheckNext(const SourceMgr &SM, StringRef Buffer) const { } if (NumNewLines != 1) { - SM.PrintMessage(Loc, SourceMgr::DK_Error, CheckPrefix+ + SM.PrintMessage(Loc, SourceMgr::DK_Error, Prefix + "-NEXT: is not on the line after the previous match"); SM.PrintMessage(SMLoc::getFromPointer(Buffer.end()), SourceMgr::DK_Note, "'next' match was here"); @@ -932,7 +1093,7 @@ bool CheckString::CheckNot(const SourceMgr &SM, StringRef Buffer, for (unsigned ChunkNo = 0, e = NotStrings.size(); ChunkNo != e; ++ChunkNo) { const Pattern *Pat = NotStrings[ChunkNo]; - assert(Pat->getMatchNot() && "Expect CHECK-NOT!"); + assert((Pat->getCheckTy() == Check::CheckNot) && "Expect CHECK-NOT!"); size_t MatchLen = 0; size_t Pos = Pat->Match(Buffer, MatchLen, VariableTable); @@ -941,9 +1102,9 @@ bool CheckString::CheckNot(const SourceMgr &SM, StringRef Buffer, SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()+Pos), SourceMgr::DK_Error, - CheckPrefix+"-NOT: string occurred!"); + Prefix + "-NOT: string occurred!"); SM.PrintMessage(Pat->getLoc(), SourceMgr::DK_Note, - CheckPrefix+"-NOT: pattern specified here"); + Prefix + "-NOT: pattern specified here"); return true; } @@ -963,15 +1124,16 @@ size_t CheckString::CheckDag(const SourceMgr &SM, StringRef Buffer, ChunkNo != e; ++ChunkNo) { const Pattern &Pat = DagNotStrings[ChunkNo]; - assert((Pat.getMatchDag() ^ Pat.getMatchNot()) && + assert((Pat.getCheckTy() == Check::CheckDAG || + Pat.getCheckTy() == Check::CheckNot) && "Invalid CHECK-DAG or CHECK-NOT!"); - if (Pat.getMatchNot()) { + if (Pat.getCheckTy() == Check::CheckNot) { NotStrings.push_back(&Pat); continue; } - assert(Pat.getMatchDag() && "Expect CHECK-DAG!"); + assert((Pat.getCheckTy() == Check::CheckDAG) && "Expect CHECK-DAG!"); size_t MatchLen = 0, MatchPos; @@ -992,17 +1154,17 @@ size_t CheckString::CheckDag(const SourceMgr &SM, StringRef Buffer, // Reordered? SM.PrintMessage(SMLoc::getFromPointer(Buffer.data() + MatchPos), SourceMgr::DK_Error, - CheckPrefix+"-DAG: found a match of CHECK-DAG" + Prefix + "-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" + Prefix + "-DAG: the farthest match of CHECK-DAG" " is found here"); SM.PrintMessage(NotStrings[0]->getLoc(), SourceMgr::DK_Note, - CheckPrefix+"-NOT: the crossed pattern specified" + Prefix + "-NOT: the crossed pattern specified" " here"); SM.PrintMessage(Pat.getLoc(), SourceMgr::DK_Note, - CheckPrefix+"-DAG: the reordered pattern specified" + Prefix + "-DAG: the reordered pattern specified" " here"); return StringRef::npos; } @@ -1026,11 +1188,50 @@ size_t CheckString::CheckDag(const SourceMgr &SM, StringRef Buffer, return LastPos; } +// A check prefix must contain only alphanumeric, hyphens and underscores. +static bool ValidateCheckPrefix(StringRef CheckPrefix) { + Regex Validator("^[a-zA-Z0-9_-]*$"); + return Validator.match(CheckPrefix); +} + +static bool ValidateCheckPrefixes() { + StringSet<> PrefixSet; + + for (prefix_iterator I = CheckPrefixes.begin(), E = CheckPrefixes.end(); + I != E; ++I) { + StringRef Prefix(*I); + + if (!PrefixSet.insert(Prefix)) + return false; + + if (!ValidateCheckPrefix(Prefix)) + return false; + } + + return true; +} + +// I don't think there's a way to specify an initial value for cl::list, +// so if nothing was specified, add the default +static void AddCheckPrefixIfNeeded() { + if (CheckPrefixes.empty()) + CheckPrefixes.push_back("CHECK"); +} + int main(int argc, char **argv) { sys::PrintStackTraceOnErrorSignal(); PrettyStackTraceProgram X(argc, argv); cl::ParseCommandLineOptions(argc, argv); + if (!ValidateCheckPrefixes()) { + errs() << "Supplied check-prefix is invalid! Prefixes must be unique and " + "start with a letter and contain only alphanumeric characters, " + "hyphens and underscores\n"; + return 2; + } + + AddCheckPrefixIfNeeded(); + SourceMgr SM; // Read the expected strings from the check file. @@ -1076,7 +1277,7 @@ int main(int argc, char **argv) { CheckRegion = Buffer; } else { const CheckString &CheckLabelStr = CheckStrings[j]; - if (!CheckLabelStr.IsCheckLabel) { + if (CheckLabelStr.CheckTy != Check::CheckLabel) { ++j; continue; } diff --git a/utils/FileUpdate/CMakeLists.txt b/utils/FileUpdate/CMakeLists.txt index 655aaec..0114e50 100644 --- a/utils/FileUpdate/CMakeLists.txt +++ b/utils/FileUpdate/CMakeLists.txt @@ -4,7 +4,7 @@ add_llvm_utility(FileUpdate target_link_libraries(FileUpdate LLVMSupport) if( MINGW ) - target_link_libraries(FileUpdate imagehlp psapi) + target_link_libraries(FileUpdate imagehlp psapi shell32) endif( MINGW ) if( LLVM_ENABLE_THREADS AND HAVE_LIBPTHREAD ) target_link_libraries(FileUpdate pthread) diff --git a/utils/GetRepositoryPath b/utils/GetRepositoryPath index f3b0cc5..2d1122e 100755 --- a/utils/GetRepositoryPath +++ b/utils/GetRepositoryPath @@ -15,11 +15,11 @@ fi cd $1 if [ -d .svn ]; then - svn info | grep 'URL:' | cut -d: -f2- + svn info | grep '^URL:' | cut -d: -f2- elif [ -f .git/svn/.metadata ]; then git svn info | grep 'URL:' | cut -d: -f2- elif [ -d .git ]; then - git remote -v | grep 'fetch' | awk '{ print $2 }' + git remote -v | grep 'fetch' | awk '{ print $2 }' | head -n1 else exit 1; fi diff --git a/utils/Misc/mergefunctions.clang.svn.patch b/utils/Misc/mergefunctions.clang.svn.patch new file mode 100644 index 0000000..6e2f0f5 --- /dev/null +++ b/utils/Misc/mergefunctions.clang.svn.patch @@ -0,0 +1,14 @@ +Index: lib/CodeGen/BackendUtil.cpp +=================================================================== +--- lib/CodeGen/BackendUtil.cpp (revision 191330) ++++ lib/CodeGen/BackendUtil.cpp (working copy) +@@ -336,6 +336,9 @@ + MPM->add(createStripSymbolsPass(true)); + } + ++ // Force MergeFunctions pass. ++ MPM->add(createMergeFunctionsPass()); ++ + PMBuilder.populateModulePassManager(*MPM); + } + diff --git a/utils/TableGen/AsmMatcherEmitter.cpp b/utils/TableGen/AsmMatcherEmitter.cpp index 468ce1c..de24cde 100644 --- a/utils/TableGen/AsmMatcherEmitter.cpp +++ b/utils/TableGen/AsmMatcherEmitter.cpp @@ -97,7 +97,6 @@ //===----------------------------------------------------------------------===// #include "CodeGenTarget.h" -#include "StringToOffsetTable.h" #include "llvm/ADT/OwningPtr.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/STLExtras.h" @@ -110,8 +109,10 @@ #include "llvm/TableGen/Error.h" #include "llvm/TableGen/Record.h" #include "llvm/TableGen/StringMatcher.h" +#include "llvm/TableGen/StringToOffsetTable.h" #include "llvm/TableGen/TableGenBackend.h" #include <cassert> +#include <cctype> #include <map> #include <set> #include <sstream> @@ -125,6 +126,13 @@ namespace { class AsmMatcherInfo; struct SubtargetFeatureInfo; +// Register sets are used as keys in some second-order sets TableGen creates +// when generating its data structures. This means that the order of two +// RegisterSets can be seen in the outputted AsmMatcher tables occasionally, and +// can even affect compiler output (at least seen in diagnostics produced when +// all matches fail). So we use a type that sorts them consistently. +typedef std::set<Record*, LessRecordByID> RegisterSet; + class AsmMatcherEmitter { RecordKeeper &Records; public: @@ -185,7 +193,7 @@ struct ClassInfo { std::string ParserMethod; /// For register classes, the records for all the registers in this class. - std::set<Record*> Registers; + RegisterSet Registers; /// For custom match classes, he diagnostic kind for when the predicate fails. std::string DiagnosticType; @@ -213,11 +221,11 @@ public: if (!isRegisterClass() || !RHS.isRegisterClass()) return false; - std::set<Record*> Tmp; - std::insert_iterator< std::set<Record*> > II(Tmp, Tmp.begin()); + RegisterSet Tmp; + std::insert_iterator<RegisterSet> II(Tmp, Tmp.begin()); std::set_intersection(Registers.begin(), Registers.end(), RHS.Registers.begin(), RHS.Registers.end(), - II); + II, LessRecordByID()); return !Tmp.empty(); } @@ -430,6 +438,9 @@ struct MatchableInfo { /// function. std::string ConversionFnKind; + /// If this instruction is deprecated in some form. + bool HasDeprecation; + MatchableInfo(const CodeGenInstruction &CGI) : AsmVariantID(0), TheDef(CGI.TheDef), DefRec(&CGI), AsmString(CGI.AsmString) { @@ -613,7 +624,7 @@ public: RegisterClassesTy RegisterClasses; /// Map of Predicate records to their subtarget information. - std::map<Record*, SubtargetFeatureInfo*> SubtargetFeatures; + std::map<Record*, SubtargetFeatureInfo*, LessRecordByID> SubtargetFeatures; /// Map of AsmOperandClass records to their class information. std::map<Record*, ClassInfo*> AsmOperandClasses; @@ -663,7 +674,7 @@ public: /// given operand. SubtargetFeatureInfo *getSubtargetFeature(Record *Def) const { assert(Def->isSubClassOf("Predicate") && "Invalid predicate type!"); - std::map<Record*, SubtargetFeatureInfo*>::const_iterator I = + std::map<Record*, SubtargetFeatureInfo*, LessRecordByID>::const_iterator I = SubtargetFeatures.find(Def); return I == SubtargetFeatures.end() ? 0 : I->second; } @@ -779,6 +790,13 @@ void MatchableInfo::initialize(const AsmMatcherInfo &Info, if (Record *Reg = AsmOperands[i].SingletonReg) SingletonRegisters.insert(Reg); } + + const RecordVal *DepMask = TheDef->getValue("DeprecatedFeatureMask"); + if (!DepMask) + DepMask = TheDef->getValue("ComplexDeprecationPredicate"); + + HasDeprecation = + DepMask ? !DepMask->getValue()->getAsUnquotedString().empty() : false; } /// tokenizeAsmString - Tokenize a simplified assembly string. @@ -1047,6 +1065,18 @@ AsmMatcherInfo::getOperandClass(Record *Rec, int SubOpIdx) { PrintFatalError(Rec->getLoc(), "operand has no match class!"); } +struct LessRegisterSet { + bool operator() (const RegisterSet &LHS, const RegisterSet & RHS) const { + // std::set<T> defines its own compariso "operator<", but it + // performs a lexicographical comparison by T's innate comparison + // for some reason. We don't want non-deterministic pointer + // comparisons so use this instead. + return std::lexicographical_compare(LHS.begin(), LHS.end(), + RHS.begin(), RHS.end(), + LessRecordByID()); + } +}; + void AsmMatcherInfo:: buildRegisterClasses(SmallPtrSet<Record*, 16> &SingletonRegisters) { const std::vector<CodeGenRegister*> &Registers = @@ -1054,33 +1084,35 @@ buildRegisterClasses(SmallPtrSet<Record*, 16> &SingletonRegisters) { ArrayRef<CodeGenRegisterClass*> RegClassList = Target.getRegBank().getRegClasses(); + typedef std::set<RegisterSet, LessRegisterSet> RegisterSetSet; + // The register sets used for matching. - std::set< std::set<Record*> > RegisterSets; + RegisterSetSet RegisterSets; // Gather the defined sets. for (ArrayRef<CodeGenRegisterClass*>::const_iterator it = - RegClassList.begin(), ie = RegClassList.end(); it != ie; ++it) - RegisterSets.insert(std::set<Record*>( + RegClassList.begin(), ie = RegClassList.end(); it != ie; ++it) + RegisterSets.insert(RegisterSet( (*it)->getOrder().begin(), (*it)->getOrder().end())); // Add any required singleton sets. for (SmallPtrSet<Record*, 16>::iterator it = SingletonRegisters.begin(), ie = SingletonRegisters.end(); it != ie; ++it) { Record *Rec = *it; - RegisterSets.insert(std::set<Record*>(&Rec, &Rec + 1)); + RegisterSets.insert(RegisterSet(&Rec, &Rec + 1)); } // Introduce derived sets where necessary (when a register does not determine // a unique register set class), and build the mapping of registers to the set // they should classify to. - std::map<Record*, std::set<Record*> > RegisterMap; + std::map<Record*, RegisterSet> RegisterMap; for (std::vector<CodeGenRegister*>::const_iterator it = Registers.begin(), ie = Registers.end(); it != ie; ++it) { const CodeGenRegister &CGR = **it; // Compute the intersection of all sets containing this register. - std::set<Record*> ContainingSet; + RegisterSet ContainingSet; - for (std::set< std::set<Record*> >::iterator it = RegisterSets.begin(), + for (RegisterSetSet::iterator it = RegisterSets.begin(), ie = RegisterSets.end(); it != ie; ++it) { if (!it->count(CGR.TheDef)) continue; @@ -1090,11 +1122,12 @@ buildRegisterClasses(SmallPtrSet<Record*, 16> &SingletonRegisters) { continue; } - std::set<Record*> Tmp; + RegisterSet Tmp; std::swap(Tmp, ContainingSet); - std::insert_iterator< std::set<Record*> > II(ContainingSet, - ContainingSet.begin()); - std::set_intersection(Tmp.begin(), Tmp.end(), it->begin(), it->end(), II); + std::insert_iterator<RegisterSet> II(ContainingSet, + ContainingSet.begin()); + std::set_intersection(Tmp.begin(), Tmp.end(), it->begin(), it->end(), II, + LessRecordByID()); } if (!ContainingSet.empty()) { @@ -1104,9 +1137,9 @@ buildRegisterClasses(SmallPtrSet<Record*, 16> &SingletonRegisters) { } // Construct the register classes. - std::map<std::set<Record*>, ClassInfo*> RegisterSetClasses; + std::map<RegisterSet, ClassInfo*, LessRegisterSet> RegisterSetClasses; unsigned Index = 0; - for (std::set< std::set<Record*> >::iterator it = RegisterSets.begin(), + for (RegisterSetSet::iterator it = RegisterSets.begin(), ie = RegisterSets.end(); it != ie; ++it, ++Index) { ClassInfo *CI = new ClassInfo(); CI->Kind = ClassInfo::RegisterClass0 + Index; @@ -1124,13 +1157,14 @@ buildRegisterClasses(SmallPtrSet<Record*, 16> &SingletonRegisters) { // Find the superclasses; we could compute only the subgroup lattice edges, // but there isn't really a point. - for (std::set< std::set<Record*> >::iterator it = RegisterSets.begin(), + for (RegisterSetSet::iterator it = RegisterSets.begin(), ie = RegisterSets.end(); it != ie; ++it) { ClassInfo *CI = RegisterSetClasses[*it]; - for (std::set< std::set<Record*> >::iterator it2 = RegisterSets.begin(), + for (RegisterSetSet::iterator it2 = RegisterSets.begin(), ie2 = RegisterSets.end(); it2 != ie2; ++it2) if (*it != *it2 && - std::includes(it2->begin(), it2->end(), it->begin(), it->end())) + std::includes(it2->begin(), it2->end(), it->begin(), it->end(), + LessRecordByID())) CI->SuperClasses.push_back(RegisterSetClasses[*it2]); } @@ -1142,8 +1176,8 @@ buildRegisterClasses(SmallPtrSet<Record*, 16> &SingletonRegisters) { Record *Def = RC.getDef(); if (!Def) continue; - ClassInfo *CI = RegisterSetClasses[std::set<Record*>(RC.getOrder().begin(), - RC.getOrder().end())]; + ClassInfo *CI = RegisterSetClasses[RegisterSet(RC.getOrder().begin(), + RC.getOrder().end())]; if (CI->ValueName.empty()) { CI->ClassName = RC.getName(); CI->Name = "MCK_" + RC.getName(); @@ -1155,7 +1189,7 @@ buildRegisterClasses(SmallPtrSet<Record*, 16> &SingletonRegisters) { } // Populate the map for individual registers. - for (std::map<Record*, std::set<Record*> >::iterator it = RegisterMap.begin(), + for (std::map<Record*, RegisterSet>::iterator it = RegisterMap.begin(), ie = RegisterMap.end(); it != ie; ++it) RegisterClasses[it->first] = RegisterSetClasses[it->second]; @@ -2179,7 +2213,7 @@ static void emitSubtargetFeatureFlagEnumeration(AsmMatcherInfo &Info, OS << "// Flags for subtarget features that participate in " << "instruction matching.\n"; OS << "enum SubtargetFeatureFlag {\n"; - for (std::map<Record*, SubtargetFeatureInfo*>::const_iterator + for (std::map<Record*, SubtargetFeatureInfo*, LessRecordByID>::const_iterator it = Info.SubtargetFeatures.begin(), ie = Info.SubtargetFeatures.end(); it != ie; ++it) { SubtargetFeatureInfo &SFI = *it->second; @@ -2217,9 +2251,9 @@ static void emitGetSubtargetFeatureName(AsmMatcherInfo &Info, raw_ostream &OS) { << "static const char *getSubtargetFeatureName(unsigned Val) {\n"; if (!Info.SubtargetFeatures.empty()) { OS << " switch(Val) {\n"; - for (std::map<Record*, SubtargetFeatureInfo*>::const_iterator - it = Info.SubtargetFeatures.begin(), - ie = Info.SubtargetFeatures.end(); it != ie; ++it) { + typedef std::map<Record*, SubtargetFeatureInfo*, LessRecordByID> RecFeatMap; + for (RecFeatMap::const_iterator it = Info.SubtargetFeatures.begin(), + ie = Info.SubtargetFeatures.end(); it != ie; ++it) { SubtargetFeatureInfo &SFI = *it->second; // FIXME: Totally just a placeholder name to get the algorithm working. OS << " case " << SFI.getEnumName() << ": return \"" @@ -2244,7 +2278,7 @@ static void emitComputeAvailableFeatures(AsmMatcherInfo &Info, OS << "unsigned " << Info.Target.getName() << ClassName << "::\n" << "ComputeAvailableFeatures(uint64_t FB) const {\n"; OS << " unsigned Features = 0;\n"; - for (std::map<Record*, SubtargetFeatureInfo*>::const_iterator + for (std::map<Record*, SubtargetFeatureInfo*, LessRecordByID>::const_iterator it = Info.SubtargetFeatures.begin(), ie = Info.SubtargetFeatures.end(); it != ie; ++it) { SubtargetFeatureInfo &SFI = *it->second; @@ -2743,11 +2777,13 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { size_t MaxNumOperands = 0; unsigned MaxMnemonicIndex = 0; + bool HasDeprecation = false; for (std::vector<MatchableInfo*>::const_iterator it = Info.Matchables.begin(), ie = Info.Matchables.end(); it != ie; ++it) { MatchableInfo &II = **it; MaxNumOperands = std::max(MaxNumOperands, II.AsmOperands.size()); + HasDeprecation |= II.HasDeprecation; // Store a pascal-style length byte in the mnemonic. std::string LenMnemonic = char(II.Mnemonic.size()) + II.Mnemonic.str(); @@ -2804,9 +2840,6 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { unsigned VariantCount = Target.getAsmParserVariantCount(); for (unsigned VC = 0; VC != VariantCount; ++VC) { Record *AsmVariant = Target.getAsmParserVariant(VC); - std::string CommentDelimiter = - AsmVariant->getValueAsString("CommentDelimiter"); - std::string RegisterPrefix = AsmVariant->getValueAsString("RegisterPrefix"); int AsmVariantNo = AsmVariant->getValueAsInt("Variant"); OS << "static const MatchEntry MatchTable" << VC << "[] = {\n"; @@ -2857,9 +2890,6 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { OS << " default: // unreachable\n"; for (unsigned VC = 0; VC != VariantCount; ++VC) { Record *AsmVariant = Target.getAsmParserVariant(VC); - std::string CommentDelimiter = - AsmVariant->getValueAsString("CommentDelimiter"); - std::string RegisterPrefix = AsmVariant->getValueAsString("RegisterPrefix"); int AsmVariantNo = AsmVariant->getValueAsInt("Variant"); OS << " case " << AsmVariantNo << ": Start = MatchTable" << VC << "; End = array_endof(MatchTable" << VC << "); break;\n"; @@ -2915,9 +2945,6 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { OS << " default: // unreachable\n"; for (unsigned VC = 0; VC != VariantCount; ++VC) { Record *AsmVariant = Target.getAsmParserVariant(VC); - std::string CommentDelimiter = - AsmVariant->getValueAsString("CommentDelimiter"); - std::string RegisterPrefix = AsmVariant->getValueAsString("RegisterPrefix"); int AsmVariantNo = AsmVariant->getValueAsInt("Variant"); OS << " case " << AsmVariantNo << ": Start = MatchTable" << VC << "; End = array_endof(MatchTable" << VC << "); break;\n"; @@ -3018,6 +3045,14 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { if (!InsnCleanupFn.empty()) OS << " " << InsnCleanupFn << "(Inst);\n"; + if (HasDeprecation) { + OS << " std::string Info;\n"; + OS << " if (MII.get(Inst.getOpcode()).getDeprecatedInfo(Inst, STI, Info)) {\n"; + OS << " SMLoc Loc = ((" << Target.getName() << "Operand*)Operands[0])->getStartLoc();\n"; + OS << " Parser.Warning(Loc, Info, None);\n"; + OS << " }\n"; + } + OS << " return Match_Success;\n"; OS << " }\n\n"; diff --git a/utils/TableGen/AsmWriterEmitter.cpp b/utils/TableGen/AsmWriterEmitter.cpp index ac8d896..a18b6b5 100644 --- a/utils/TableGen/AsmWriterEmitter.cpp +++ b/utils/TableGen/AsmWriterEmitter.cpp @@ -18,6 +18,7 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/Format.h" #include "llvm/Support/MathExtras.h" #include "llvm/TableGen/Error.h" #include "llvm/TableGen/Record.h" @@ -31,10 +32,12 @@ using namespace llvm; namespace { class AsmWriterEmitter { RecordKeeper &Records; + CodeGenTarget Target; std::map<const CodeGenInstruction*, AsmWriterInst*> CGIAWIMap; std::vector<const CodeGenInstruction*> NumberedInstructions; + std::vector<AsmWriterInst> Instructions; public: - AsmWriterEmitter(RecordKeeper &R) : Records(R) {} + AsmWriterEmitter(RecordKeeper &R); void run(raw_ostream &o); @@ -272,9 +275,9 @@ static void UnescapeString(std::string &Str) { } /// EmitPrintInstruction - Generate the code for the "printInstruction" method -/// implementation. +/// implementation. Destroys all instances of AsmWriterInst information, by +/// clearing the Instructions vector. void AsmWriterEmitter::EmitPrintInstruction(raw_ostream &O) { - CodeGenTarget Target(Records); Record *AsmWriter = Target.getAsmWriter(); std::string ClassName = AsmWriter->getValueAsString("AsmWriterClassName"); bool isMC = AsmWriter->getValueAsBit("isMCAsmWriter"); @@ -287,27 +290,6 @@ void AsmWriterEmitter::EmitPrintInstruction(raw_ostream &O) { << "::printInstruction(const " << MachineInstrClassName << " *MI, raw_ostream &O) {\n"; - std::vector<AsmWriterInst> Instructions; - - for (CodeGenTarget::inst_iterator I = Target.inst_begin(), - E = Target.inst_end(); I != E; ++I) - if (!(*I)->AsmString.empty() && - (*I)->TheDef->getName() != "PHI") - Instructions.push_back( - AsmWriterInst(**I, - AsmWriter->getValueAsInt("Variant"), - AsmWriter->getValueAsInt("FirstOperandColumn"), - AsmWriter->getValueAsInt("OperandSpacing"))); - - // Get the instruction numbering. - NumberedInstructions = Target.getInstructionsByEnumValue(); - - // Compute the CodeGenInstruction -> AsmWriterInst mapping. Note that not - // all machine instructions are necessarily being printed, so there may be - // target instructions not in this map. - for (unsigned i = 0, e = Instructions.size(); i != e; ++i) - CGIAWIMap.insert(std::make_pair(Instructions[i].CGI, &Instructions[i])); - // Build an aggregate string, and build a table of offsets into it. SequenceToOffsetTable<std::string> StringTable; @@ -591,7 +573,6 @@ emitRegisterNameString(raw_ostream &O, StringRef AltName, } void AsmWriterEmitter::EmitGetRegisterName(raw_ostream &O) { - CodeGenTarget Target(Records); Record *AsmWriter = Target.getAsmWriter(); std::string ClassName = AsmWriter->getValueAsString("AsmWriterClassName"); const std::vector<CodeGenRegister*> &Registers = @@ -657,7 +638,10 @@ public: void addCond(const std::string &C) { Conds.push_back(C); } - void addOperand(StringRef Op, unsigned Idx) { OpMap[Op] = Idx; } + void addOperand(StringRef Op, unsigned Idx) { + assert(Idx < 0xFF && "Index too large!"); + OpMap[Op] = Idx; + } unsigned getOpIndex(StringRef Op) { return OpMap[Op]; } bool isOpMapped(StringRef Op) { return OpMap.find(Op) != OpMap.end(); } @@ -681,12 +665,35 @@ public: O << ") {\n"; O.indent(6) << "// " << Result << "\n"; - O.indent(6) << "AsmString = \"" << AsmString << "\";\n"; - for (std::map<StringRef, unsigned>::iterator - I = OpMap.begin(), E = OpMap.end(); I != E; ++I) - O.indent(6) << "OpMap.push_back(std::make_pair(\"" << I->first << "\", " - << I->second << "));\n"; + // Directly mangle mapped operands into the string. Each operand is + // identified by a '$' sign followed by a byte identifying the number of the + // operand. We add one to the index to avoid zero bytes. + std::pair<StringRef, StringRef> ASM = StringRef(AsmString).split(' '); + SmallString<128> OutString = ASM.first; + if (!ASM.second.empty()) { + raw_svector_ostream OS(OutString); + OS << ' '; + for (StringRef::iterator I = ASM.second.begin(), E = ASM.second.end(); + I != E;) { + OS << *I; + if (*I == '$') { + StringRef::iterator Start = ++I; + while (I != E && + ((*I >= 'a' && *I <= 'z') || (*I >= 'A' && *I <= 'Z') || + (*I >= '0' && *I <= '9') || *I == '_')) + ++I; + StringRef Name(Start, I - Start); + assert(isOpMapped(Name) && "Unmapped operand!"); + OS << format("\\x%02X", (unsigned char)getOpIndex(Name) + 1); + } else { + ++I; + } + } + } + + // Emit the string. + O.indent(6) << "AsmString = \"" << OutString.str() << "\";\n"; O.indent(6) << "break;\n"; O.indent(4) << '}'; @@ -721,19 +728,6 @@ public: } // end anonymous namespace -static void EmitGetMapOperandNumber(raw_ostream &O) { - O << "static unsigned getMapOperandNumber(" - << "const SmallVectorImpl<std::pair<StringRef, unsigned> > &OpMap,\n"; - O << " StringRef Name) {\n"; - O << " for (SmallVectorImpl<std::pair<StringRef, unsigned> >::" - << "const_iterator\n"; - O << " I = OpMap.begin(), E = OpMap.end(); I != E; ++I)\n"; - O << " if (I->first == Name)\n"; - O << " return I->second;\n"; - O << " llvm_unreachable(\"Operand not in map!\");\n"; - O << "}\n\n"; -} - static unsigned CountNumOperands(StringRef AsmString) { unsigned NumOps = 0; std::pair<StringRef, StringRef> ASM = AsmString.split(' '); @@ -768,7 +762,6 @@ static unsigned CountResultNumOperands(StringRef AsmString) { } void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) { - CodeGenTarget Target(Records); Record *AsmWriter = Target.getAsmWriter(); if (!AsmWriter->getValueAsBit("isMCAsmWriter")) @@ -822,7 +815,6 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) { Cond = std::string("MI->getNumOperands() == ") + llvm::utostr(LastOpNo); IAP->addCond(Cond); - std::map<StringRef, unsigned> OpMap; bool CantHandle = false; for (unsigned i = 0, e = LastOpNo; i != e; ++i) { @@ -955,11 +947,8 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) { return; } - EmitGetMapOperandNumber(O); - O << HeaderO.str(); - O.indent(2) << "StringRef AsmString;\n"; - O.indent(2) << "SmallVector<std::pair<StringRef, unsigned>, 4> OpMap;\n"; + O.indent(2) << "const char *AsmString;\n"; O.indent(2) << "switch (MI->getOpcode()) {\n"; O.indent(2) << "default: return false;\n"; O << CasesO.str(); @@ -967,27 +956,21 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) { // Code that prints the alias, replacing the operands with the ones from the // MCInst. - O << " std::pair<StringRef, StringRef> ASM = AsmString.split(' ');\n"; - O << " OS << '\\t' << ASM.first;\n"; + O << " unsigned I = 0;\n"; + O << " while (AsmString[I] != ' ' && AsmString[I] != '\\0')\n"; + O << " ++I;\n"; + O << " OS << '\\t' << StringRef(AsmString, I);\n"; - O << " if (!ASM.second.empty()) {\n"; + O << " if (AsmString[I] != '\\0') {\n"; O << " OS << '\\t';\n"; - O << " for (StringRef::iterator\n"; - O << " I = ASM.second.begin(), E = ASM.second.end(); I != E; ) {\n"; - O << " if (*I == '$') {\n"; - O << " StringRef::iterator Start = ++I;\n"; - O << " while (I != E &&\n"; - O << " ((*I >= 'a' && *I <= 'z') ||\n"; - O << " (*I >= 'A' && *I <= 'Z') ||\n"; - O << " (*I >= '0' && *I <= '9') ||\n"; - O << " *I == '_'))\n"; - O << " ++I;\n"; - O << " StringRef Name(Start, I - Start);\n"; - O << " printOperand(MI, getMapOperandNumber(OpMap, Name), OS);\n"; + O << " do {\n"; + O << " if (AsmString[I] == '$') {\n"; + O << " ++I;\n"; + O << " printOperand(MI, unsigned(AsmString[I++]) - 1, OS);\n"; O << " } else {\n"; - O << " OS << *I++;\n"; + O << " OS << AsmString[I++];\n"; O << " }\n"; - O << " }\n"; + O << " } while (AsmString[I] != '\\0');\n"; O << " }\n\n"; O << " return true;\n"; @@ -996,6 +979,27 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) { O << "#endif // PRINT_ALIAS_INSTR\n"; } +AsmWriterEmitter::AsmWriterEmitter(RecordKeeper &R) : Records(R), Target(R) { + Record *AsmWriter = Target.getAsmWriter(); + for (CodeGenTarget::inst_iterator I = Target.inst_begin(), + E = Target.inst_end(); + I != E; ++I) + if (!(*I)->AsmString.empty() && (*I)->TheDef->getName() != "PHI") + Instructions.push_back( + AsmWriterInst(**I, AsmWriter->getValueAsInt("Variant"), + AsmWriter->getValueAsInt("FirstOperandColumn"), + AsmWriter->getValueAsInt("OperandSpacing"))); + + // Get the instruction numbering. + NumberedInstructions = Target.getInstructionsByEnumValue(); + + // Compute the CodeGenInstruction -> AsmWriterInst mapping. Note that not + // all machine instructions are necessarily being printed, so there may be + // target instructions not in this map. + for (unsigned i = 0, e = Instructions.size(); i != e; ++i) + CGIAWIMap.insert(std::make_pair(Instructions[i].CGI, &Instructions[i])); +} + void AsmWriterEmitter::run(raw_ostream &O) { EmitPrintInstruction(O); EmitGetRegisterName(O); diff --git a/utils/TableGen/CodeGenDAGPatterns.cpp b/utils/TableGen/CodeGenDAGPatterns.cpp index ee025a0..717090a 100644 --- a/utils/TableGen/CodeGenDAGPatterns.cpp +++ b/utils/TableGen/CodeGenDAGPatterns.cpp @@ -30,16 +30,16 @@ using namespace llvm; //===----------------------------------------------------------------------===// static inline bool isInteger(MVT::SimpleValueType VT) { - return EVT(VT).isInteger(); + return MVT(VT).isInteger(); } static inline bool isFloatingPoint(MVT::SimpleValueType VT) { - return EVT(VT).isFloatingPoint(); + return MVT(VT).isFloatingPoint(); } static inline bool isVector(MVT::SimpleValueType VT) { - return EVT(VT).isVector(); + return MVT(VT).isVector(); } static inline bool isScalar(MVT::SimpleValueType VT) { - return !EVT(VT).isVector(); + return !MVT(VT).isVector(); } EEVT::TypeSet::TypeSet(MVT::SimpleValueType VT, TreePattern &TP) { @@ -385,8 +385,8 @@ bool EEVT::TypeSet::EnforceSmallerThan(EEVT::TypeSet &Other, TreePattern &TP) { // Otherwise, if these are both vector types, either this vector // must have a larger bitsize than the other, or this element type // must be larger than the other. - EVT Type(TypeVec[0]); - EVT OtherType(Other.TypeVec[0]); + MVT Type(TypeVec[0]); + MVT OtherType(Other.TypeVec[0]); if (hasVectorTypes() && Other.hasVectorTypes()) { if (Type.getSizeInBits() >= OtherType.getSizeInBits()) @@ -397,8 +397,7 @@ bool EEVT::TypeSet::EnforceSmallerThan(EEVT::TypeSet &Other, TreePattern &TP) { Other.getName() +"'!"); return false; } - } - else + } else // For scalar types, the bitsize of this type must be larger // than that of the other. if (Type.getSizeInBits() >= OtherType.getSizeInBits()) { @@ -450,8 +449,7 @@ bool EEVT::TypeSet::EnforceSmallerThan(EEVT::TypeSet &Other, TreePattern &TP) { MadeChange = true; continue; } - } - else if (isFloatingPoint(*TVI)) { + } else if (isFloatingPoint(*TVI)) { ++OtherFPSize; if (*TVI == SmallestFP) { TVI = Other.TypeVec.erase(TVI); @@ -465,8 +463,8 @@ bool EEVT::TypeSet::EnforceSmallerThan(EEVT::TypeSet &Other, TreePattern &TP) { // If this is the only type in the large set, the constraint can never be // satisfied. - if ((Other.hasIntegerTypes() && OtherIntSize == 0) - || (Other.hasFloatingPointTypes() && OtherFPSize == 0)) { + if ((Other.hasIntegerTypes() && OtherIntSize == 0) || + (Other.hasFloatingPointTypes() && OtherFPSize == 0)) { TP.error("Type inference contradiction found, '" + Other.getName() + "' has nothing larger than '" + getName() +"'!"); return false; @@ -508,8 +506,7 @@ bool EEVT::TypeSet::EnforceSmallerThan(EEVT::TypeSet &Other, TreePattern &TP) { MadeChange = true; continue; } - } - else if (isFloatingPoint(*TVI)) { + } else if (isFloatingPoint(*TVI)) { ++FPSize; if (*TVI == LargestFP) { TVI = TypeVec.erase(TVI); @@ -523,8 +520,8 @@ bool EEVT::TypeSet::EnforceSmallerThan(EEVT::TypeSet &Other, TreePattern &TP) { // If this is the only type in the small set, the constraint can never be // satisfied. - if ((hasIntegerTypes() && IntSize == 0) - || (hasFloatingPointTypes() && FPSize == 0)) { + if ((hasIntegerTypes() && IntSize == 0) || + (hasFloatingPointTypes() && FPSize == 0)) { TP.error("Type inference contradiction found, '" + getName() + "' has nothing smaller than '" + Other.getName()+"'!"); return false; @@ -547,10 +544,10 @@ bool EEVT::TypeSet::EnforceVectorEltTypeIs(EEVT::TypeSet &VTOperand, // If we know the vector type, it forces the scalar to agree. if (isConcrete()) { - EVT IVT = getConcrete(); + MVT IVT = getConcrete(); IVT = IVT.getVectorElementType(); return MadeChange | - VTOperand.MergeInTypeInfo(IVT.getSimpleVT().SimpleTy, TP); + VTOperand.MergeInTypeInfo(IVT.SimpleTy, TP); } // If the scalar type is known, filter out vector types whose element types @@ -565,7 +562,7 @@ bool EEVT::TypeSet::EnforceVectorEltTypeIs(EEVT::TypeSet &VTOperand, // Filter out all the types which don't have the right element type. for (unsigned i = 0; i != TypeVec.size(); ++i) { assert(isVector(TypeVec[i]) && "EnforceVector didn't work"); - if (EVT(TypeVec[i]).getVectorElementType().getSimpleVT().SimpleTy != VT) { + if (MVT(TypeVec[i]).getVectorElementType().SimpleTy != VT) { TypeVec.erase(TypeVec.begin()+i--); MadeChange = true; } @@ -593,16 +590,16 @@ bool EEVT::TypeSet::EnforceVectorSubVectorTypeIs(EEVT::TypeSet &VTOperand, // If we know the vector type, it forces the scalar types to agree. if (isConcrete()) { - EVT IVT = getConcrete(); + MVT IVT = getConcrete(); IVT = IVT.getVectorElementType(); - EEVT::TypeSet EltTypeSet(IVT.getSimpleVT().SimpleTy, TP); + EEVT::TypeSet EltTypeSet(IVT.SimpleTy, TP); MadeChange |= VTOperand.EnforceVectorEltTypeIs(EltTypeSet, TP); } else if (VTOperand.isConcrete()) { - EVT IVT = VTOperand.getConcrete(); + MVT IVT = VTOperand.getConcrete(); IVT = IVT.getVectorElementType(); - EEVT::TypeSet EltTypeSet(IVT.getSimpleVT().SimpleTy, TP); + EEVT::TypeSet EltTypeSet(IVT.SimpleTy, TP); MadeChange |= EnforceVectorEltTypeIs(EltTypeSet, TP); } @@ -1522,7 +1519,7 @@ bool TreePatternNode::ApplyTypeConstraints(TreePattern &TP, bool NotRegisters) { if (VT == MVT::iPTR || VT == MVT::iPTRAny) return MadeChange; - unsigned Size = EVT(VT).getSizeInBits(); + unsigned Size = MVT(VT).getSizeInBits(); // Make sure that the value is representable for this type. if (Size >= 32) return MadeChange; @@ -2678,54 +2675,13 @@ static bool checkOperandClass(CGIOperandList::OperandInfo &OI, return false; } -/// ParseInstructions - Parse all of the instructions, inlining and resolving -/// any fragments involved. This populates the Instructions list with fully -/// resolved instructions. -void CodeGenDAGPatterns::ParseInstructions() { - std::vector<Record*> Instrs = Records.getAllDerivedDefinitions("Instruction"); - - for (unsigned i = 0, e = Instrs.size(); i != e; ++i) { - ListInit *LI = 0; +const DAGInstruction &CodeGenDAGPatterns::parseInstructionPattern( + CodeGenInstruction &CGI, ListInit *Pat, DAGInstMap &DAGInsts) { - if (isa<ListInit>(Instrs[i]->getValueInit("Pattern"))) - LI = Instrs[i]->getValueAsListInit("Pattern"); - - // If there is no pattern, only collect minimal information about the - // instruction for its operand list. We have to assume that there is one - // result, as we have no detailed info. A pattern which references the - // null_frag operator is as-if no pattern were specified. Normally this - // is from a multiclass expansion w/ a SDPatternOperator passed in as - // null_frag. - if (!LI || LI->getSize() == 0 || hasNullFragReference(LI)) { - std::vector<Record*> Results; - std::vector<Record*> Operands; - - CodeGenInstruction &InstInfo = Target.getInstruction(Instrs[i]); - - if (InstInfo.Operands.size() != 0) { - if (InstInfo.Operands.NumDefs == 0) { - // These produce no results - for (unsigned j = 0, e = InstInfo.Operands.size(); j < e; ++j) - Operands.push_back(InstInfo.Operands[j].Rec); - } else { - // Assume the first operand is the result. - Results.push_back(InstInfo.Operands[0].Rec); - - // The rest are inputs. - for (unsigned j = 1, e = InstInfo.Operands.size(); j < e; ++j) - Operands.push_back(InstInfo.Operands[j].Rec); - } - } - - // Create and insert the instruction. - std::vector<Record*> ImpResults; - Instructions.insert(std::make_pair(Instrs[i], - DAGInstruction(0, Results, Operands, ImpResults))); - continue; // no pattern. - } + assert(!DAGInsts.count(CGI.TheDef) && "Instruction already parsed!"); // Parse the instruction. - TreePattern *I = new TreePattern(Instrs[i], LI, true, *this); + TreePattern *I = new TreePattern(CGI.TheDef, Pat, true, *this); // Inline pattern fragments into it. I->InlinePatternFragments(); @@ -2764,7 +2720,6 @@ void CodeGenDAGPatterns::ParseInstructions() { // Parse the operands list from the (ops) list, validating it. assert(I->getArgList().empty() && "Args list should still be empty here!"); - CodeGenInstruction &CGI = Target.getInstruction(Instrs[i]); // Check that all of the results occur first in the list. std::vector<Record*> Results; @@ -2863,18 +2818,71 @@ void CodeGenDAGPatterns::ParseInstructions() { // Create and insert the instruction. // FIXME: InstImpResults should not be part of DAGInstruction. DAGInstruction TheInst(I, Results, Operands, InstImpResults); - Instructions.insert(std::make_pair(I->getRecord(), TheInst)); + DAGInsts.insert(std::make_pair(I->getRecord(), TheInst)); // Use a temporary tree pattern to infer all types and make sure that the // constructed result is correct. This depends on the instruction already - // being inserted into the Instructions map. + // being inserted into the DAGInsts map. TreePattern Temp(I->getRecord(), ResultPattern, false, *this); Temp.InferAllTypes(&I->getNamedNodesMap()); - DAGInstruction &TheInsertedInst = Instructions.find(I->getRecord())->second; + DAGInstruction &TheInsertedInst = DAGInsts.find(I->getRecord())->second; TheInsertedInst.setResultPattern(Temp.getOnlyTree()); - DEBUG(I->dump()); + return TheInsertedInst; + } + +/// ParseInstructions - Parse all of the instructions, inlining and resolving +/// any fragments involved. This populates the Instructions list with fully +/// resolved instructions. +void CodeGenDAGPatterns::ParseInstructions() { + std::vector<Record*> Instrs = Records.getAllDerivedDefinitions("Instruction"); + + for (unsigned i = 0, e = Instrs.size(); i != e; ++i) { + ListInit *LI = 0; + + if (isa<ListInit>(Instrs[i]->getValueInit("Pattern"))) + LI = Instrs[i]->getValueAsListInit("Pattern"); + + // If there is no pattern, only collect minimal information about the + // instruction for its operand list. We have to assume that there is one + // result, as we have no detailed info. A pattern which references the + // null_frag operator is as-if no pattern were specified. Normally this + // is from a multiclass expansion w/ a SDPatternOperator passed in as + // null_frag. + if (!LI || LI->getSize() == 0 || hasNullFragReference(LI)) { + std::vector<Record*> Results; + std::vector<Record*> Operands; + + CodeGenInstruction &InstInfo = Target.getInstruction(Instrs[i]); + + if (InstInfo.Operands.size() != 0) { + if (InstInfo.Operands.NumDefs == 0) { + // These produce no results + for (unsigned j = 0, e = InstInfo.Operands.size(); j < e; ++j) + Operands.push_back(InstInfo.Operands[j].Rec); + } else { + // Assume the first operand is the result. + Results.push_back(InstInfo.Operands[0].Rec); + + // The rest are inputs. + for (unsigned j = 1, e = InstInfo.Operands.size(); j < e; ++j) + Operands.push_back(InstInfo.Operands[j].Rec); + } + } + + // Create and insert the instruction. + std::vector<Record*> ImpResults; + Instructions.insert(std::make_pair(Instrs[i], + DAGInstruction(0, Results, Operands, ImpResults))); + continue; // no pattern. + } + + CodeGenInstruction &CGI = Target.getInstruction(Instrs[i]); + const DAGInstruction &DI = parseInstructionPattern(CGI, LI, Instructions); + + (void)DI; + DEBUG(DI.getPattern()->dump()); } // If we can, convert the instructions to be patterns that are matched! diff --git a/utils/TableGen/CodeGenDAGPatterns.h b/utils/TableGen/CodeGenDAGPatterns.h index 7c2fa36..6fbdd4f 100644 --- a/utils/TableGen/CodeGenDAGPatterns.h +++ b/utils/TableGen/CodeGenDAGPatterns.h @@ -778,7 +778,11 @@ public: ptm_iterator ptm_begin() const { return PatternsToMatch.begin(); } ptm_iterator ptm_end() const { return PatternsToMatch.end(); } - + /// Parse the Pattern for an instruction, and insert the result in DAGInsts. + typedef std::map<Record*, DAGInstruction, LessRecordByID> DAGInstMap; + const DAGInstruction &parseInstructionPattern( + CodeGenInstruction &CGI, ListInit *Pattern, + DAGInstMap &DAGInsts); const DAGInstruction &getInstruction(Record *R) const { assert(Instructions.count(R) && "Unknown instruction!"); diff --git a/utils/TableGen/CodeGenInstruction.cpp b/utils/TableGen/CodeGenInstruction.cpp index 3673204..576388b 100644 --- a/utils/TableGen/CodeGenInstruction.cpp +++ b/utils/TableGen/CodeGenInstruction.cpp @@ -90,7 +90,7 @@ CGIOperandList::CGIOperandList(Record *R) : TheDef(R) { if (unsigned NumArgs = MIOpInfo->getNumArgs()) NumOps = NumArgs; - if (Rec->isSubClassOf("PredicateOperand")) + if (Rec->isSubClassOf("PredicateOp")) isPredicable = true; else if (Rec->isSubClassOf("OptionalDefOperand")) hasOptionalDef = true; @@ -192,6 +192,7 @@ CGIOperandList::ParseOperandName(const std::string &Op, bool AllowWholeOp) { // Otherwise, didn't find it! PrintFatalError(TheDef->getName() + ": unknown suboperand name in '" + Op + "'"); + return std::make_pair(0U, 0U); } static void ParseConstraint(const std::string &CStr, CGIOperandList &Ops) { @@ -336,6 +337,20 @@ CodeGenInstruction::CodeGenInstruction(Record *R) // Parse the DisableEncoding field. Operands.ProcessDisableEncoding(R->getValueAsString("DisableEncoding")); + + // First check for a ComplexDeprecationPredicate. + if (R->getValue("ComplexDeprecationPredicate")) { + HasComplexDeprecationPredicate = true; + DeprecatedReason = R->getValueAsString("ComplexDeprecationPredicate"); + } else if (RecordVal *Dep = R->getValue("DeprecatedFeatureMask")) { + // Check if we have a Subtarget feature mask. + HasComplexDeprecationPredicate = false; + DeprecatedReason = Dep->getValue()->getAsString(); + } else { + // This instruction isn't deprecated. + HasComplexDeprecationPredicate = false; + DeprecatedReason = ""; + } } /// HasOneImplicitDefWithKnownVT - If the instruction has at least one diff --git a/utils/TableGen/CodeGenInstruction.h b/utils/TableGen/CodeGenInstruction.h index d1e1153..6004f66 100644 --- a/utils/TableGen/CodeGenInstruction.h +++ b/utils/TableGen/CodeGenInstruction.h @@ -248,6 +248,9 @@ namespace llvm { bool isCodeGenOnly; bool isPseudo; + std::string DeprecatedReason; + bool HasComplexDeprecationPredicate; + /// Are there any undefined flags? bool hasUndefFlags() const { return mayLoad_Unset || mayStore_Unset || hasSideEffects_Unset; diff --git a/utils/TableGen/CodeGenMapTable.cpp b/utils/TableGen/CodeGenMapTable.cpp index ee32aa1..cb7ec3e 100644 --- a/utils/TableGen/CodeGenMapTable.cpp +++ b/utils/TableGen/CodeGenMapTable.cpp @@ -240,7 +240,6 @@ public: void MapTableEmitter::buildRowInstrMap() { for (unsigned i = 0, e = InstrDefs.size(); i < e; i++) { - std::vector<Record*> InstrList; Record *CurInstr = InstrDefs[i]; std::vector<Init*> KeyValue; ListInit *RowFields = InstrMapDesc.getRowFields(); diff --git a/utils/TableGen/CodeGenRegisters.cpp b/utils/TableGen/CodeGenRegisters.cpp index 43de2be..f2eef4f 100644 --- a/utils/TableGen/CodeGenRegisters.cpp +++ b/utils/TableGen/CodeGenRegisters.cpp @@ -813,9 +813,10 @@ static bool testSubClass(const CodeGenRegisterClass *A, /// Register classes with the same registers, spill size, and alignment form a /// clique. They will be ordered alphabetically. /// -static int TopoOrderRC(const void *PA, const void *PB) { - const CodeGenRegisterClass *A = *(const CodeGenRegisterClass* const*)PA; - const CodeGenRegisterClass *B = *(const CodeGenRegisterClass* const*)PB; +static int TopoOrderRC(CodeGenRegisterClass *const *PA, + CodeGenRegisterClass *const *PB) { + const CodeGenRegisterClass *A = *PA; + const CodeGenRegisterClass *B = *PB; if (A == B) return 0; diff --git a/utils/TableGen/CodeGenSchedule.cpp b/utils/TableGen/CodeGenSchedule.cpp index 001e97d..dd06433 100644 --- a/utils/TableGen/CodeGenSchedule.cpp +++ b/utils/TableGen/CodeGenSchedule.cpp @@ -36,10 +36,11 @@ static void dumpIdxVec(const SmallVectorImpl<unsigned> &V) { } #endif +namespace { // (instrs a, b, ...) Evaluate and union all arguments. Identical to AddOp. struct InstrsOp : public SetTheory::Operator { - void apply(SetTheory &ST, DagInit *Expr, SetTheory::RecSet &Elts, - ArrayRef<SMLoc> Loc) { + virtual void apply(SetTheory &ST, DagInit *Expr, SetTheory::RecSet &Elts, + ArrayRef<SMLoc> Loc) { ST.evaluate(Expr->arg_begin(), Expr->arg_end(), Elts, Loc); } }; @@ -84,6 +85,7 @@ struct InstRegexOp : public SetTheory::Operator { DeleteContainerPointers(RegexList); } }; +} // end anonymous namespace /// CodeGenModels ctor interprets machine model records and populates maps. CodeGenSchedModels::CodeGenSchedModels(RecordKeeper &RK, diff --git a/utils/TableGen/CodeGenTarget.cpp b/utils/TableGen/CodeGenTarget.cpp index b2c883d..dd17059 100644 --- a/utils/TableGen/CodeGenTarget.cpp +++ b/utils/TableGen/CodeGenTarget.cpp @@ -75,6 +75,7 @@ std::string llvm::getEnumName(MVT::SimpleValueType T) { case MVT::v16i1: return "MVT::v16i1"; case MVT::v32i1: return "MVT::v32i1"; case MVT::v64i1: return "MVT::v64i1"; + case MVT::v1i8: return "MVT::v1i8"; case MVT::v2i8: return "MVT::v2i8"; case MVT::v4i8: return "MVT::v4i8"; case MVT::v8i8: return "MVT::v8i8"; @@ -98,10 +99,14 @@ std::string llvm::getEnumName(MVT::SimpleValueType T) { case MVT::v8i64: return "MVT::v8i64"; case MVT::v16i64: return "MVT::v16i64"; case MVT::v2f16: return "MVT::v2f16"; + case MVT::v4f16: return "MVT::v4f16"; + case MVT::v8f16: return "MVT::v8f16"; + case MVT::v1f32: return "MVT::v1f32"; case MVT::v2f32: return "MVT::v2f32"; case MVT::v4f32: return "MVT::v4f32"; case MVT::v8f32: return "MVT::v8f32"; case MVT::v16f32: return "MVT::v16f32"; + case MVT::v1f64: return "MVT::v1f64"; case MVT::v2f64: return "MVT::v2f64"; case MVT::v4f64: return "MVT::v4f64"; case MVT::v8f64: return "MVT::v8f64"; @@ -316,6 +321,8 @@ void CodeGenTarget::ComputeInstrsByEnum() const { "BUNDLE", "LIFETIME_START", "LIFETIME_END", + "STACKMAP", + "PATCHPOINT", 0 }; const DenseMap<const Record*, CodeGenInstruction*> &Insts = getInstructions(); diff --git a/utils/TableGen/DAGISelEmitter.cpp b/utils/TableGen/DAGISelEmitter.cpp index b47dd71..a76ea32 100644 --- a/utils/TableGen/DAGISelEmitter.cpp +++ b/utils/TableGen/DAGISelEmitter.cpp @@ -81,15 +81,13 @@ struct PatternSortingPredicate { const TreePatternNode *LHSSrc = LHS->getSrcPattern(); const TreePatternNode *RHSSrc = RHS->getSrcPattern(); - if (LHSSrc->getNumTypes() != 0 && RHSSrc->getNumTypes() != 0 && - LHSSrc->getType(0) != RHSSrc->getType(0)) { - MVT::SimpleValueType V1 = LHSSrc->getType(0), V2 = RHSSrc->getType(0); - if (MVT(V1).isVector() != MVT(V2).isVector()) - return MVT(V2).isVector(); - - if (MVT(V1).isFloatingPoint() != MVT(V2).isFloatingPoint()) - return MVT(V2).isFloatingPoint(); - } + MVT LHSVT = (LHSSrc->getNumTypes() != 0 ? LHSSrc->getType(0) : MVT::Other); + MVT RHSVT = (RHSSrc->getNumTypes() != 0 ? RHSSrc->getType(0) : MVT::Other); + if (LHSVT.isVector() != RHSVT.isVector()) + return RHSVT.isVector(); + + if (LHSVT.isFloatingPoint() != RHSVT.isFloatingPoint()) + return RHSVT.isFloatingPoint(); // Otherwise, if the patterns might both match, sort based on complexity, // which means that we prefer to match patterns that cover more nodes in the diff --git a/utils/TableGen/DAGISelMatcher.cpp b/utils/TableGen/DAGISelMatcher.cpp index d173cf0..5d6a11a 100644 --- a/utils/TableGen/DAGISelMatcher.cpp +++ b/utils/TableGen/DAGISelMatcher.cpp @@ -63,7 +63,7 @@ bool Matcher::canMoveBefore(const Matcher *Other) const { } } -/// canMoveBefore - Return true if it is safe to move the current matcher +/// canMoveBeforeNode - Return true if it is safe to move the current matcher /// across the specified one. bool Matcher::canMoveBeforeNode(const Matcher *Other) const { // We can move simple predicates before record nodes. @@ -134,6 +134,10 @@ void CheckSameMatcher::printImpl(raw_ostream &OS, unsigned indent) const { OS.indent(indent) << "CheckSame " << MatchNumber << '\n'; } +void CheckChildSameMatcher::printImpl(raw_ostream &OS, unsigned indent) const { + OS.indent(indent) << "CheckChild" << ChildNo << "Same\n"; +} + void CheckPatternPredicateMatcher:: printImpl(raw_ostream &OS, unsigned indent) const { OS.indent(indent) << "CheckPatternPredicate " << Predicate << '\n'; diff --git a/utils/TableGen/DAGISelMatcher.h b/utils/TableGen/DAGISelMatcher.h index f978188..70031fa 100644 --- a/utils/TableGen/DAGISelMatcher.h +++ b/utils/TableGen/DAGISelMatcher.h @@ -55,6 +55,7 @@ public: // Predicate checking. CheckSame, // Fail if not same as prev match. + CheckChildSame, // Fail if child not same as prev match. CheckPatternPredicate, CheckPredicate, // Fail if node predicate fails. CheckOpcode, // Fail if not opcode. @@ -122,6 +123,7 @@ public: switch (getKind()) { default: return false; case CheckSame: + case CheckChildSame: case CheckPatternPredicate: case CheckPredicate: case CheckOpcode: @@ -154,7 +156,7 @@ public: /// node. Other must be equal to or before this. bool canMoveBefore(const Matcher *Other) const; - /// canMoveBefore - Return true if it is safe to move the current matcher + /// canMoveBeforeNode - Return true if it is safe to move the current matcher /// across the specified one. bool canMoveBeforeNode(const Matcher *Other) const; @@ -392,6 +394,34 @@ private: virtual unsigned getHashImpl() const { return getMatchNumber(); } }; +/// CheckChildSameMatcher - This checks to see if child node is exactly the same +/// node as the specified match that was recorded with 'Record'. This is used +/// when patterns have the same name in them, like '(mul GPR:$in, GPR:$in)'. +class CheckChildSameMatcher : public Matcher { + unsigned ChildNo; + unsigned MatchNumber; +public: + CheckChildSameMatcher(unsigned childno, unsigned matchnumber) + : Matcher(CheckChildSame), ChildNo(childno), MatchNumber(matchnumber) {} + + unsigned getChildNo() const { return ChildNo; } + unsigned getMatchNumber() const { return MatchNumber; } + + static inline bool classof(const Matcher *N) { + return N->getKind() == CheckChildSame; + } + + virtual bool isSafeToReorderWithPatternPredicate() const { return true; } + +private: + virtual void printImpl(raw_ostream &OS, unsigned indent) const; + virtual bool isEqualImpl(const Matcher *M) const { + return cast<CheckChildSameMatcher>(M)->ChildNo == ChildNo && + cast<CheckChildSameMatcher>(M)->MatchNumber == MatchNumber; + } + virtual unsigned getHashImpl() const { return (MatchNumber << 2) | ChildNo; } +}; + /// CheckPatternPredicateMatcher - This checks the target-specific predicate /// to see if the entire pattern is capable of matching. This predicate does /// not take a node as input. This is used for subtarget feature checks etc. diff --git a/utils/TableGen/DAGISelMatcherEmitter.cpp b/utils/TableGen/DAGISelMatcherEmitter.cpp index 93f84ce..04fe0d1 100644 --- a/utils/TableGen/DAGISelMatcherEmitter.cpp +++ b/utils/TableGen/DAGISelMatcherEmitter.cpp @@ -242,6 +242,12 @@ EmitMatcher(const Matcher *N, unsigned Indent, unsigned CurrentIdx, << cast<CheckSameMatcher>(N)->getMatchNumber() << ",\n"; return 2; + case Matcher::CheckChildSame: + OS << "OPC_CheckChild" + << cast<CheckChildSameMatcher>(N)->getChildNo() << "Same, " + << cast<CheckChildSameMatcher>(N)->getMatchNumber() << ",\n"; + return 2; + case Matcher::CheckPatternPredicate: { StringRef Pred =cast<CheckPatternPredicateMatcher>(N)->getPredicate(); OS << "OPC_CheckPatternPredicate, " << getPatternPredicate(Pred) << ','; @@ -315,10 +321,12 @@ EmitMatcher(const Matcher *N, unsigned Indent, unsigned CurrentIdx, assert(ChildSize != 0 && "Should not have a zero-sized child!"); if (i != 0) { + if (!OmitComments) + OS << "/*" << CurrentIdx << "*/"; OS.PadToColumn(Indent*2); if (!OmitComments) - OS << (isa<SwitchOpcodeMatcher>(N) ? - "/*SwitchOpcode*/ " : "/*SwitchType*/ "); + OS << (isa<SwitchOpcodeMatcher>(N) ? + "/*SwitchOpcode*/ " : "/*SwitchType*/ "); } // Emit the VBR. @@ -340,6 +348,8 @@ EmitMatcher(const Matcher *N, unsigned Indent, unsigned CurrentIdx, } // Emit the final zero to terminate the switch. + if (!OmitComments) + OS << "/*" << CurrentIdx << "*/"; OS.PadToColumn(Indent*2) << "0, "; if (!OmitComments) OS << (isa<SwitchOpcodeMatcher>(N) ? @@ -749,6 +759,7 @@ void MatcherTableEmitter::EmitHistogram(const Matcher *M, case Matcher::MoveChild: OS << "OPC_MoveChild"; break; case Matcher::MoveParent: OS << "OPC_MoveParent"; break; case Matcher::CheckSame: OS << "OPC_CheckSame"; break; + case Matcher::CheckChildSame: OS << "OPC_CheckChildSame"; break; case Matcher::CheckPatternPredicate: OS << "OPC_CheckPatternPredicate"; break; case Matcher::CheckPredicate: OS << "OPC_CheckPredicate"; break; diff --git a/utils/TableGen/DAGISelMatcherOpt.cpp b/utils/TableGen/DAGISelMatcherOpt.cpp index f996422..82e5d63 100644 --- a/utils/TableGen/DAGISelMatcherOpt.cpp +++ b/utils/TableGen/DAGISelMatcherOpt.cpp @@ -51,7 +51,11 @@ static void ContractNodes(OwningPtr<Matcher> &MatcherPtr, if (MC->getChildNo() < 8 && // Only have CheckChildType0...7 CT->getResNo() == 0) // CheckChildType checks res #0 New = new CheckChildTypeMatcher(MC->getChildNo(), CT->getType()); - + + if (CheckSameMatcher *CS = dyn_cast<CheckSameMatcher>(MC->getNext())) + if (MC->getChildNo() < 4) // Only have CheckChildSame0...3 + New = new CheckChildSameMatcher(MC->getChildNo(), CS->getMatchNumber()); + if (New) { // Insert the new node. New->setNext(MatcherPtr.take()); diff --git a/utils/TableGen/FastISelEmitter.cpp b/utils/TableGen/FastISelEmitter.cpp index ce7f8c8..e35cf0f 100644 --- a/utils/TableGen/FastISelEmitter.cpp +++ b/utils/TableGen/FastISelEmitter.cpp @@ -46,7 +46,7 @@ class ImmPredicateSet { DenseMap<TreePattern *, unsigned> ImmIDs; std::vector<TreePredicateFn> PredsByName; public: - + unsigned getIDFor(TreePredicateFn Pred) { unsigned &Entry = ImmIDs[Pred.getOrigPatFragRecord()]; if (Entry == 0) { @@ -55,16 +55,16 @@ public: } return Entry-1; } - + const TreePredicateFn &getPredicate(unsigned i) { assert(i < PredsByName.size()); return PredsByName[i]; } - + typedef std::vector<TreePredicateFn>::const_iterator iterator; iterator begin() const { return PredsByName.begin(); } iterator end() const { return PredsByName.end(); } - + }; } // End anonymous namespace @@ -77,9 +77,9 @@ struct OperandsSignature { enum { OK_Reg, OK_FP, OK_Imm, OK_Invalid = -1 }; char Repr; public: - + OpKind() : Repr(OK_Invalid) {} - + bool operator<(OpKind RHS) const { return Repr < RHS.Repr; } bool operator==(OpKind RHS) const { return Repr == RHS.Repr; } @@ -90,13 +90,13 @@ struct OperandsSignature { "Too many integer predicates for the 'Repr' char"); OpKind K; K.Repr = OK_Imm+V; return K; } - + bool isReg() const { return Repr == OK_Reg; } bool isFP() const { return Repr == OK_FP; } bool isImm() const { return Repr >= OK_Imm; } - + unsigned getImmCode() const { assert(isImm()); return Repr-OK_Imm; } - + void printManglingSuffix(raw_ostream &OS, ImmPredicateSet &ImmPredicates, bool StripImmCodes) const { if (isReg()) @@ -111,8 +111,8 @@ struct OperandsSignature { } } }; - - + + SmallVector<OpKind, 3> Operands; bool operator<(const OperandsSignature &O) const { @@ -130,7 +130,7 @@ struct OperandsSignature { return true; return false; } - + /// getWithoutImmCodes - Return a copy of this with any immediate codes forced /// to zero. OperandsSignature getWithoutImmCodes() const { @@ -142,31 +142,31 @@ struct OperandsSignature { Result.Operands.push_back(OpKind::getImm(0)); return Result; } - + void emitImmediatePredicate(raw_ostream &OS, ImmPredicateSet &ImmPredicates) { bool EmittedAnything = false; for (unsigned i = 0, e = Operands.size(); i != e; ++i) { if (!Operands[i].isImm()) continue; - + unsigned Code = Operands[i].getImmCode(); if (Code == 0) continue; - + if (EmittedAnything) OS << " &&\n "; - + TreePredicateFn PredFn = ImmPredicates.getPredicate(Code-1); - + // Emit the type check. OS << "VT == " << getEnumName(PredFn.getOrigPatFragRecord()->getTree(0)->getType(0)) << " && "; - - + + OS << PredFn.getFnName() << "(imm" << i <<')'; EmittedAnything = true; } } - + /// initialize - Examine the given pattern and initialize the contents /// of the Operands array accordingly. Return true if all the operands /// are supported, false otherwise. @@ -177,12 +177,12 @@ struct OperandsSignature { const CodeGenRegisterClass *OrigDstRC) { if (InstPatNode->isLeaf()) return false; - + if (InstPatNode->getOperator()->getName() == "imm") { Operands.push_back(OpKind::getImm(0)); return true; } - + if (InstPatNode->getOperator()->getName() == "fpimm") { Operands.push_back(OpKind::getFP()); return true; @@ -211,19 +211,19 @@ struct OperandsSignature { Record *Rec = PredFn.getOrigPatFragRecord()->getRecord(); if (Rec->getValueAsBit("FastIselShouldIgnore")) return false; - + PredNo = ImmediatePredicates.getIDFor(PredFn)+1; } - + // Handle unmatched immediate sizes here. //if (Op->getType(0) != VT) // return false; - + Operands.push_back(OpKind::getImm(PredNo)); continue; } - + // For now, filter out any operand with a predicate. // For now, filter out any operand with multiple values. if (!Op->getPredicateFns().empty() || Op->getNumTypes() != 1) @@ -237,7 +237,7 @@ struct OperandsSignature { // For now, ignore other non-leaf nodes. return false; } - + assert(Op->hasTypeSet(0) && "Type infererence not done?"); // For now, all the operands must have the same type (if they aren't @@ -250,7 +250,7 @@ struct OperandsSignature { if (!OpDI) return false; Record *OpLeafRec = OpDI->getDef(); - + // For now, the only other thing we accept is register operands. const CodeGenRegisterClass *RC = 0; if (OpLeafRec->isSubClassOf("RegisterOperand")) @@ -375,7 +375,7 @@ class FastISelMap { std::map<OperandsSignature, std::vector<OperandsSignature> > SignaturesWithConstantForms; - + std::string InstNS; ImmPredicateSet ImmediatePredicates; public: @@ -551,13 +551,13 @@ void FastISelMap::collectPatterns(CodeGenDAGPatterns &CGP) { SubRegNo, PhysRegInputs }; - + if (SimplePatterns[Operands][OpcodeName][VT][RetVT].count(PredicateCheck)) PrintFatalError(Pattern.getSrcRecord()->getLoc(), "Duplicate record in FastISel table!"); SimplePatterns[Operands][OpcodeName][VT][RetVT][PredicateCheck] = Memo; - + // If any of the operands were immediates with predicates on them, strip // them down to a signature that doesn't have predicates so that we can // associate them with the stripped predicate version. @@ -571,14 +571,14 @@ void FastISelMap::collectPatterns(CodeGenDAGPatterns &CGP) { void FastISelMap::printImmediatePredicates(raw_ostream &OS) { if (ImmediatePredicates.begin() == ImmediatePredicates.end()) return; - + OS << "\n// FastEmit Immediate Predicate functions.\n"; for (ImmPredicateSet::iterator I = ImmediatePredicates.begin(), E = ImmediatePredicates.end(); I != E; ++I) { OS << "static bool " << I->getFnName() << "(int64_t Imm) {\n"; OS << I->getImmediatePredicateCode() << "\n}\n"; } - + OS << "\n\n"; } @@ -804,11 +804,11 @@ void FastISelMap::printFunctionDefinitions(raw_ostream &OS) { OS << ", "; Operands.PrintParameters(OS); OS << ") {\n"; - - // If there are any forms of this signature available that operand on - // constrained forms of the immediate (e.g. 32-bit sext immediate in a + + // If there are any forms of this signature available that operate on + // constrained forms of the immediate (e.g., 32-bit sext immediate in a // 64-bit operand), check them first. - + std::map<OperandsSignature, std::vector<OperandsSignature> >::iterator MI = SignaturesWithConstantForms.find(Operands); if (MI != SignaturesWithConstantForms.end()) { @@ -816,7 +816,7 @@ void FastISelMap::printFunctionDefinitions(raw_ostream &OS) { std::sort(MI->second.begin(), MI->second.end()); MI->second.erase(std::unique(MI->second.begin(), MI->second.end()), MI->second.end()); - + // Check each in order it was seen. It would be nice to have a good // relative ordering between them, but we're not going for optimality // here. @@ -831,11 +831,11 @@ void FastISelMap::printFunctionDefinitions(raw_ostream &OS) { MI->second[i].PrintArguments(OS); OS << "))\n return Reg;\n\n"; } - + // Done with this, remove it. SignaturesWithConstantForms.erase(MI); } - + OS << " switch (Opcode) {\n"; for (OpcodeTypeRetPredMap::const_iterator I = OTM.begin(), E = OTM.end(); I != E; ++I) { @@ -855,7 +855,7 @@ void FastISelMap::printFunctionDefinitions(raw_ostream &OS) { OS << "}\n"; OS << "\n"; } - + // TODO: SignaturesWithConstantForms should be empty here. } diff --git a/utils/TableGen/FixedLenDecoderEmitter.cpp b/utils/TableGen/FixedLenDecoderEmitter.cpp index 6e45240..87d18cd 100644 --- a/utils/TableGen/FixedLenDecoderEmitter.cpp +++ b/utils/TableGen/FixedLenDecoderEmitter.cpp @@ -2038,7 +2038,6 @@ void FixedLenDecoderEmitter::run(raw_ostream &o) { } DecoderTableInfo TableInfo; - std::set<unsigned> Sizes; for (std::map<std::pair<std::string, unsigned>, std::vector<unsigned> >::const_iterator I = OpcMap.begin(), E = OpcMap.end(); I != E; ++I) { diff --git a/utils/TableGen/InstrInfoEmitter.cpp b/utils/TableGen/InstrInfoEmitter.cpp index c8304de..d3d9cc1 100644 --- a/utils/TableGen/InstrInfoEmitter.cpp +++ b/utils/TableGen/InstrInfoEmitter.cpp @@ -57,6 +57,7 @@ private: std::map<std::vector<Record*>, unsigned> &EL, const OperandInfoMapTy &OpInfo, raw_ostream &OS); + void emitOperandTypesEnum(raw_ostream &OS, const CodeGenTarget &Target); void initOperandMapData( const std::vector<const CodeGenInstruction *> NumberedInstructions, const std::string &Namespace, @@ -132,8 +133,8 @@ InstrInfoEmitter::GetOperandInfo(const CodeGenInstruction &Inst) { Res += "|(1<<MCOI::LookupPtrRegClass)"; // Predicate operands. Check to see if the original unexpanded operand - // was of type PredicateOperand. - if (Inst.Operands[i].Rec->isSubClassOf("PredicateOperand")) + // was of type PredicateOp. + if (Inst.Operands[i].Rec->isSubClassOf("PredicateOp")) Res += "|(1<<MCOI::Predicate)"; // Optional def operands. Check to see if the original unexpanded operand @@ -311,6 +312,34 @@ void InstrInfoEmitter::emitOperandNameMappings(raw_ostream &OS, } +/// Generate an enum for all the operand types for this target, under the +/// llvm::TargetNamespace::OpTypes namespace. +/// Operand types are all definitions derived of the Operand Target.td class. +void InstrInfoEmitter::emitOperandTypesEnum(raw_ostream &OS, + const CodeGenTarget &Target) { + + const std::string &Namespace = Target.getInstNamespace(); + std::vector<Record *> Operands = Records.getAllDerivedDefinitions("Operand"); + + OS << "\n#ifdef GET_INSTRINFO_OPERAND_TYPES_ENUM\n"; + OS << "#undef GET_INSTRINFO_OPERAND_TYPES_ENUM\n"; + OS << "namespace llvm {"; + OS << "namespace " << Namespace << " {\n"; + OS << "namespace OpTypes { \n"; + OS << "enum OperandType {\n"; + + for (unsigned oi = 0, oe = Operands.size(); oi != oe; ++oi) { + if (!Operands[oi]->isAnonymous()) + OS << " " << Operands[oi]->getName() << " = " << oi << ",\n"; + } + + OS << " OPERAND_TYPE_LIST_END" << "\n};\n"; + OS << "} // End namespace OpTypes\n"; + OS << "} // End namespace " << Namespace << "\n"; + OS << "} // End namespace llvm\n"; + OS << "#endif // GET_INSTRINFO_OPERAND_TYPES_ENUM\n"; +} + //===----------------------------------------------------------------------===// // Main Output. //===----------------------------------------------------------------------===// @@ -408,13 +437,14 @@ void InstrInfoEmitter::run(raw_ostream &OS) { OS << "namespace llvm {\n"; OS << "struct " << ClassName << " : public TargetInstrInfo {\n" << " explicit " << ClassName << "(int SO = -1, int DO = -1);\n" + << " virtual ~" << ClassName << "();\n" << "};\n"; OS << "} // End llvm namespace \n"; OS << "#endif // GET_INSTRINFO_HEADER\n\n"; - OS << "\n#ifdef GET_INSTRINFO_CTOR\n"; - OS << "#undef GET_INSTRINFO_CTOR\n"; + OS << "\n#ifdef GET_INSTRINFO_CTOR_DTOR\n"; + OS << "#undef GET_INSTRINFO_CTOR_DTOR\n"; OS << "namespace llvm {\n"; OS << "extern const MCInstrDesc " << TargetName << "Insts[];\n"; @@ -424,12 +454,15 @@ void InstrInfoEmitter::run(raw_ostream &OS) { << " : TargetInstrInfo(SO, DO) {\n" << " InitMCInstrInfo(" << TargetName << "Insts, " << TargetName << "InstrNameIndices, " << TargetName << "InstrNameData, " - << NumberedInstructions.size() << ");\n}\n"; + << NumberedInstructions.size() << ");\n}\n" + << ClassName << "::~" << ClassName << "() {}\n"; OS << "} // End llvm namespace \n"; - OS << "#endif // GET_INSTRINFO_CTOR\n\n"; + OS << "#endif // GET_INSTRINFO_CTOR_DTOR\n\n"; emitOperandNameMappings(OS, Target, NumberedInstructions); + + emitOperandTypesEnum(OS, Target); } void InstrInfoEmitter::emitRecord(const CodeGenInstruction &Inst, unsigned Num, @@ -514,6 +547,19 @@ void InstrInfoEmitter::emitRecord(const CodeGenInstruction &Inst, unsigned Num, else OS << "OperandInfo" << OpInfo.find(OperandInfo)->second; + CodeGenTarget &Target = CDP.getTargetInfo(); + if (Inst.HasComplexDeprecationPredicate) + // Emit a function pointer to the complex predicate method. + OS << ",0" + << ",&get" << Inst.DeprecatedReason << "DeprecationInfo"; + else if (!Inst.DeprecatedReason.empty()) + // Emit the Subtarget feature. + OS << "," << Target.getInstNamespace() << "::" << Inst.DeprecatedReason + << ",0"; + else + // Instruction isn't deprecated. + OS << ",0,0"; + OS << " }, // Inst #" << Num << " = " << Inst.TheDef->getName() << "\n"; } @@ -545,7 +591,15 @@ void InstrInfoEmitter::emitEnums(raw_ostream &OS) { << "\t= " << i << ",\n"; } OS << " INSTRUCTION_LIST_END = " << NumberedInstructions.size() << "\n"; - OS << " };\n}\n"; + OS << " };\n"; + OS << "namespace Sched {\n"; + OS << " enum {\n"; + for (unsigned i = 0, e = SchedModels.numInstrSchedClasses(); i != e; ++i) { + OS << " " << SchedModels.getSchedClass(i).Name + << "\t= " << i << ",\n"; + } + OS << " SCHED_LIST_END = " << SchedModels.numInstrSchedClasses() << "\n"; + OS << " };\n}\n}\n"; OS << "} // End llvm namespace \n"; OS << "#endif // GET_INSTRINFO_ENUM\n\n"; diff --git a/utils/TableGen/IntrinsicEmitter.cpp b/utils/TableGen/IntrinsicEmitter.cpp index c508795..8f137f8 100644 --- a/utils/TableGen/IntrinsicEmitter.cpp +++ b/utils/TableGen/IntrinsicEmitter.cpp @@ -84,10 +84,10 @@ void IntrinsicEmitter::run(raw_ostream &OS) { // Emit the function name recognizer. EmitFnNameRecognizer(Ints, OS); - + // Emit the intrinsic declaration generator. EmitGenerator(Ints, OS); - + // Emit the intrinsic parameter attributes. EmitAttributes(Ints, OS); @@ -125,7 +125,7 @@ void IntrinsicEmitter::EmitEnumInfo(const std::vector<CodeGenIntrinsic> &Ints, for (unsigned i = 0, e = Ints.size(); i != e; ++i) { OS << " " << Ints[i].EnumName; OS << ((i != e-1) ? ", " : " "); - OS << std::string(40-Ints[i].EnumName.size(), ' ') + OS << std::string(40-Ints[i].EnumName.size(), ' ') << "// " << Ints[i].Name << "\n"; } OS << "#endif\n\n"; @@ -146,13 +146,13 @@ private: }; void IntrinsicEmitter:: -EmitFnNameRecognizer(const std::vector<CodeGenIntrinsic> &Ints, +EmitFnNameRecognizer(const std::vector<CodeGenIntrinsic> &Ints, raw_ostream &OS) { // Build a 'first character of function name' -> intrinsic # mapping. std::map<char, std::vector<unsigned> > IntMapping; for (unsigned i = 0, e = Ints.size(); i != e; ++i) IntMapping[Ints[i].Name[5]].push_back(i); - + OS << "// Function name -> enum value recognizer code.\n"; OS << "#ifdef GET_FUNCTION_RECOGNIZER\n"; OS << " StringRef NameR(Name+6, Len-6); // Skip over 'llvm.'\n"; @@ -171,7 +171,7 @@ EmitFnNameRecognizer(const std::vector<CodeGenIntrinsic> &Ints, // Emit all the overloaded intrinsics first, build a table of the // non-overloaded ones. std::vector<StringMatcher::StringPair> MatchTable; - + for (unsigned i = 0, e = IntList.size(); i != e; ++i) { unsigned IntNo = IntList[i]; std::string Result = "return " + TargetPrefix + "Intrinsic::" + @@ -188,18 +188,18 @@ EmitFnNameRecognizer(const std::vector<CodeGenIntrinsic> &Ints, OS << " if (NameR.startswith(\"" << TheStr << "\")) " << Result << '\n'; } - + // Emit the matcher logic for the fixed length strings. StringMatcher("NameR", MatchTable, OS).Emit(1); OS << " break; // end of '" << I->first << "' case.\n"; } - + OS << " }\n"; OS << "#endif\n\n"; } void IntrinsicEmitter:: -EmitIntrinsicToNameTable(const std::vector<CodeGenIntrinsic> &Ints, +EmitIntrinsicToNameTable(const std::vector<CodeGenIntrinsic> &Ints, raw_ostream &OS) { OS << "// Intrinsic ID to name table\n"; OS << "#ifdef GET_INTRINSIC_NAME_TABLE\n"; @@ -210,7 +210,7 @@ EmitIntrinsicToNameTable(const std::vector<CodeGenIntrinsic> &Ints, } void IntrinsicEmitter:: -EmitIntrinsicToOverloadTable(const std::vector<CodeGenIntrinsic> &Ints, +EmitIntrinsicToOverloadTable(const std::vector<CodeGenIntrinsic> &Ints, raw_ostream &OS) { OS << "// Intrinsic ID to overload bitset\n"; OS << "#ifdef GET_INTRINSIC_OVERLOAD_TABLE\n"; @@ -260,7 +260,9 @@ enum IIT_Info { IIT_STRUCT5 = 22, IIT_EXTEND_VEC_ARG = 23, IIT_TRUNC_VEC_ARG = 24, - IIT_ANYPTR = 25 + IIT_ANYPTR = 25, + IIT_V1 = 26, + IIT_VARARG = 27 }; @@ -277,7 +279,7 @@ static void EncodeFixedValueType(MVT::SimpleValueType VT, case 64: return Sig.push_back(IIT_I64); } } - + switch (VT) { default: PrintFatalError("unhandled MVT in intrinsic!"); case MVT::f16: return Sig.push_back(IIT_F16); @@ -287,16 +289,18 @@ static void EncodeFixedValueType(MVT::SimpleValueType VT, case MVT::x86mmx: return Sig.push_back(IIT_MMX); // MVT::OtherVT is used to mean the empty struct type here. case MVT::Other: return Sig.push_back(IIT_EMPTYSTRUCT); + // MVT::isVoid is used to represent varargs here. + case MVT::isVoid: return Sig.push_back(IIT_VARARG); } } #ifdef _MSC_VER #pragma optimize("",off) // MSVC 2010 optimizer can't deal with this function. -#endif +#endif static void EncodeFixedType(Record *R, std::vector<unsigned char> &ArgCodes, std::vector<unsigned char> &Sig) { - + if (R->isSubClassOf("LLVMMatchType")) { unsigned Number = R->getValueAsInt("Number"); assert(Number < ArgCodes.size() && "Invalid matching number!"); @@ -308,7 +312,7 @@ static void EncodeFixedType(Record *R, std::vector<unsigned char> &ArgCodes, Sig.push_back(IIT_ARG); return Sig.push_back((Number << 2) | ArgCodes[Number]); } - + MVT::SimpleValueType VT = getValueType(R->getValueAsDef("VT")); unsigned Tmp = 0; @@ -319,17 +323,17 @@ static void EncodeFixedType(Record *R, std::vector<unsigned char> &ArgCodes, case MVT::fAny: ++Tmp; // FALL THROUGH. case MVT::iAny: { // If this is an "any" valuetype, then the type is the type of the next - // type in the list specified to getIntrinsic(). + // type in the list specified to getIntrinsic(). Sig.push_back(IIT_ARG); - + // Figure out what arg # this is consuming, and remember what kind it was. unsigned ArgNo = ArgCodes.size(); ArgCodes.push_back(Tmp); - + // Encode what sort of argument it must be in the low 2 bits of the ArgNo. return Sig.push_back((ArgNo << 2) | Tmp); } - + case MVT::iPTR: { unsigned AddrSpace = 0; if (R->isSubClassOf("LLVMQualPointerType")) { @@ -345,18 +349,19 @@ static void EncodeFixedType(Record *R, std::vector<unsigned char> &ArgCodes, return EncodeFixedType(R->getValueAsDef("ElTy"), ArgCodes, Sig); } } - + if (EVT(VT).isVector()) { EVT VVT = VT; switch (VVT.getVectorNumElements()) { default: PrintFatalError("unhandled vector type width in intrinsic!"); + case 1: Sig.push_back(IIT_V1); break; case 2: Sig.push_back(IIT_V2); break; case 4: Sig.push_back(IIT_V4); break; case 8: Sig.push_back(IIT_V8); break; case 16: Sig.push_back(IIT_V16); break; case 32: Sig.push_back(IIT_V32); break; } - + return EncodeFixedValueType(VVT.getVectorElementType(). getSimpleVT().SimpleTy, Sig); } @@ -373,7 +378,7 @@ static void EncodeFixedType(Record *R, std::vector<unsigned char> &ArgCodes, static void ComputeFixedEncoding(const CodeGenIntrinsic &Int, std::vector<unsigned char> &TypeSig) { std::vector<unsigned char> ArgCodes; - + if (Int.IS.RetVTs.empty()) TypeSig.push_back(IIT_Done); else if (Int.IS.RetVTs.size() == 1 && @@ -388,11 +393,11 @@ static void ComputeFixedEncoding(const CodeGenIntrinsic &Int, case 5: TypeSig.push_back(IIT_STRUCT5); break; default: assert(0 && "Unhandled case in struct"); } - + for (unsigned i = 0, e = Int.IS.RetVTs.size(); i != e; ++i) EncodeFixedType(Int.IS.RetTypeDefs[i], ArgCodes, TypeSig); } - + for (unsigned i = 0, e = Int.IS.ParamTypeDefs.size(); i != e; ++i) EncodeFixedType(Int.IS.ParamTypeDefs[i], ArgCodes, TypeSig); } @@ -401,16 +406,16 @@ static void printIITEntry(raw_ostream &OS, unsigned char X) { OS << (unsigned)X; } -void IntrinsicEmitter::EmitGenerator(const std::vector<CodeGenIntrinsic> &Ints, +void IntrinsicEmitter::EmitGenerator(const std::vector<CodeGenIntrinsic> &Ints, raw_ostream &OS) { // If we can compute a 32-bit fixed encoding for this intrinsic, do so and // capture it in this vector, otherwise store a ~0U. std::vector<unsigned> FixedEncodings; - + SequenceToOffsetTable<std::vector<unsigned char> > LongEncodingTable; - + std::vector<unsigned char> TypeSig; - + // Compute the unique argument type info. for (unsigned i = 0, e = Ints.size(); i != e; ++i) { // Get the signature for the intrinsic. @@ -430,7 +435,7 @@ void IntrinsicEmitter::EmitGenerator(const std::vector<CodeGenIntrinsic> &Ints, } Result = (Result << 4) | TypeSig[e-i-1]; } - + // If this could be encoded into a 31-bit word, return it. if (!Failed && (Result >> 31) == 0) { FixedEncodings.push_back(Result); @@ -441,45 +446,45 @@ void IntrinsicEmitter::EmitGenerator(const std::vector<CodeGenIntrinsic> &Ints, // Otherwise, we're going to unique the sequence into the // LongEncodingTable, and use its offset in the 32-bit table instead. LongEncodingTable.add(TypeSig); - + // This is a placehold that we'll replace after the table is laid out. FixedEncodings.push_back(~0U); } - + LongEncodingTable.layout(); - + OS << "// Global intrinsic function declaration type table.\n"; OS << "#ifdef GET_INTRINSIC_GENERATOR_GLOBAL\n"; OS << "static const unsigned IIT_Table[] = {\n "; - + for (unsigned i = 0, e = FixedEncodings.size(); i != e; ++i) { if ((i & 7) == 7) OS << "\n "; - + // If the entry fit in the table, just emit it. if (FixedEncodings[i] != ~0U) { OS << "0x" << utohexstr(FixedEncodings[i]) << ", "; continue; } - + TypeSig.clear(); ComputeFixedEncoding(Ints[i], TypeSig); - + // Otherwise, emit the offset into the long encoding table. We emit it this // way so that it is easier to read the offset in the .def file. OS << "(1U<<31) | " << LongEncodingTable.get(TypeSig) << ", "; } - + OS << "0\n};\n\n"; - + // Emit the shared table of register lists. OS << "static const unsigned char IIT_LongEncodingTable[] = {\n"; if (!LongEncodingTable.empty()) LongEncodingTable.emit(OS, printIITEntry); OS << " 255\n};\n\n"; - + OS << "#endif\n\n"; // End of GET_INTRINSIC_GENERATOR_GLOBAL } @@ -567,7 +572,6 @@ EmitAttributes(const std::vector<CodeGenIntrinsic> &Ints, raw_ostream &OS) { OS << " AttributeSet AS[" << maxArgAttrs+1 << "];\n"; OS << " unsigned NumAttrs = 0;\n"; OS << " if (id != 0) {\n"; - OS << " SmallVector<Attribute::AttrKind, 8> AttrVec;\n"; OS << " switch(IntrinsicsToAttributesMap[id - "; if (TargetOnly) OS << "Intrinsic::num_intrinsics"; @@ -577,7 +581,7 @@ EmitAttributes(const std::vector<CodeGenIntrinsic> &Ints, raw_ostream &OS) { OS << " default: llvm_unreachable(\"Invalid attribute number\");\n"; for (UniqAttrMapTy::const_iterator I = UniqAttributes.begin(), E = UniqAttributes.end(); I != E; ++I) { - OS << " case " << I->second << ":\n"; + OS << " case " << I->second << ": {\n"; const CodeGenIntrinsic &intrinsic = *(I->first); @@ -590,60 +594,82 @@ EmitAttributes(const std::vector<CodeGenIntrinsic> &Ints, raw_ostream &OS) { while (ai != ae) { unsigned argNo = intrinsic.ArgumentAttributes[ai].first; - OS << " AttrVec.clear();\n"; + OS << " const Attribute::AttrKind AttrParam" << argNo + 1 <<"[]= {"; + bool addComma = false; do { switch (intrinsic.ArgumentAttributes[ai].second) { case CodeGenIntrinsic::NoCapture: - OS << " AttrVec.push_back(Attribute::NoCapture);\n"; + if (addComma) + OS << ","; + OS << "Attribute::NoCapture"; + addComma = true; break; case CodeGenIntrinsic::ReadOnly: - OS << " AttrVec.push_back(Attribute::ReadOnly);\n"; + if (addComma) + OS << ","; + OS << "Attribute::ReadOnly"; + addComma = true; break; case CodeGenIntrinsic::ReadNone: - OS << " AttrVec.push_back(Attribute::ReadNone);\n"; + if (addComma) + OS << ","; + OS << "Attributes::ReadNone"; + addComma = true; break; } ++ai; } while (ai != ae && intrinsic.ArgumentAttributes[ai].first == argNo); - + OS << "};\n"; OS << " AS[" << numAttrs++ << "] = AttributeSet::get(C, " - << argNo+1 << ", AttrVec);\n"; + << argNo+1 << ", AttrParam" << argNo +1 << ");\n"; } } ModRefKind modRef = getModRefKind(intrinsic); if (!intrinsic.canThrow || modRef || intrinsic.isNoReturn) { - OS << " AttrVec.clear();\n"; - - if (!intrinsic.canThrow) - OS << " AttrVec.push_back(Attribute::NoUnwind);\n"; - if (intrinsic.isNoReturn) - OS << " AttrVec.push_back(Attribute::NoReturn);\n"; + OS << " const Attribute::AttrKind Atts[] = {"; + bool addComma = false; + if (!intrinsic.canThrow) { + OS << "Attribute::NoUnwind"; + addComma = true; + } + if (intrinsic.isNoReturn) { + if (addComma) + OS << ","; + OS << "Attribute::NoReturn"; + addComma = true; + } switch (modRef) { case MRK_none: break; case MRK_readonly: - OS << " AttrVec.push_back(Attribute::ReadOnly);\n"; + if (addComma) + OS << ","; + OS << "Attribute::ReadOnly"; break; case MRK_readnone: - OS << " AttrVec.push_back(Attribute::ReadNone);\n"; + if (addComma) + OS << ","; + OS << "Attribute::ReadNone"; break; } + OS << "};\n"; OS << " AS[" << numAttrs++ << "] = AttributeSet::get(C, " - << "AttributeSet::FunctionIndex, AttrVec);\n"; + << "AttributeSet::FunctionIndex, Atts);\n"; } if (numAttrs) { OS << " NumAttrs = " << numAttrs << ";\n"; OS << " break;\n"; + OS << " }\n"; } else { OS << " return AttributeSet();\n"; } } - + OS << " }\n"; OS << " }\n"; OS << " return AttributeSet::get(C, ArrayRef<AttributeSet>(AS, " @@ -692,9 +718,9 @@ EmitModRefBehavior(const std::vector<CodeGenIntrinsic> &Ints, raw_ostream &OS){ static void EmitTargetBuiltins(const std::map<std::string, std::string> &BIM, const std::string &TargetPrefix, raw_ostream &OS) { - + std::vector<StringMatcher::StringPair> Results; - + for (std::map<std::string, std::string>::const_iterator I = BIM.begin(), E = BIM.end(); I != E; ++I) { std::string ResultCode = @@ -705,9 +731,9 @@ static void EmitTargetBuiltins(const std::map<std::string, std::string> &BIM, StringMatcher("BuiltinName", Results, OS).Emit(); } - + void IntrinsicEmitter:: -EmitIntrinsicToGCCBuiltinMap(const std::vector<CodeGenIntrinsic> &Ints, +EmitIntrinsicToGCCBuiltinMap(const std::vector<CodeGenIntrinsic> &Ints, raw_ostream &OS) { typedef std::map<std::string, std::map<std::string, std::string> > BIMTy; BIMTy BuiltinMap; @@ -715,20 +741,20 @@ EmitIntrinsicToGCCBuiltinMap(const std::vector<CodeGenIntrinsic> &Ints, if (!Ints[i].GCCBuiltinName.empty()) { // Get the map for this target prefix. std::map<std::string, std::string> &BIM =BuiltinMap[Ints[i].TargetPrefix]; - + if (!BIM.insert(std::make_pair(Ints[i].GCCBuiltinName, Ints[i].EnumName)).second) PrintFatalError("Intrinsic '" + Ints[i].TheDef->getName() + "': duplicate GCC builtin name!"); } } - + OS << "// Get the LLVM intrinsic that corresponds to a GCC builtin.\n"; OS << "// This is used by the C front-end. The GCC builtin name is passed\n"; OS << "// in as BuiltinName, and a target prefix (e.g. 'ppc') is passed\n"; OS << "// in as TargetPrefix. The result is assigned to 'IntrinsicID'.\n"; OS << "#ifdef GET_LLVM_INTRINSIC_FOR_GCC_BUILTIN\n"; - + if (TargetOnly) { OS << "static " << TargetPrefix << "Intrinsic::ID " << "getIntrinsicForGCCBuiltin(const char " @@ -737,10 +763,10 @@ EmitIntrinsicToGCCBuiltinMap(const std::vector<CodeGenIntrinsic> &Ints, OS << "Intrinsic::ID Intrinsic::getIntrinsicForGCCBuiltin(const char " << "*TargetPrefixStr, const char *BuiltinNameStr) {\n"; } - + OS << " StringRef BuiltinName(BuiltinNameStr);\n"; OS << " StringRef TargetPrefix(TargetPrefixStr);\n\n"; - + // Note: this could emit significantly better code if we cared. for (BIMTy::iterator I = BuiltinMap.begin(), E = BuiltinMap.end();I != E;++I){ OS << " "; diff --git a/utils/TableGen/OptParserEmitter.cpp b/utils/TableGen/OptParserEmitter.cpp index 86328bf..9c4daaf 100644 --- a/utils/TableGen/OptParserEmitter.cpp +++ b/utils/TableGen/OptParserEmitter.cpp @@ -13,18 +13,23 @@ #include "llvm/ADT/Twine.h" #include "llvm/TableGen/Record.h" #include "llvm/TableGen/TableGenBackend.h" +#include <cstring> +#include <cctype> #include <map> using namespace llvm; +// Ordering on Info. The logic should match with the consumer-side function in +// llvm/Option/OptTable.h. static int StrCmpOptionName(const char *A, const char *B) { - char a = *A, b = *B; + const char *X = A, *Y = B; + char a = tolower(*A), b = tolower(*B); while (a == b) { if (a == '\0') - return 0; + return strcmp(A, B); - a = *++A; - b = *++B; + a = tolower(*++X); + b = tolower(*++Y); } if (a == '\0') // A is a prefix of B. @@ -36,9 +41,9 @@ static int StrCmpOptionName(const char *A, const char *B) { return (a < b) ? -1 : 1; } -static int CompareOptionRecords(const void *Av, const void *Bv) { - const Record *A = *(const Record*const*) Av; - const Record *B = *(const Record*const*) Bv; +static int CompareOptionRecords(Record *const *Av, Record *const *Bv) { + const Record *A = *Av; + const Record *B = *Bv; // Sentinel options precede all others and are only ordered by precedence. bool ASent = A->getValueAsDef("Kind")->getValueAsBit("Sentinel"); @@ -50,7 +55,7 @@ static int CompareOptionRecords(const void *Av, const void *Bv) { if (!ASent) if (int Cmp = StrCmpOptionName(A->getValueAsString("Name").c_str(), B->getValueAsString("Name").c_str())) - return Cmp; + return Cmp; if (!ASent) { std::vector<std::string> APrefixes = A->getValueAsListOfStrings("Prefixes"); @@ -74,7 +79,7 @@ static int CompareOptionRecords(const void *Av, const void *Bv) { if (APrec == BPrec && A->getValueAsListOfStrings("Prefixes") == B->getValueAsListOfStrings("Prefixes")) { - PrintError(A->getLoc(), Twine("Option is equivilent to")); + PrintError(A->getLoc(), Twine("Option is equivalent to")); PrintError(B->getLoc(), Twine("Other defined here")); PrintFatalError("Equivalent Options found."); } diff --git a/utils/TableGen/RegisterInfoEmitter.cpp b/utils/TableGen/RegisterInfoEmitter.cpp index 731dccf..cc08df9 100644 --- a/utils/TableGen/RegisterInfoEmitter.cpp +++ b/utils/TableGen/RegisterInfoEmitter.cpp @@ -121,7 +121,7 @@ void RegisterInfoEmitter::runEnums(raw_ostream &OS, OS << "}\n"; } - const std::vector<Record*> RegAltNameIndices = Target.getRegAltNameIndices(); + const std::vector<Record*> &RegAltNameIndices = Target.getRegAltNameIndices(); // If the only definition is the default NoRegAltName, we don't need to // emit anything. if (RegAltNameIndices.size() > 1) { @@ -722,7 +722,7 @@ RegisterInfoEmitter::runMCDesc(raw_ostream &OS, CodeGenTarget &Target, // Keep track of sub-register names as well. These are not differentially // encoded. typedef SmallVector<const CodeGenSubRegIndex*, 4> SubRegIdxVec; - SequenceToOffsetTable<SubRegIdxVec> SubRegIdxSeqs; + SequenceToOffsetTable<SubRegIdxVec, CodeGenSubRegIndex::Less> SubRegIdxSeqs; SmallVector<SubRegIdxVec, 4> SubRegIdxLists(Regs.size()); SequenceToOffsetTable<std::string> RegStrings; @@ -1090,7 +1090,7 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target, // Compress the sub-reg index lists. typedef std::vector<const CodeGenSubRegIndex*> IdxList; SmallVector<IdxList, 8> SuperRegIdxLists(RegisterClasses.size()); - SequenceToOffsetTable<IdxList> SuperRegIdxSeqs; + SequenceToOffsetTable<IdxList, CodeGenSubRegIndex::Less> SuperRegIdxSeqs; BitVector MaskBV(RegisterClasses.size()); for (unsigned rc = 0, e = RegisterClasses.size(); rc != e; ++rc) { @@ -1314,9 +1314,21 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target, OS << "0 };\n"; // Emit the *_RegMask bit mask of call-preserved registers. + BitVector Covered = RegBank.computeCoveredRegisters(*Regs); + + // Check for an optional OtherPreserved set. + // Add those registers to RegMask, but not to SaveList. + if (DagInit *OPDag = + dyn_cast<DagInit>(CSRSet->getValueInit("OtherPreserved"))) { + SetTheory::RecSet OPSet; + RegBank.getSets().evaluate(OPDag, OPSet, CSRSet->getLoc()); + Covered |= RegBank.computeCoveredRegisters( + ArrayRef<Record*>(OPSet.begin(), OPSet.end())); + } + OS << "static const uint32_t " << CSRSet->getName() << "_RegMask[] = { "; - printBitVectorAsHex(OS, RegBank.computeCoveredRegisters(*Regs), 32); + printBitVectorAsHex(OS, Covered, 32); OS << "};\n"; } OS << "\n\n"; diff --git a/utils/TableGen/SequenceToOffsetTable.h b/utils/TableGen/SequenceToOffsetTable.h index fcda233..e6ab664 100644 --- a/utils/TableGen/SequenceToOffsetTable.h +++ b/utils/TableGen/SequenceToOffsetTable.h @@ -21,6 +21,7 @@ #include <cassert> #include <cctype> #include <functional> +#include <map> #include <vector> namespace llvm { diff --git a/utils/TableGen/SetTheory.cpp b/utils/TableGen/SetTheory.cpp index 3e5c38c..ad3d7c7 100644 --- a/utils/TableGen/SetTheory.cpp +++ b/utils/TableGen/SetTheory.cpp @@ -27,14 +27,16 @@ typedef SetTheory::RecVec RecVec; // (add a, b, ...) Evaluate and union all arguments. struct AddOp : public SetTheory::Operator { - void apply(SetTheory &ST, DagInit *Expr, RecSet &Elts, ArrayRef<SMLoc> Loc) { + virtual void apply(SetTheory &ST, DagInit *Expr, RecSet &Elts, + ArrayRef<SMLoc> Loc) { ST.evaluate(Expr->arg_begin(), Expr->arg_end(), Elts, Loc); } }; // (sub Add, Sub, ...) Set difference. struct SubOp : public SetTheory::Operator { - void apply(SetTheory &ST, DagInit *Expr, RecSet &Elts, ArrayRef<SMLoc> Loc) { + virtual void apply(SetTheory &ST, DagInit *Expr, RecSet &Elts, + ArrayRef<SMLoc> Loc) { if (Expr->arg_size() < 2) PrintFatalError(Loc, "Set difference needs at least two arguments: " + Expr->getAsString()); @@ -49,7 +51,8 @@ struct SubOp : public SetTheory::Operator { // (and S1, S2) Set intersection. struct AndOp : public SetTheory::Operator { - void apply(SetTheory &ST, DagInit *Expr, RecSet &Elts, ArrayRef<SMLoc> Loc) { + virtual void apply(SetTheory &ST, DagInit *Expr, RecSet &Elts, + ArrayRef<SMLoc> Loc) { if (Expr->arg_size() != 2) PrintFatalError(Loc, "Set intersection requires two arguments: " + Expr->getAsString()); @@ -68,7 +71,8 @@ struct SetIntBinOp : public SetTheory::Operator { RecSet &Set, int64_t N, RecSet &Elts, ArrayRef<SMLoc> Loc) =0; - void apply(SetTheory &ST, DagInit *Expr, RecSet &Elts, ArrayRef<SMLoc> Loc) { + virtual void apply(SetTheory &ST, DagInit *Expr, RecSet &Elts, + ArrayRef<SMLoc> Loc) { if (Expr->arg_size() != 2) PrintFatalError(Loc, "Operator requires (Op Set, Int) arguments: " + Expr->getAsString()); @@ -84,9 +88,9 @@ struct SetIntBinOp : public SetTheory::Operator { // (shl S, N) Shift left, remove the first N elements. struct ShlOp : public SetIntBinOp { - void apply2(SetTheory &ST, DagInit *Expr, - RecSet &Set, int64_t N, - RecSet &Elts, ArrayRef<SMLoc> Loc) { + virtual void apply2(SetTheory &ST, DagInit *Expr, + RecSet &Set, int64_t N, + RecSet &Elts, ArrayRef<SMLoc> Loc) { if (N < 0) PrintFatalError(Loc, "Positive shift required: " + Expr->getAsString()); @@ -97,9 +101,9 @@ struct ShlOp : public SetIntBinOp { // (trunc S, N) Truncate after the first N elements. struct TruncOp : public SetIntBinOp { - void apply2(SetTheory &ST, DagInit *Expr, - RecSet &Set, int64_t N, - RecSet &Elts, ArrayRef<SMLoc> Loc) { + virtual void apply2(SetTheory &ST, DagInit *Expr, + RecSet &Set, int64_t N, + RecSet &Elts, ArrayRef<SMLoc> Loc) { if (N < 0) PrintFatalError(Loc, "Positive length required: " + Expr->getAsString()); @@ -115,9 +119,9 @@ struct RotOp : public SetIntBinOp { RotOp(bool Rev) : Reverse(Rev) {} - void apply2(SetTheory &ST, DagInit *Expr, - RecSet &Set, int64_t N, - RecSet &Elts, ArrayRef<SMLoc> Loc) { + virtual void apply2(SetTheory &ST, DagInit *Expr, + RecSet &Set, int64_t N, + RecSet &Elts, ArrayRef<SMLoc> Loc) { if (Reverse) N = -N; // N > 0 -> rotate left, N < 0 -> rotate right. @@ -134,9 +138,9 @@ struct RotOp : public SetIntBinOp { // (decimate S, N) Pick every N'th element of S. struct DecimateOp : public SetIntBinOp { - void apply2(SetTheory &ST, DagInit *Expr, - RecSet &Set, int64_t N, - RecSet &Elts, ArrayRef<SMLoc> Loc) { + virtual void apply2(SetTheory &ST, DagInit *Expr, + RecSet &Set, int64_t N, + RecSet &Elts, ArrayRef<SMLoc> Loc) { if (N <= 0) PrintFatalError(Loc, "Positive stride required: " + Expr->getAsString()); @@ -147,7 +151,8 @@ struct DecimateOp : public SetIntBinOp { // (interleave S1, S2, ...) Interleave elements of the arguments. struct InterleaveOp : public SetTheory::Operator { - void apply(SetTheory &ST, DagInit *Expr, RecSet &Elts, ArrayRef<SMLoc> Loc) { + virtual void apply(SetTheory &ST, DagInit *Expr, RecSet &Elts, + ArrayRef<SMLoc> Loc) { // Evaluate the arguments individually. SmallVector<RecSet, 4> Args(Expr->getNumArgs()); unsigned MaxSize = 0; @@ -165,7 +170,8 @@ struct InterleaveOp : public SetTheory::Operator { // (sequence "Format", From, To) Generate a sequence of records by name. struct SequenceOp : public SetTheory::Operator { - void apply(SetTheory &ST, DagInit *Expr, RecSet &Elts, ArrayRef<SMLoc> Loc) { + virtual void apply(SetTheory &ST, DagInit *Expr, RecSet &Elts, + ArrayRef<SMLoc> Loc) { int Step = 1; if (Expr->arg_size() > 4) PrintFatalError(Loc, "Bad args to (sequence \"Format\", From, To): " + @@ -232,15 +238,16 @@ struct FieldExpander : public SetTheory::Expander { FieldExpander(StringRef fn) : FieldName(fn) {} - void expand(SetTheory &ST, Record *Def, RecSet &Elts) { + virtual void expand(SetTheory &ST, Record *Def, RecSet &Elts) { ST.evaluate(Def->getValueInit(FieldName), Elts, Def->getLoc()); } }; } // end anonymous namespace -void SetTheory::Operator::anchor() { } +// Pin the vtables to this file. +void SetTheory::Operator::anchor() {} +void SetTheory::Expander::anchor() {} -void SetTheory::Expander::anchor() { } SetTheory::SetTheory() { addOperator("add", new AddOp); diff --git a/utils/TableGen/StringToOffsetTable.h b/utils/TableGen/StringToOffsetTable.h deleted file mode 100644 index d94d3a2..0000000 --- a/utils/TableGen/StringToOffsetTable.h +++ /dev/null @@ -1,83 +0,0 @@ -//===- StringToOffsetTable.h - Emit a big concatenated string ---*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef TBLGEN_STRING_TO_OFFSET_TABLE_H -#define TBLGEN_STRING_TO_OFFSET_TABLE_H - -#include "llvm/ADT/SmallString.h" -#include "llvm/ADT/StringExtras.h" -#include "llvm/ADT/StringMap.h" -#include "llvm/Support/raw_ostream.h" -#include <cctype> - -namespace llvm { - -/// StringToOffsetTable - This class uniques a bunch of nul-terminated strings -/// and keeps track of their offset in a massive contiguous string allocation. -/// It can then output this string blob and use indexes into the string to -/// reference each piece. -class StringToOffsetTable { - StringMap<unsigned> StringOffset; - std::string AggregateString; -public: - - unsigned GetOrAddStringOffset(StringRef Str, bool appendZero = true) { - StringMapEntry<unsigned> &Entry = StringOffset.GetOrCreateValue(Str, -1U); - if (Entry.getValue() == -1U) { - // Add the string to the aggregate if this is the first time found. - Entry.setValue(AggregateString.size()); - AggregateString.append(Str.begin(), Str.end()); - if (appendZero) - AggregateString += '\0'; - } - - return Entry.getValue(); - } - - void EmitString(raw_ostream &O) { - // Escape the string. - SmallString<256> Str; - raw_svector_ostream(Str).write_escaped(AggregateString); - AggregateString = Str.str(); - - O << " \""; - unsigned CharsPrinted = 0; - for (unsigned i = 0, e = AggregateString.size(); i != e; ++i) { - if (CharsPrinted > 70) { - O << "\"\n \""; - CharsPrinted = 0; - } - O << AggregateString[i]; - ++CharsPrinted; - - // Print escape sequences all together. - if (AggregateString[i] != '\\') - continue; - - assert(i+1 < AggregateString.size() && "Incomplete escape sequence!"); - if (isdigit(AggregateString[i+1])) { - assert(isdigit(AggregateString[i+2]) && - isdigit(AggregateString[i+3]) && - "Expected 3 digit octal escape!"); - O << AggregateString[++i]; - O << AggregateString[++i]; - O << AggregateString[++i]; - CharsPrinted += 3; - } else { - O << AggregateString[++i]; - ++CharsPrinted; - } - } - O << "\""; - } -}; - -} // end namespace llvm - -#endif diff --git a/utils/TableGen/SubtargetEmitter.cpp b/utils/TableGen/SubtargetEmitter.cpp index 81bb6f8..b9f9d06 100644 --- a/utils/TableGen/SubtargetEmitter.cpp +++ b/utils/TableGen/SubtargetEmitter.cpp @@ -1198,6 +1198,11 @@ void SubtargetEmitter::EmitProcessorModels(raw_ostream &OS) { EmitProcessorProp(OS, PI->ModelDef, "LoadLatency", ','); EmitProcessorProp(OS, PI->ModelDef, "HighLatency", ','); EmitProcessorProp(OS, PI->ModelDef, "MispredictPenalty", ','); + + OS << " " << (bool)(PI->ModelDef ? + PI->ModelDef->getValueAsBit("CompleteModel") : 0) + << ", // " << "CompleteModel\n"; + OS << " " << PI->Index << ", // Processor ID\n"; if (PI->hasInstrSchedModel()) OS << " " << PI->ModelName << "ProcResources" << ",\n" diff --git a/utils/TableGen/TGValueTypes.cpp b/utils/TableGen/TGValueTypes.cpp index 3ac71a4..f4893f5 100644 --- a/utils/TableGen/TGValueTypes.cpp +++ b/utils/TableGen/TGValueTypes.cpp @@ -35,11 +35,15 @@ public: } Type(TypeKind K) : Kind(K) {} virtual unsigned getSizeInBits() const = 0; - virtual ~Type() {} + virtual ~Type(); }; +// Provide out-of-line definition to prevent weak vtable. +Type::~Type() {} + } +namespace { class ExtendedIntegerType : public Type { unsigned BitWidth; public: @@ -48,7 +52,7 @@ public: static bool classof(const Type *T) { return T->getKind() == TK_ExtendedIntegerType; } - unsigned getSizeInBits() const { + virtual unsigned getSizeInBits() const { return getBitWidth(); } unsigned getBitWidth() const { @@ -65,7 +69,7 @@ public: static bool classof(const Type *T) { return T->getKind() == TK_ExtendedVectorType; } - unsigned getSizeInBits() const { + virtual unsigned getSizeInBits() const { return getNumElements() * getElementType().getSizeInBits(); } EVT getElementType() const { @@ -75,6 +79,7 @@ public: return NumElements; } }; +} // end anonymous namespace static std::map<unsigned, const Type *> ExtendedIntegerTypeMap; diff --git a/utils/TableGen/X86DisassemblerTables.cpp b/utils/TableGen/X86DisassemblerTables.cpp index 0838ac4..bdb4793 100644 --- a/utils/TableGen/X86DisassemblerTables.cpp +++ b/utils/TableGen/X86DisassemblerTables.cpp @@ -81,31 +81,37 @@ static inline bool inheritsFrom(InstructionContext child, case IC_64BIT_REXW_OPSIZE: return false; case IC_VEX: - return inheritsFrom(child, IC_VEX_L_W) || + return (VEX_LIG && inheritsFrom(child, IC_VEX_L_W)) || inheritsFrom(child, IC_VEX_W) || (VEX_LIG && inheritsFrom(child, IC_VEX_L)); case IC_VEX_XS: - return inheritsFrom(child, IC_VEX_L_W_XS) || + return (VEX_LIG && inheritsFrom(child, IC_VEX_L_W_XS)) || inheritsFrom(child, IC_VEX_W_XS) || (VEX_LIG && inheritsFrom(child, IC_VEX_L_XS)); case IC_VEX_XD: - return inheritsFrom(child, IC_VEX_L_W_XD) || + return (VEX_LIG && inheritsFrom(child, IC_VEX_L_W_XD)) || inheritsFrom(child, IC_VEX_W_XD) || (VEX_LIG && inheritsFrom(child, IC_VEX_L_XD)); case IC_VEX_OPSIZE: - return inheritsFrom(child, IC_VEX_L_W_OPSIZE) || + return (VEX_LIG && inheritsFrom(child, IC_VEX_L_W_OPSIZE)) || inheritsFrom(child, IC_VEX_W_OPSIZE) || (VEX_LIG && inheritsFrom(child, IC_VEX_L_OPSIZE)); case IC_VEX_W: + return VEX_LIG && inheritsFrom(child, IC_VEX_L_W); case IC_VEX_W_XS: + return VEX_LIG && inheritsFrom(child, IC_VEX_L_W_XS); case IC_VEX_W_XD: + return VEX_LIG && inheritsFrom(child, IC_VEX_L_W_XD); case IC_VEX_W_OPSIZE: - return false; + return VEX_LIG && inheritsFrom(child, IC_VEX_L_W_OPSIZE); case IC_VEX_L: + return inheritsFrom(child, IC_VEX_L_W); case IC_VEX_L_XS: + return inheritsFrom(child, IC_VEX_L_W_XS); case IC_VEX_L_XD: + return inheritsFrom(child, IC_VEX_L_W_XD); case IC_VEX_L_OPSIZE: - return false; + return inheritsFrom(child, IC_VEX_L_W_OPSIZE); case IC_VEX_L_W: case IC_VEX_L_W_XS: case IC_VEX_L_W_XD: @@ -122,7 +128,7 @@ static inline bool inheritsFrom(InstructionContext child, inheritsFrom(child, IC_EVEX_L_W_XD); case IC_EVEX_OPSIZE: return inheritsFrom(child, IC_EVEX_W_OPSIZE) || - inheritsFrom(child, IC_EVEX_W_OPSIZE); + inheritsFrom(child, IC_EVEX_L_W_OPSIZE); case IC_EVEX_W: case IC_EVEX_W_XS: case IC_EVEX_W_XD: @@ -170,10 +176,24 @@ static inline bool inheritsFrom(InstructionContext child, case IC_EVEX_L_XD_K: case IC_EVEX_L_OPSIZE_K: return false; + case IC_EVEX_W_KZ: + case IC_EVEX_W_XS_KZ: + case IC_EVEX_W_XD_KZ: + case IC_EVEX_W_OPSIZE_KZ: + return false; + case IC_EVEX_L_KZ: + case IC_EVEX_L_XS_KZ: + case IC_EVEX_L_XD_KZ: + case IC_EVEX_L_OPSIZE_KZ: + return false; case IC_EVEX_L_W_K: case IC_EVEX_L_W_XS_K: case IC_EVEX_L_W_XD_K: case IC_EVEX_L_W_OPSIZE_K: + case IC_EVEX_L_W_KZ: + case IC_EVEX_L_W_XS_KZ: + case IC_EVEX_L_W_XD_KZ: + case IC_EVEX_L_W_OPSIZE_KZ: return false; case IC_EVEX_L2_K: case IC_EVEX_L2_B: @@ -181,12 +201,25 @@ static inline bool inheritsFrom(InstructionContext child, case IC_EVEX_L2_XD_K: case IC_EVEX_L2_OPSIZE_K: case IC_EVEX_L2_OPSIZE_B: + case IC_EVEX_L2_OPSIZE_K_B: + case IC_EVEX_L2_KZ: + case IC_EVEX_L2_XS_KZ: + case IC_EVEX_L2_XD_KZ: + case IC_EVEX_L2_OPSIZE_KZ: + case IC_EVEX_L2_OPSIZE_KZ_B: return false; case IC_EVEX_L2_W_K: + case IC_EVEX_L2_W_B: case IC_EVEX_L2_W_XS_K: case IC_EVEX_L2_W_XD_K: case IC_EVEX_L2_W_OPSIZE_K: case IC_EVEX_L2_W_OPSIZE_B: + case IC_EVEX_L2_W_OPSIZE_K_B: + case IC_EVEX_L2_W_KZ: + case IC_EVEX_L2_W_XS_KZ: + case IC_EVEX_L2_W_XD_KZ: + case IC_EVEX_L2_W_OPSIZE_KZ: + case IC_EVEX_L2_W_OPSIZE_KZ_B: return false; default: llvm_unreachable("Unknown instruction class"); @@ -207,7 +240,8 @@ static inline bool outranks(InstructionContext upper, #define ENUM_ENTRY(n, r, d) r, #define ENUM_ENTRY_K_B(n, r, d) ENUM_ENTRY(n, r, d) \ - ENUM_ENTRY(n##_K_B, r, d) ENUM_ENTRY(n##_K, r, d) ENUM_ENTRY(n##_B, r, d) + ENUM_ENTRY(n##_K_B, r, d) ENUM_ENTRY(n##_KZ_B, r, d) \ + ENUM_ENTRY(n##_KZ, r, d) ENUM_ENTRY(n##_K, r, d) ENUM_ENTRY(n##_B, r, d) static int ranks[IC_max] = { INSTRUCTION_CONTEXTS }; @@ -229,7 +263,8 @@ static inline const char* stringForContext(InstructionContext insnContext) { llvm_unreachable("Unhandled instruction class"); #define ENUM_ENTRY(n, r, d) case n: return #n; break; #define ENUM_ENTRY_K_B(n, r, d) ENUM_ENTRY(n, r, d) ENUM_ENTRY(n##_K_B, r, d)\ - ENUM_ENTRY(n##_K, r, d) ENUM_ENTRY(n##_B, r, d) + ENUM_ENTRY(n##_KZ, r, d) ENUM_ENTRY(n##_K, r, d) ENUM_ENTRY(n##_B, r, d)\ + ENUM_ENTRY(n##_KZ_B, r, d) INSTRUCTION_CONTEXTS #undef ENUM_ENTRY #undef ENUM_ENTRY_K_B @@ -259,35 +294,6 @@ static inline const char* stringForOperandEncoding(OperandEncoding encoding) { } } -void DisassemblerTables::emitOneID(raw_ostream &o, unsigned &i, InstrUID id, - bool addComma) const { - if (id) - o.indent(i * 2) << format("0x%hx", id); - else - o.indent(i * 2) << 0; - - if (addComma) - o << ", "; - else - o << " "; - - o << "/* "; - o << InstructionSpecifiers[id].name; - o << "*/"; - - o << "\n"; -} - -/// emitEmptyTable - Emits the modRMEmptyTable, which is used as a ID table by -/// all ModR/M decisions for instructions that are invalid for all possible -/// ModR/M byte values. -/// -/// @param o - The output stream on which to emit the table. -/// @param i - The indentation level for that output stream. -static void emitEmptyTable(raw_ostream &o, unsigned &i) { - o.indent(i * 2) << "0x0, /* EmptyTable */\n"; -} - /// getDecisionType - Determines whether a ModRM decision with 255 entries can /// be compacted by eliminating redundant information. /// @@ -387,6 +393,7 @@ DisassemblerTables::~DisassemblerTables() { void DisassemblerTables::emitModRMDecision(raw_ostream &o1, raw_ostream &o2, unsigned &i1, unsigned &i2, + unsigned &ModRMTableNum, ModRMDecision &decision) const { static uint32_t sTableNumber = 0; static uint32_t sEntryNumber = 1; @@ -405,44 +412,56 @@ void DisassemblerTables::emitModRMDecision(raw_ostream &o1, raw_ostream &o2, return; } - o1 << "/* Table" << sTableNumber << " */\n"; - i1++; + std::vector<unsigned> ModRMDecision; switch (dt) { default: llvm_unreachable("Unknown decision type"); case MODRM_ONEENTRY: - emitOneID(o1, i1, decision.instructionIDs[0], true); + ModRMDecision.push_back(decision.instructionIDs[0]); break; case MODRM_SPLITRM: - emitOneID(o1, i1, decision.instructionIDs[0x00], true); // mod = 0b00 - emitOneID(o1, i1, decision.instructionIDs[0xc0], true); // mod = 0b11 + ModRMDecision.push_back(decision.instructionIDs[0x00]); + ModRMDecision.push_back(decision.instructionIDs[0xc0]); break; case MODRM_SPLITREG: for (unsigned index = 0; index < 64; index += 8) - emitOneID(o1, i1, decision.instructionIDs[index], true); + ModRMDecision.push_back(decision.instructionIDs[index]); for (unsigned index = 0xc0; index < 256; index += 8) - emitOneID(o1, i1, decision.instructionIDs[index], true); + ModRMDecision.push_back(decision.instructionIDs[index]); break; case MODRM_SPLITMISC: for (unsigned index = 0; index < 64; index += 8) - emitOneID(o1, i1, decision.instructionIDs[index], true); + ModRMDecision.push_back(decision.instructionIDs[index]); for (unsigned index = 0xc0; index < 256; ++index) - emitOneID(o1, i1, decision.instructionIDs[index], true); + ModRMDecision.push_back(decision.instructionIDs[index]); break; case MODRM_FULL: for (unsigned index = 0; index < 256; ++index) - emitOneID(o1, i1, decision.instructionIDs[index], true); + ModRMDecision.push_back(decision.instructionIDs[index]); break; } - i1--; + unsigned &EntryNumber = ModRMTable[ModRMDecision]; + if (EntryNumber == 0) { + EntryNumber = ModRMTableNum; + + ModRMTableNum += ModRMDecision.size(); + o1 << "/* Table" << EntryNumber << " */\n"; + i1++; + for (std::vector<unsigned>::const_iterator I = ModRMDecision.begin(), + E = ModRMDecision.end(); I != E; ++I) { + o1.indent(i1 * 2) << format("0x%hx", *I) << ", /* " + << InstructionSpecifiers[*I].name << " */\n"; + } + i1--; + } o2.indent(i2) << "{ /* struct ModRMDecision */" << "\n"; i2++; o2.indent(i2) << stringForDecisionType(dt) << "," << "\n"; - o2.indent(i2) << sEntryNumber << " /* Table" << sTableNumber << " */\n"; + o2.indent(i2) << EntryNumber << " /* Table" << EntryNumber << " */\n"; i2--; o2.indent(i2) << "}"; @@ -476,6 +495,7 @@ void DisassemblerTables::emitModRMDecision(raw_ostream &o1, raw_ostream &o2, void DisassemblerTables::emitOpcodeDecision(raw_ostream &o1, raw_ostream &o2, unsigned &i1, unsigned &i2, + unsigned &ModRMTableNum, OpcodeDecision &decision) const { o2.indent(i2) << "{ /* struct OpcodeDecision */" << "\n"; i2++; @@ -487,7 +507,8 @@ void DisassemblerTables::emitOpcodeDecision(raw_ostream &o1, raw_ostream &o2, o2 << "/* 0x" << format("%02hhx", index) << " */" << "\n"; - emitModRMDecision(o1, o2, i1, i2, decision.modRMDecisions[index]); + emitModRMDecision(o1, o2, i1, i2, ModRMTableNum, + decision.modRMDecisions[index]); if (index < 255) o2 << ","; @@ -503,6 +524,7 @@ void DisassemblerTables::emitOpcodeDecision(raw_ostream &o1, raw_ostream &o2, void DisassemblerTables::emitContextDecision(raw_ostream &o1, raw_ostream &o2, unsigned &i1, unsigned &i2, + unsigned &ModRMTableNum, ContextDecision &decision, const char* name) const { o2.indent(i2) << "static const struct ContextDecision " << name << " = {\n"; @@ -516,7 +538,8 @@ void DisassemblerTables::emitContextDecision(raw_ostream &o1, raw_ostream &o2, o2 << " */"; o2 << "\n"; - emitOpcodeDecision(o1, o2, i1, i2, decision.opcodeDecisions[index]); + emitOpcodeDecision(o1, o2, i1, i2, ModRMTableNum, + decision.opcodeDecisions[index]); if (index + 1 < IC_max) o2 << ", "; @@ -622,6 +645,12 @@ void DisassemblerTables::emitContextTable(raw_ostream &o, unsigned &i) const { if ((index & ATTR_VEXL) && (index & ATTR_REXW) && (index & ATTR_OPSIZE)) o << "IC_VEX_L_W_OPSIZE"; + else if ((index & ATTR_VEXL) && (index & ATTR_REXW) && (index & ATTR_XD)) + o << "IC_VEX_L_W_XD"; + else if ((index & ATTR_VEXL) && (index & ATTR_REXW) && (index & ATTR_XS)) + o << "IC_VEX_L_W_XS"; + else if ((index & ATTR_VEXL) && (index & ATTR_REXW)) + o << "IC_VEX_L_W"; else if ((index & ATTR_VEXL) && (index & ATTR_OPSIZE)) o << "IC_VEX_L_OPSIZE"; else if ((index & ATTR_VEXL) && (index & ATTR_XD)) @@ -699,13 +728,17 @@ void DisassemblerTables::emitContextTable(raw_ostream &o, unsigned &i) const { } void DisassemblerTables::emitContextDecisions(raw_ostream &o1, raw_ostream &o2, - unsigned &i1, unsigned &i2) const { - emitContextDecision(o1, o2, i1, i2, *Tables[0], ONEBYTE_STR); - emitContextDecision(o1, o2, i1, i2, *Tables[1], TWOBYTE_STR); - emitContextDecision(o1, o2, i1, i2, *Tables[2], THREEBYTE38_STR); - emitContextDecision(o1, o2, i1, i2, *Tables[3], THREEBYTE3A_STR); - emitContextDecision(o1, o2, i1, i2, *Tables[4], THREEBYTEA6_STR); - emitContextDecision(o1, o2, i1, i2, *Tables[5], THREEBYTEA7_STR); + unsigned &i1, unsigned &i2, + unsigned &ModRMTableNum) const { + emitContextDecision(o1, o2, i1, i2, ModRMTableNum, *Tables[0], ONEBYTE_STR); + emitContextDecision(o1, o2, i1, i2, ModRMTableNum, *Tables[1], TWOBYTE_STR); + emitContextDecision(o1, o2, i1, i2, ModRMTableNum, *Tables[2], THREEBYTE38_STR); + emitContextDecision(o1, o2, i1, i2, ModRMTableNum, *Tables[3], THREEBYTE3A_STR); + emitContextDecision(o1, o2, i1, i2, ModRMTableNum, *Tables[4], THREEBYTEA6_STR); + emitContextDecision(o1, o2, i1, i2, ModRMTableNum, *Tables[5], THREEBYTEA7_STR); + emitContextDecision(o1, o2, i1, i2, ModRMTableNum, *Tables[6], XOP8_MAP_STR); + emitContextDecision(o1, o2, i1, i2, ModRMTableNum, *Tables[7], XOP9_MAP_STR); + emitContextDecision(o1, o2, i1, i2, ModRMTableNum, *Tables[8], XOPA_MAP_STR); } void DisassemblerTables::emit(raw_ostream &o) const { @@ -724,11 +757,17 @@ void DisassemblerTables::emit(raw_ostream &o) const { emitContextTable(o, i2); o << "\n"; + unsigned ModRMTableNum = 0; + o << "static const InstrUID modRMTable[] = {\n"; i1++; - emitEmptyTable(o1, i1); + std::vector<unsigned> EmptyTable(1, 0); + ModRMTable[EmptyTable] = ModRMTableNum; + ModRMTableNum += EmptyTable.size(); + o1 << "/* EmptyTable */\n"; + o1.indent(i1 * 2) << "0x0,\n"; i1--; - emitContextDecisions(o1, o2, i1, i2); + emitContextDecisions(o1, o2, i1, i2, ModRMTableNum); o << o1.str(); o << " 0x0\n"; diff --git a/utils/TableGen/X86DisassemblerTables.h b/utils/TableGen/X86DisassemblerTables.h index 01aeaaf..bf8b127 100644 --- a/utils/TableGen/X86DisassemblerTables.h +++ b/utils/TableGen/X86DisassemblerTables.h @@ -20,6 +20,7 @@ #include "X86DisassemblerShared.h" #include "X86ModRMFilters.h" #include "llvm/Support/raw_ostream.h" +#include <map> #include <vector> namespace llvm { @@ -39,7 +40,14 @@ private: /// [3] three-byte opcodes of the form 0f 3a __ /// [4] three-byte opcodes of the form 0f a6 __ /// [5] three-byte opcodes of the form 0f a7 __ - ContextDecision* Tables[6]; + /// [6] XOP8 map opcode + /// [7] XOP9 map opcode + /// [8] XOPA map opcode + ContextDecision* Tables[9]; + + // Table of ModRM encodings. + typedef std::map<std::vector<unsigned>, unsigned> ModRMMapTy; + mutable ModRMMapTy ModRMTable; /// The instruction information table std::vector<InstructionSpecifier> InstructionSpecifiers; @@ -47,22 +55,6 @@ private: /// True if there are primary decode conflicts in the instruction set bool HasConflicts; - /// emitOneID - Emits a table entry for a single instruction entry, at the - /// innermost level of the structure hierarchy. The entry is printed out - /// in the format "nnnn, /* MNEMONIC */" where nnnn is the ID in decimal, - /// the comma is printed if addComma is true, and the menonic is the name - /// of the instruction as listed in the LLVM tables. - /// - /// @param o - The output stream to print the entry on. - /// @param i - The indentation level for o. - /// @param id - The unique ID of the instruction to print. - /// @param addComma - Whether or not to print a comma after the ID. True if - /// additional items will follow. - void emitOneID(raw_ostream &o, - uint32_t &i, - InstrUID id, - bool addComma) const; - /// emitModRMDecision - Emits a table of entries corresponding to a single /// ModR/M decision. Compacts the ModR/M decision if possible. ModR/M /// decisions are printed as: @@ -91,12 +83,11 @@ private: /// @param o2 - The output stream to print the decision structure to. /// @param i1 - The indentation level to use with stream o1. /// @param i2 - The indentation level to use with stream o2. + /// @param ModRMTableNum - next table number for adding to ModRMTable. /// @param decision - The ModR/M decision to emit. This decision has 256 /// entries - emitModRMDecision decides how to compact it. - void emitModRMDecision(raw_ostream &o1, - raw_ostream &o2, - uint32_t &i1, - uint32_t &i2, + void emitModRMDecision(raw_ostream &o1, raw_ostream &o2, + unsigned &i1, unsigned &i2, unsigned &ModRMTableNum, ModRMDecision &decision) const; /// emitOpcodeDecision - Emits an OpcodeDecision and all its subsidiary ModR/M @@ -120,12 +111,11 @@ private: /// @param o2 - The output stream for the decision structure itself. /// @param i1 - The indent level to use with stream o1. /// @param i2 - The indent level to use with stream o2. + /// @param ModRMTableNum - next table number for adding to ModRMTable. /// @param decision - The OpcodeDecision to emit along with its subsidiary /// structures. - void emitOpcodeDecision(raw_ostream &o1, - raw_ostream &o2, - uint32_t &i1, - uint32_t &i2, + void emitOpcodeDecision(raw_ostream &o1, raw_ostream &o2, + unsigned &i1, unsigned &i2, unsigned &ModRMTableNum, OpcodeDecision &decision) const; /// emitContextDecision - Emits a ContextDecision and all its subsidiary @@ -155,15 +145,13 @@ private: /// @param o2 - The output stream to print the decision structure to. /// @param i1 - The indent level to use with stream o1. /// @param i2 - The indent level to use with stream o2. + /// @param ModRMTableNum - next table number for adding to ModRMTable. /// @param decision - The ContextDecision to emit along with its subsidiary /// structures. /// @param name - The name for the ContextDecision. - void emitContextDecision(raw_ostream &o1, - raw_ostream &o2, - uint32_t &i1, - uint32_t &i2, - ContextDecision &decision, - const char* name) const; + void emitContextDecision(raw_ostream &o1, raw_ostream &o2, + unsigned &i1, unsigned &i2, unsigned &ModRMTableNum, + ContextDecision &decision, const char* name) const; /// emitInstructionInfo - Prints the instruction specifier table, which has /// one entry for each instruction, and contains name and operand @@ -194,7 +182,7 @@ private: /// @param o - The output stream to which the instruction table should be /// written. /// @param i - The indent level for use with the stream. - void emitInstructionInfo(raw_ostream &o, uint32_t &i) const; + void emitInstructionInfo(raw_ostream &o, unsigned &i) const; /// emitContextTable - Prints the table that is used to translate from an /// instruction attribute mask to an instruction context. This table is @@ -220,10 +208,10 @@ private: /// @param o2 - The output stream to print the decision structures to. /// @param i1 - The indent level to use with stream o1. /// @param i2 - The indent level to use with stream o2. - void emitContextDecisions(raw_ostream &o1, - raw_ostream &o2, - uint32_t &i1, - uint32_t &i2) const; + /// @param ModRMTableNum - next table number for adding to ModRMTable. + void emitContextDecisions(raw_ostream &o1, raw_ostream &o2, + unsigned &i1, unsigned &i2, + unsigned &ModRMTableNum) const; /// setTableFields - Uses a ModRMFilter to set the appropriate entries in a /// ModRMDecision to refer to a particular instruction ID. diff --git a/utils/TableGen/X86RecognizableInstr.cpp b/utils/TableGen/X86RecognizableInstr.cpp index 7962f9b..708e72d 100644 --- a/utils/TableGen/X86RecognizableInstr.cpp +++ b/utils/TableGen/X86RecognizableInstr.cpp @@ -79,7 +79,8 @@ namespace X86Local { DC = 7, DD = 8, DE = 9, DF = 10, XD = 11, XS = 12, T8 = 13, P_TA = 14, - A6 = 15, A7 = 16, T8XD = 17, T8XS = 18, TAXD = 19 + A6 = 15, A7 = 16, T8XD = 17, T8XS = 18, TAXD = 19, + XOP8 = 20, XOP9 = 21, XOPA = 22 }; } @@ -134,6 +135,10 @@ namespace X86Local { #define THREE_BYTE_38_EXTENSION_TABLES \ EXTENSION_TABLE(F3) +#define XOP9_MAP_EXTENSION_TABLES \ + EXTENSION_TABLE(01) \ + EXTENSION_TABLE(02) + using namespace X86Disassembler; /// needsModRMForDecode - Indicates whether a particular instruction requires a @@ -239,6 +244,7 @@ RecognizableInstr::RecognizableInstr(DisassemblerTables &tables, HasEVEXPrefix = Rec->getValueAsBit("hasEVEXPrefix"); HasEVEX_L2Prefix = Rec->getValueAsBit("hasEVEX_L2"); HasEVEX_K = Rec->getValueAsBit("hasEVEX_K"); + HasEVEX_KZ = Rec->getValueAsBit("hasEVEX_Z"); HasEVEX_B = Rec->getValueAsBit("hasEVEX_B"); HasLockPrefix = Rec->getValueAsBit("hasLockPrefix"); IsCodeGenOnly = Rec->getValueAsBit("isCodeGenOnly"); @@ -299,8 +305,10 @@ void RecognizableInstr::processInstr(DisassemblerTables &tables, recogInstr.emitDecodePath(tables); } -#define EVEX_KB(n) (HasEVEX_K && HasEVEX_B? n##_K_B : \ - (HasEVEX_K? n##_K : (HasEVEX_B ? n##_B : n))) +#define EVEX_KB(n) (HasEVEX_KZ && HasEVEX_B ? n##_KZ_B : \ + (HasEVEX_K && HasEVEX_B ? n##_K_B : \ + (HasEVEX_KZ ? n##_KZ : \ + (HasEVEX_K? n##_K : (HasEVEX_B ? n##_B : n))))) InstructionContext RecognizableInstr::insnContext() const { InstructionContext insnContext; @@ -486,7 +494,8 @@ RecognizableInstr::filter_ret RecognizableInstr::filter() const { assert(Rec->isSubClassOf("X86Inst") && "Can only filter X86 instructions"); if (Form == X86Local::Pseudo || - (IsCodeGenOnly && Name.find("_REV") == Name.npos)) + (IsCodeGenOnly && Name.find("_REV") == Name.npos && + Name.find("INC32") == Name.npos && Name.find("DEC32") == Name.npos)) return FILTER_STRONG; @@ -516,35 +525,17 @@ RecognizableInstr::filter_ret RecognizableInstr::filter() const { // Filter out alternate forms of AVX instructions if (Name.find("_alt") != Name.npos || - Name.find("XrYr") != Name.npos || - (Name.find("r64r") != Name.npos && Name.find("r64r64") == Name.npos) || + (Name.find("r64r") != Name.npos && Name.find("r64r64") == Name.npos && Name.find("r64r8") == Name.npos) || Name.find("_64mr") != Name.npos || - Name.find("Xrr") != Name.npos || Name.find("rr64") != Name.npos) return FILTER_WEAK; // Special cases. - if (Name.find("PCMPISTRI") != Name.npos && Name != "PCMPISTRI") - return FILTER_WEAK; - if (Name.find("PCMPESTRI") != Name.npos && Name != "PCMPESTRI") - return FILTER_WEAK; - - if (Name.find("MOV") != Name.npos && Name.find("r0") != Name.npos) - return FILTER_WEAK; - if (Name.find("MOVZ") != Name.npos && Name.find("MOVZX") == Name.npos) - return FILTER_WEAK; - if (Name.find("Fs") != Name.npos) - return FILTER_WEAK; if (Name == "PUSH64i16" || Name == "MOVPQI2QImr" || Name == "VMOVPQI2QImr" || - Name == "MMX_MOVD64rrv164" || - Name == "MOV64ri64i32" || - Name == "VMASKMOVDQU64" || - Name == "VEXTRACTPSrr64" || - Name == "VMOVQd64rr" || - Name == "VMOVQs64rr") + Name == "VMASKMOVDQU64") return FILTER_WEAK; // XACQUIRE and XRELEASE reuse REPNE and REP respectively. @@ -553,11 +544,6 @@ RecognizableInstr::filter_ret RecognizableInstr::filter() const { Name == "XRELEASE_PREFIX") return FILTER_WEAK; - if (HasFROperands && Name.find("MOV") != Name.npos && - ((Name.find("2") != Name.npos && Name.find("32") == Name.npos) || - (Name.find("to") != Name.npos))) - return FILTER_STRONG; - return FILTER_NORMAL; } @@ -818,17 +804,20 @@ void RecognizableInstr::emitInstructionSpecifier(DisassemblerTables &tables) { case X86Local::MRM5r: case X86Local::MRM6r: case X86Local::MRM7r: - // Operand 1 is a register operand in the R/M field. - // Operand 2 (optional) is an immediate or relocation. - // Operand 3 (optional) is an immediate. - if (HasVEX_4VPrefix) - assert(numPhysicalOperands <= 3 && - "Unexpected number of operands for MRMnRFrm with VEX_4V"); - else - assert(numPhysicalOperands <= 3 && - "Unexpected number of operands for MRMnRFrm"); + { + // Operand 1 is a register operand in the R/M field. + // Operand 2 (optional) is an immediate or relocation. + // Operand 3 (optional) is an immediate. + unsigned kOp = (HasEVEX_K) ? 1:0; + unsigned Op4v = (HasVEX_4VPrefix) ? 1:0; + if (numPhysicalOperands > 3 + kOp + Op4v) + llvm_unreachable("Unexpected number of operands for MRMnr"); + } if (HasVEX_4VPrefix) HANDLE_OPERAND(vvvvRegister) + + if (HasEVEX_K) + HANDLE_OPERAND(writemaskRegister) HANDLE_OPTIONAL(rmRegister) HANDLE_OPTIONAL(relocation) HANDLE_OPTIONAL(immediate) @@ -841,16 +830,19 @@ void RecognizableInstr::emitInstructionSpecifier(DisassemblerTables &tables) { case X86Local::MRM5m: case X86Local::MRM6m: case X86Local::MRM7m: - // Operand 1 is a memory operand (possibly SIB-extended) - // Operand 2 (optional) is an immediate or relocation. - if (HasVEX_4VPrefix) - assert(numPhysicalOperands >= 2 && numPhysicalOperands <= 3 && - "Unexpected number of operands for MRMnMFrm"); - else - assert(numPhysicalOperands >= 1 && numPhysicalOperands <= 2 && - "Unexpected number of operands for MRMnMFrm"); + { + // Operand 1 is a memory operand (possibly SIB-extended) + // Operand 2 (optional) is an immediate or relocation. + unsigned kOp = (HasEVEX_K) ? 1:0; + unsigned Op4v = (HasVEX_4VPrefix) ? 1:0; + if (numPhysicalOperands < 1 + kOp + Op4v || + numPhysicalOperands > 2 + kOp + Op4v) + llvm_unreachable("Unexpected number of operands for MRMnm"); + } if (HasVEX_4VPrefix) HANDLE_OPERAND(vvvvRegister) + if (HasEVEX_K) + HANDLE_OPERAND(writemaskRegister) HANDLE_OPERAND(memory) HANDLE_OPTIONAL(relocation) break; @@ -902,6 +894,7 @@ void RecognizableInstr::emitDecodePath(DisassemblerTables &tables) const { uint8_t opcodeToSet = 0; switch (Prefix) { + default: llvm_unreachable("Invalid prefix!"); // Extended two-byte opcodes can start with f2 0f, f3 0f, or 0f case X86Local::XD: case X86Local::XS: @@ -1015,6 +1008,63 @@ void RecognizableInstr::emitDecodePath(DisassemblerTables &tables) const { filter = new DumbFilter(); opcodeToSet = Opcode; break; + case X86Local::XOP8: + opcodeType = XOP8_MAP; + if (needsModRMForDecode(Form)) + filter = new ModFilter(isRegFormat(Form)); + else + filter = new DumbFilter(); + opcodeToSet = Opcode; + break; + case X86Local::XOP9: + opcodeType = XOP9_MAP; + switch (Opcode) { + default: + if (needsModRMForDecode(Form)) + filter = new ModFilter(isRegFormat(Form)); + else + filter = new DumbFilter(); + break; +#define EXTENSION_TABLE(n) case 0x##n: + XOP9_MAP_EXTENSION_TABLES +#undef EXTENSION_TABLE + switch (Form) { + default: + llvm_unreachable("Unhandled XOP9 extended opcode"); + case X86Local::MRM0r: + case X86Local::MRM1r: + case X86Local::MRM2r: + case X86Local::MRM3r: + case X86Local::MRM4r: + case X86Local::MRM5r: + case X86Local::MRM6r: + case X86Local::MRM7r: + filter = new ExtendedFilter(true, Form - X86Local::MRM0r); + break; + case X86Local::MRM0m: + case X86Local::MRM1m: + case X86Local::MRM2m: + case X86Local::MRM3m: + case X86Local::MRM4m: + case X86Local::MRM5m: + case X86Local::MRM6m: + case X86Local::MRM7m: + filter = new ExtendedFilter(false, Form - X86Local::MRM0m); + break; + MRM_MAPPING + } // switch (Form) + break; + } // switch (Opcode) + opcodeToSet = Opcode; + break; + case X86Local::XOPA: + opcodeType = XOPA_MAP; + if (needsModRMForDecode(Form)) + filter = new ModFilter(isRegFormat(Form)); + else + filter = new DumbFilter(); + opcodeToSet = Opcode; + break; case X86Local::D8: case X86Local::D9: case X86Local::DA: @@ -1035,7 +1085,7 @@ void RecognizableInstr::emitDecodePath(DisassemblerTables &tables) const { opcodeToSet = 0xd8 + (Prefix - X86Local::D8); break; case X86Local::REP: - default: + case 0: opcodeType = ONEBYTE; switch (Opcode) { #define EXTENSION_TABLE(n) case 0x##n: @@ -1166,6 +1216,7 @@ OperandType RecognizableInstr::typeFromString(const std::string &s, TYPE("i32i8imm", TYPE_IMM32) TYPE("u32u8imm", TYPE_IMM32) TYPE("GR32", TYPE_Rv) + TYPE("GR32orGR64", TYPE_R32) TYPE("i64mem", TYPE_Mv) TYPE("i64i32imm", TYPE_IMM64) TYPE("i64i8imm", TYPE_IMM64) @@ -1276,6 +1327,7 @@ OperandEncoding RecognizableInstr::rmRegisterEncodingFromString bool hasOpSizePrefix) { ENCODING("GR16", ENCODING_RM) ENCODING("GR32", ENCODING_RM) + ENCODING("GR32orGR64", ENCODING_RM) ENCODING("GR64", ENCODING_RM) ENCODING("GR8", ENCODING_RM) ENCODING("VR128", ENCODING_RM) @@ -1299,6 +1351,7 @@ OperandEncoding RecognizableInstr::roRegisterEncodingFromString bool hasOpSizePrefix) { ENCODING("GR16", ENCODING_REG) ENCODING("GR32", ENCODING_REG) + ENCODING("GR32orGR64", ENCODING_REG) ENCODING("GR64", ENCODING_REG) ENCODING("GR8", ENCODING_REG) ENCODING("VR128", ENCODING_REG) diff --git a/utils/TableGen/X86RecognizableInstr.h b/utils/TableGen/X86RecognizableInstr.h index 7e1d362..4d4686e 100644 --- a/utils/TableGen/X86RecognizableInstr.h +++ b/utils/TableGen/X86RecognizableInstr.h @@ -72,6 +72,8 @@ private: bool HasEVEX_L2Prefix; /// The hasEVEX_K field from the record bool HasEVEX_K; + /// The hasEVEX_KZ field from the record + bool HasEVEX_KZ; /// The hasEVEX_B field from the record bool HasEVEX_B; /// The hasLockPrefix field from the record diff --git a/utils/buildit/build_llvm b/utils/buildit/build_llvm index 4ed25f2..9e93216 100755 --- a/utils/buildit/build_llvm +++ b/utils/buildit/build_llvm @@ -104,7 +104,8 @@ COMMON_CONFIGURE_OPTS="\ --enable-assertions=$LLVM_ASSERTIONS \ --enable-optimized=$LLVM_OPTIMIZED \ --disable-bindings \ - --disable-zlib" + --disable-zlib \ + --enable-terminfo=no" COMMON_MAKEFLAGS="\ UNIVERSAL=1 \ diff --git a/utils/emacs/llvm-mode.el b/utils/emacs/llvm-mode.el index 25d9742..99d3294 100644 --- a/utils/emacs/llvm-mode.el +++ b/utils/emacs/llvm-mode.el @@ -32,16 +32,23 @@ "null" "undef" "to" "except" "not" "target" "endian" "little" "big" "pointersize" "volatile" "fastcc" "coldcc" "cc") 'words) . font-lock-keyword-face) ;; Arithmetic and Logical Operators - `(,(regexp-opt '("add" "sub" "mul" "div" "rem" "and" "or" "xor" + `(,(regexp-opt '("add" "sub" "mul" "sdiv" "udiv" "urem" "srem" "and" "or" "xor" "setne" "seteq" "setlt" "setgt" "setle" "setge") 'words) . font-lock-keyword-face) ;; Floating-point operators `(,(regexp-opt '("fadd" "fsub" "fmul" "fdiv" "frem") 'words) . font-lock-keyword-face) ;; Special instructions - `(,(regexp-opt '("phi" "tail" "call" "cast" "select" "to" "shl" "shr" "fcmp" "icmp" "vaarg" "vanext") 'words) . font-lock-keyword-face) + `(,(regexp-opt '("phi" "tail" "call" "select" "to" "shl" "lshr" "ashr" "fcmp" "icmp" "va_arg" "landingpad") 'words) . font-lock-keyword-face) ;; Control instructions - `(,(regexp-opt '("ret" "br" "switch" "invoke" "unwind" "unreachable") 'words) . font-lock-keyword-face) + `(,(regexp-opt '("ret" "br" "switch" "invoke" "resume" "unwind" "unreachable" "indirectbr") 'words) . font-lock-keyword-face) ;; Memory operators - `(,(regexp-opt '("malloc" "alloca" "free" "load" "store" "getelementptr") 'words) . font-lock-keyword-face) + `(,(regexp-opt '("malloc" "alloca" "free" "load" "store" "getelementptr" "fence" "cmpxchg" "atomicrmw") 'words) . font-lock-keyword-face) + ;; Casts + `(,(regexp-opt '("bitcast" "inttoptr" "ptrtoint" "trunc" "zext" "sext" "fptrunc" "fpext" "fptoui" "fptosi" "uitofp" "sitofp" "addrspacecast") 'words) . font-lock-keyword-face) + ;; Vector ops + `(,(regexp-opt '("extractelement" "insertelement" "shufflevector") 'words) . font-lock-keyword-face) + ;; Aggregate ops + `(,(regexp-opt '("extractvalue" "insertvalue") 'words) . font-lock-keyword-face) + ) "Syntax highlighting for LLVM" ) diff --git a/utils/kate/llvm.xml b/utils/kate/llvm.xml index 1778cfc..dfacfb5 100644 --- a/utils/kate/llvm.xml +++ b/utils/kate/llvm.xml @@ -41,7 +41,6 @@ <item> private </item> <item> linker_private </item> <item> linker_private_weak </item> - <item> linker_private_weak_def_auto </item> <item> internal </item> <item> available_externally </item> <item> linkonce </item> @@ -85,6 +84,7 @@ <item> noredzone </item> <item> noreturn </item> <item> nounwind </item> + <item> optnone </item> <item> optsize </item> <item> readnone </item> <item> readonly </item> @@ -157,6 +157,7 @@ <item> ptrtoint </item> <item> inttoptr </item> <item> bitcast </item> + <item> addrspacecast </item> <item> icmp </item> <item> fcmp </item> <item> phi </item> diff --git a/utils/lit/TODO b/utils/lit/TODO index d2ff842..c1a60c6 100644 --- a/utils/lit/TODO +++ b/utils/lit/TODO @@ -1,26 +1,166 @@ - - Move temp directory name into local test config. +================ + lit TODO Items +================ - - Add --show-unsupported, don't show by default? +Infrastructure +============== - - Optionally use multiprocessing. +1. Change to always load suites, then resolve command line arguments? - - Support valgrind in all configs, and LLVM style valgrind. + Currently we expect each input argument to be a path on disk; we do a + recursive search to find the test suite for each item, but then we only do a + local search based at the input path to find tests. Additionally, for any path + that matches a file on disk we explicitly construct a test instance (bypassing + the formats on discovery implementation). - - Support a timeout / ulimit. + This has a couple problems: - - Rename 'lit' injected variable for config to be lit_config. + * The test format doesn't have control over the test instances that result + from file paths. - - Allow import of 'lit' in test suite definitions. + * It isn't possible to specify virtual tests as inputs. For example, it is not + possible to specify an individual subtest to run with the googletest format. - - Create an explicit test suite object (instead of using the top-level - TestingConfig object). + * The test format doesn't have full control over the discovery of tests in + subdirectories. - - Allow 'lit' driver to cooperate with test suites to add options (or at least - sanitize accepted params). + Instead, we should move to a model whereby first all of the input specifiers + are resolved to test suites, and then the resolution of the input specifier is + delegated to each test suite. This could take a couple forms: - - Consider move to identifying all tests by path-to-test-suite and then path to + * We could resolve to test suites, then fully load each test suite, then have + a fixed process to map input specifiers to tests in the test suite + (presumably based on path-in-suite derivations). This has the benefit of + being consistent across all test formats, but the downside of requiring + loading the entire test suite. + + * We could delegate all of the resolution of specifiers to the test + suite. This would allow formats that anticipate large test suites to manage + their own resolution for better performance. We could provide a default + resolution strategy that was similar to what we do now (start at subpaths + for directories, but allow the test format control over what happens for + individual tests). + +2. Consider move to identifying all tests by path-to-test-suite and then path to subtest, and don't use test suite names. - - Consider move to change workflow to always load suites, then resolve command - line arguments. + Currently the test suite name is presented as part of test names, but it has + no other useful function, and it is something that has to be skipped over to + cut-and-paste a name to subsequently use to rerun a test. If we just + represented each test suite by the path to its suite, then it would allow more + easy cut-and-paste of the test output lines. This has the downside that the + lines might get rather long. + +3. Allow 'lit' driver to cooperate with test formats and suites to add options + (or at least sanitize accepted params). + + We have started to use the --params method more and more extensively, and it is + cumbersome and error prone. Additionally, there are currently various options + ``lit`` honors that should more correctly be specified as belonging to the + ShTest test format. + + It would be really nice if we could allow test formats and test suites to add + their own options to be parsed. The difficulty here, of course, is that we + don't know what test formats or test suites are in use until we have parsed the + input specifiers. For test formats we could ostensibly require all the possible + formats to be registered in order to have options, but for test suites we would + certainly have to load the suite before we can query it for what options it + understands. + + That leaves us with the following options: + + * Currently we could almost get away with parsing the input specifiers without + having done option parsing first (the exception is ``--config-prefix``) but + that isn't a very extensible design. + + * We could make a distinction in the command line syntax for test format and + test suite options. For example, we could require something like:: + + lit -j 1 -sv input-specifier -- --some-format-option + + which would be relatively easy to implement with optparser (I think). + + * We could allow fully interspersed arguments by first extracting the options + lit knows about and parsing them, then dispatching the remainder to the + formats. This seems the most convenient for users, who are unlikely to care + about (or even be aware of) the distinction between the generic lit + infrastructure and format or suite specific options. + +4. Eliminate duplicate execution models for ShTest tests. + + Currently, the ShTest format uses tests written with shell-script like syntax, + and executes them in one of two ways. The first way is by converting them into + a bash script and literally executing externally them using bash. The second + way is through the use of an internal shell parser and shell execution code + (built on the subprocess module). The external execution mode is used on most + Unix systems that have bash, the internal execution mode is used on Windows. + + Having two ways to do the same thing is error prone and leads to unnecessary + complexity in the testing environment. Additionally, because the mode that + converts scripts to bash doesn't try and validate the syntax, it is possible + to write tests that use bash shell features unsupported by the internal + shell. Such tests won't work on Windows but this may not be obvious to the + developer writing the test. + + Another limitation is that when executing the scripts externally, the ShTest + format has no idea which commands fail, or what output comes from which + commands, so this limits how convenient the output of ShTest failures can be + and limits other features (for example, knowing what temporary files were + written). + + We should eliminate having two ways of executing the same tests to reduce + platform differences and make it easier to develop new features in the ShTest + module. This is currently blocked on: + + * The external execution mode is faster in some situations, because it avoids + being bottlenecked on the GIL. This can hopefully be obviated simply by + using --use-processes. + + * Some tests in LLVM/Clang are explicitly disabled with the internal shell + (because they use features specific to bash). We would need to rewrite these + tests, or add additional features to the internal shell handling to allow + them to pass. + +5. Consider changing core to support setup vs. execute distinction. + + Many of the existing test formats are cleanly divided into two phases, once + parses the test format and extracts XFAIL and REQUIRES information, etc., and + the other code actually executes the test. + + We could make this distinction part of the core infrastructure and that would + enable a couple things: + + * The REQUIREs handling could be lifted to the core, which is nice. + + * This would provide a clear place to insert subtest support, because the + setup phase could be responsible for providing subtests back to the + core. That would provide part of the infrastructure to parallelize them, for + example, and would probably interact well with other possible features like + parameterized tests. + + * This affords a clean implementation of --no-execute. + + * One possible downside could be for test formats that cannot determine their + subtests without having executed the test. Supporting such formats would + either force the test to actually be executed in the setup stage (which + might be ok, as long as the API was explicitly phrased to support that), or + would mean we are forced into supporting subtests as return values from the + execute phase. + + Any format can just keep all of its code in execute, presumably, so the only + cost of implementing this is its impact on the API and futures changes. + + +Miscellaneous +============= + +* Move temp directory name into local test config. + +* Add --show-unsupported, don't show by default? + +* Support valgrind in all configs, and LLVM style valgrind. + +* Support a timeout / ulimit. +* Create an explicit test suite object (instead of using the top-level + TestingConfig object). diff --git a/utils/lit/examples/README.txt b/utils/lit/examples/README.txt new file mode 100644 index 0000000..a59daa8 --- /dev/null +++ b/utils/lit/examples/README.txt @@ -0,0 +1,7 @@ +============== + lit Examples +============== + +This directory contains examples of 'lit' test suite configurations. The test +suites they define can be run with 'lit examples/example-name', for more details +see the README in each example. diff --git a/utils/lit/examples/many-tests/README.txt b/utils/lit/examples/many-tests/README.txt new file mode 100644 index 0000000..6bffff1 --- /dev/null +++ b/utils/lit/examples/many-tests/README.txt @@ -0,0 +1,10 @@ +======================== + Many Tests lit Example +======================== + +This directory contains a trivial lit test suite configuration that defines a +custom test format which just generates a large (N=10000) number of tests that +do a small amount of work in the Python test execution code. + +This test suite is useful for testing the performance of lit on large numbers of +tests. diff --git a/utils/lit/lit/ExampleTests/ManyTests/lit.local.cfg b/utils/lit/examples/many-tests/lit.cfg index 6cc4752..8f7b940 100644 --- a/utils/lit/lit/ExampleTests/ManyTests/lit.local.cfg +++ b/utils/lit/examples/many-tests/lit.cfg @@ -1,6 +1,6 @@ # -*- Python -*- -Test = lit.Test +from lit import Test class ManyTests(object): def __init__(self, N=10000): diff --git a/utils/lit/lit/ExampleTests/Clang/fsyntax-only.c b/utils/lit/lit/ExampleTests/Clang/fsyntax-only.c deleted file mode 100644 index a4a064b..0000000 --- a/utils/lit/lit/ExampleTests/Clang/fsyntax-only.c +++ /dev/null @@ -1,4 +0,0 @@ -// RUN: clang -fsyntax-only -Xclang -verify %s - -int f0(void) {} // expected-warning {{control reaches end of non-void function}} - diff --git a/utils/lit/lit/ExampleTests/Clang/lit.cfg b/utils/lit/lit/ExampleTests/Clang/lit.cfg deleted file mode 100644 index 9295bd9..0000000 --- a/utils/lit/lit/ExampleTests/Clang/lit.cfg +++ /dev/null @@ -1,47 +0,0 @@ -# -*- Python -*- - -# Configuration file for the 'lit' test runner. - -# name: The name of this test suite. -config.name = 'Clang' - -# testFormat: The test format to use to interpret tests. -# -# For now we require '&&' between commands, until they get globally killed and -# the test runner updated. -config.test_format = lit.formats.ShTest(execute_external = True) - -# suffixes: A list of file extensions to treat as test files. -config.suffixes = ['.c', '.cpp', '.m', '.mm'] - -# target_triple: Used by ShTest format for XFAIL checks. -config.target_triple = 'foo' - -### - -# Discover the 'clang' and 'clangcc' to use. - -import os - -def inferClang(PATH): - # Determine which clang to use. - clang = os.getenv('CLANG') - - # If the user set clang in the environment, definitely use that and don't - # try to validate. - if clang: - return clang - - # Otherwise look in the path. - clang = lit.util.which('clang', PATH) - - if not clang: - lit.fatal("couldn't find 'clang' program, try setting " - "CLANG in your environment") - - return clang - -clang = inferClang(config.environment['PATH']) -if not lit.quiet: - lit.note('using clang: %r' % clang) -config.substitutions.append( (' clang ', ' ' + clang + ' ') ) diff --git a/utils/lit/lit/ExampleTests/LLVM.InTree/test/Bar/data.txt b/utils/lit/lit/ExampleTests/LLVM.InTree/test/Bar/data.txt deleted file mode 100644 index 45b983b..0000000 --- a/utils/lit/lit/ExampleTests/LLVM.InTree/test/Bar/data.txt +++ /dev/null @@ -1 +0,0 @@ -hi diff --git a/utils/lit/lit/ExampleTests/LLVM.InTree/test/Bar/pct-S.ll b/utils/lit/lit/ExampleTests/LLVM.InTree/test/Bar/pct-S.ll deleted file mode 100644 index 3ff3633..0000000 --- a/utils/lit/lit/ExampleTests/LLVM.InTree/test/Bar/pct-S.ll +++ /dev/null @@ -1 +0,0 @@ -; RUN: grep "hi" %S/data.txt diff --git a/utils/lit/lit/ExampleTests/LLVM.InTree/test/lit.cfg b/utils/lit/lit/ExampleTests/LLVM.InTree/test/lit.cfg deleted file mode 100644 index 533c445..0000000 --- a/utils/lit/lit/ExampleTests/LLVM.InTree/test/lit.cfg +++ /dev/null @@ -1,66 +0,0 @@ -# -*- Python -*- - -# Configuration file for the 'lit' test runner. - -import os - -# name: The name of this test suite. -config.name = 'LLVM' - -# testFormat: The test format to use to interpret tests. -config.test_format = lit.formats.ShTest() - -# suffixes: A list of file extensions to treat as test files, this is actually -# set by on_clone(). -config.suffixes = [ '.ll' ] - -# test_source_root: The root path where tests are located. -config.test_source_root = os.path.dirname(__file__) - -# test_exec_root: The root path where tests should be run. -llvm_obj_root = getattr(config, 'llvm_obj_root', None) -if llvm_obj_root is not None: - config.test_exec_root = os.path.join(llvm_obj_root, 'test') - -### - -import os - -# Check that the object root is known. -if config.test_exec_root is None: - # Otherwise, we haven't loaded the site specific configuration (the user is - # probably trying to run on a test file directly, and either the site - # configuration hasn't been created by the build system, or we are in an - # out-of-tree build situation). - - # Try to detect the situation where we are using an out-of-tree build by - # looking for 'llvm-config'. - # - # FIXME: I debated (i.e., wrote and threw away) adding logic to - # automagically generate the lit.site.cfg if we are in some kind of fresh - # build situation. This means knowing how to invoke the build system - # though, and I decided it was too much magic. - - llvm_config = lit.util.which('llvm-config', config.environment['PATH']) - if not llvm_config: - lit.fatal('No site specific configuration available!') - - # Get the source and object roots. - llvm_src_root = lit.util.capture(['llvm-config', '--src-root']).strip() - llvm_obj_root = lit.util.capture(['llvm-config', '--obj-root']).strip() - - # Validate that we got a tree which points to here. - this_src_root = os.path.dirname(config.test_source_root) - if os.path.realpath(llvm_src_root) != os.path.realpath(this_src_root): - lit.fatal('No site specific configuration available!') - - # Check that the site specific configuration exists. - site_cfg = os.path.join(llvm_obj_root, 'test', 'lit.site.cfg') - if not os.path.exists(site_cfg): - lit.fatal('No site specific configuration available!') - - # Okay, that worked. Notify the user of the automagic, and reconfigure. - lit.note('using out-of-tree build at %r' % llvm_obj_root) - lit.load_config(config, site_cfg) - raise SystemExit - diff --git a/utils/lit/lit/ExampleTests/LLVM.InTree/test/lit.site.cfg b/utils/lit/lit/ExampleTests/LLVM.InTree/test/lit.site.cfg deleted file mode 100644 index d45f3ac..0000000 --- a/utils/lit/lit/ExampleTests/LLVM.InTree/test/lit.site.cfg +++ /dev/null @@ -1,7 +0,0 @@ -# -*- Python -*- - -# Preserve some key paths for use by main LLVM test suite config. -config.llvm_obj_root = os.path.dirname(os.path.dirname(__file__)) - -# Let the main config do the real work. -lit.load_config(config, os.path.join(config.llvm_obj_root, 'test/lit.cfg')) diff --git a/utils/lit/lit/ExampleTests/LLVM.OutOfTree/lit.local.cfg b/utils/lit/lit/ExampleTests/LLVM.OutOfTree/lit.local.cfg deleted file mode 100644 index 80d0c7e..0000000 --- a/utils/lit/lit/ExampleTests/LLVM.OutOfTree/lit.local.cfg +++ /dev/null @@ -1 +0,0 @@ -config.excludes = ['src'] diff --git a/utils/lit/lit/ExampleTests/LLVM.OutOfTree/obj/test/Foo/lit.local.cfg b/utils/lit/lit/ExampleTests/LLVM.OutOfTree/obj/test/Foo/lit.local.cfg deleted file mode 100644 index e69de29..0000000 --- a/utils/lit/lit/ExampleTests/LLVM.OutOfTree/obj/test/Foo/lit.local.cfg +++ /dev/null diff --git a/utils/lit/lit/ExampleTests/LLVM.OutOfTree/obj/test/lit.site.cfg b/utils/lit/lit/ExampleTests/LLVM.OutOfTree/obj/test/lit.site.cfg deleted file mode 100644 index 94a02d8..0000000 --- a/utils/lit/lit/ExampleTests/LLVM.OutOfTree/obj/test/lit.site.cfg +++ /dev/null @@ -1,8 +0,0 @@ -# -*- Python -*- - -# Preserve some key paths for use by main LLVM test suite config. -config.llvm_obj_root = os.path.dirname(os.path.dirname(__file__)) - -# Let the main config do the real work. -lit.load_config(config, os.path.join(config.llvm_obj_root, - '../src/test/lit.cfg')) diff --git a/utils/lit/lit/ExampleTests/LLVM.OutOfTree/src/test/Foo/data.txt b/utils/lit/lit/ExampleTests/LLVM.OutOfTree/src/test/Foo/data.txt deleted file mode 100644 index 45b983b..0000000 --- a/utils/lit/lit/ExampleTests/LLVM.OutOfTree/src/test/Foo/data.txt +++ /dev/null @@ -1 +0,0 @@ -hi diff --git a/utils/lit/lit/ExampleTests/LLVM.OutOfTree/src/test/Foo/pct-S.ll b/utils/lit/lit/ExampleTests/LLVM.OutOfTree/src/test/Foo/pct-S.ll deleted file mode 100644 index 3ff3633..0000000 --- a/utils/lit/lit/ExampleTests/LLVM.OutOfTree/src/test/Foo/pct-S.ll +++ /dev/null @@ -1 +0,0 @@ -; RUN: grep "hi" %S/data.txt diff --git a/utils/lit/lit/ExampleTests/LLVM.OutOfTree/src/test/lit.cfg b/utils/lit/lit/ExampleTests/LLVM.OutOfTree/src/test/lit.cfg deleted file mode 100644 index 533c445..0000000 --- a/utils/lit/lit/ExampleTests/LLVM.OutOfTree/src/test/lit.cfg +++ /dev/null @@ -1,66 +0,0 @@ -# -*- Python -*- - -# Configuration file for the 'lit' test runner. - -import os - -# name: The name of this test suite. -config.name = 'LLVM' - -# testFormat: The test format to use to interpret tests. -config.test_format = lit.formats.ShTest() - -# suffixes: A list of file extensions to treat as test files, this is actually -# set by on_clone(). -config.suffixes = [ '.ll' ] - -# test_source_root: The root path where tests are located. -config.test_source_root = os.path.dirname(__file__) - -# test_exec_root: The root path where tests should be run. -llvm_obj_root = getattr(config, 'llvm_obj_root', None) -if llvm_obj_root is not None: - config.test_exec_root = os.path.join(llvm_obj_root, 'test') - -### - -import os - -# Check that the object root is known. -if config.test_exec_root is None: - # Otherwise, we haven't loaded the site specific configuration (the user is - # probably trying to run on a test file directly, and either the site - # configuration hasn't been created by the build system, or we are in an - # out-of-tree build situation). - - # Try to detect the situation where we are using an out-of-tree build by - # looking for 'llvm-config'. - # - # FIXME: I debated (i.e., wrote and threw away) adding logic to - # automagically generate the lit.site.cfg if we are in some kind of fresh - # build situation. This means knowing how to invoke the build system - # though, and I decided it was too much magic. - - llvm_config = lit.util.which('llvm-config', config.environment['PATH']) - if not llvm_config: - lit.fatal('No site specific configuration available!') - - # Get the source and object roots. - llvm_src_root = lit.util.capture(['llvm-config', '--src-root']).strip() - llvm_obj_root = lit.util.capture(['llvm-config', '--obj-root']).strip() - - # Validate that we got a tree which points to here. - this_src_root = os.path.dirname(config.test_source_root) - if os.path.realpath(llvm_src_root) != os.path.realpath(this_src_root): - lit.fatal('No site specific configuration available!') - - # Check that the site specific configuration exists. - site_cfg = os.path.join(llvm_obj_root, 'test', 'lit.site.cfg') - if not os.path.exists(site_cfg): - lit.fatal('No site specific configuration available!') - - # Okay, that worked. Notify the user of the automagic, and reconfigure. - lit.note('using out-of-tree build at %r' % llvm_obj_root) - lit.load_config(config, site_cfg) - raise SystemExit - diff --git a/utils/lit/lit/ExampleTests/ShExternal/lit.local.cfg b/utils/lit/lit/ExampleTests/ShExternal/lit.local.cfg deleted file mode 100644 index 1061da6..0000000 --- a/utils/lit/lit/ExampleTests/ShExternal/lit.local.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# -*- Python -*- - -config.test_format = lit.formats.ShTest(execute_external = True) - -config.suffixes = ['.c'] - diff --git a/utils/lit/lit/ExampleTests/ShInternal/lit.local.cfg b/utils/lit/lit/ExampleTests/ShInternal/lit.local.cfg deleted file mode 100644 index 448eaa4..0000000 --- a/utils/lit/lit/ExampleTests/ShInternal/lit.local.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# -*- Python -*- - -config.test_format = lit.formats.ShTest(execute_external = False) - -config.suffixes = ['.c'] - diff --git a/utils/lit/lit/ExampleTests/fail.c b/utils/lit/lit/ExampleTests/fail.c deleted file mode 100644 index 84db41a..0000000 --- a/utils/lit/lit/ExampleTests/fail.c +++ /dev/null @@ -1,2 +0,0 @@ -// RUN: echo 'I am some stdout' -// RUN: false diff --git a/utils/lit/lit/ExampleTests/lit.cfg b/utils/lit/lit/ExampleTests/lit.cfg deleted file mode 100644 index 164daba..0000000 --- a/utils/lit/lit/ExampleTests/lit.cfg +++ /dev/null @@ -1,26 +0,0 @@ -# -*- Python -*- - -# Configuration file for the 'lit' test runner. - -# name: The name of this test suite. -config.name = 'Examples' - -# suffixes: A list of file extensions to treat as test files. -config.suffixes = ['.c', '.cpp', '.m', '.mm', '.ll'] - -# testFormat: The test format to use to interpret tests. -config.test_format = lit.formats.ShTest() - -# test_source_root: The path where tests are located (default is the test suite -# root). -config.test_source_root = None - -# test_exec_root: The path where tests are located (default is the test suite -# root). -config.test_exec_root = None - -# target_triple: Used by ShTest format for XFAIL checks. -config.target_triple = 'foo' - -# available_features: Used by ShTest format for REQUIRES checks. -config.available_features.add('some-feature-name') diff --git a/utils/lit/lit/ExampleTests/pass.c b/utils/lit/lit/ExampleTests/pass.c deleted file mode 100644 index 5c1031c..0000000 --- a/utils/lit/lit/ExampleTests/pass.c +++ /dev/null @@ -1 +0,0 @@ -// RUN: true diff --git a/utils/lit/lit/ExampleTests/required-and-missing.c b/utils/lit/lit/ExampleTests/required-and-missing.c deleted file mode 100644 index 47ba72e..0000000 --- a/utils/lit/lit/ExampleTests/required-and-missing.c +++ /dev/null @@ -1,4 +0,0 @@ -// This test shouldn't be run, the required feature is missing. -// -// RUN: false -// REQUIRES: some-missing-feature-name diff --git a/utils/lit/lit/ExampleTests/required-and-present.c b/utils/lit/lit/ExampleTests/required-and-present.c deleted file mode 100644 index 2a09e08..0000000 --- a/utils/lit/lit/ExampleTests/required-and-present.c +++ /dev/null @@ -1,2 +0,0 @@ -// RUN: true -// REQUIRES: some-feature-name diff --git a/utils/lit/lit/ExampleTests/vg-fail.c b/utils/lit/lit/ExampleTests/vg-fail.c deleted file mode 100644 index e3339ff..0000000 --- a/utils/lit/lit/ExampleTests/vg-fail.c +++ /dev/null @@ -1,4 +0,0 @@ -// This test should XPASS, when run without valgrind. - -// RUN: true -// XFAIL: valgrind diff --git a/utils/lit/lit/ExampleTests/xfail-feature.c b/utils/lit/lit/ExampleTests/xfail-feature.c deleted file mode 100644 index 3444bf8..0000000 --- a/utils/lit/lit/ExampleTests/xfail-feature.c +++ /dev/null @@ -1,4 +0,0 @@ -// This test should XPASS. - -// RUN: true -// XFAIL: some-feature-name diff --git a/utils/lit/lit/ExampleTests/xfail.c b/utils/lit/lit/ExampleTests/xfail.c deleted file mode 100644 index b36cd99..0000000 --- a/utils/lit/lit/ExampleTests/xfail.c +++ /dev/null @@ -1,2 +0,0 @@ -// RUN: false -// XFAIL: * diff --git a/utils/lit/lit/ExampleTests/xpass.c b/utils/lit/lit/ExampleTests/xpass.c deleted file mode 100644 index ad84990..0000000 --- a/utils/lit/lit/ExampleTests/xpass.c +++ /dev/null @@ -1,2 +0,0 @@ -// RUN: true -// XFAIL diff --git a/utils/lit/lit/LitConfig.py b/utils/lit/lit/LitConfig.py index bd7a603..b0dde5d 100644 --- a/utils/lit/lit/LitConfig.py +++ b/utils/lit/lit/LitConfig.py @@ -1,10 +1,12 @@ from __future__ import absolute_import +import inspect import os +import sys import lit.Test -import lit.TestFormats +import lit.formats import lit.TestingConfig -import lit.Util +import lit.util class LitConfig: """LitConfig - Configuration data for a 'lit' test runner instance, shared @@ -16,15 +18,6 @@ class LitConfig: easily. """ - # Provide access to Test module. - Test = lit.Test - - # Provide access to built-in formats. - formats = lit.TestFormats - - # Provide access to built-in utility functions. - util = lit.Util - def __init__(self, progname, path, quiet, useValgrind, valgrindLeakCheck, valgrindArgs, noExecute, debug, isWindows, @@ -32,7 +25,7 @@ class LitConfig: # The name of the test runner. self.progname = progname # The items to add to the PATH environment variable. - self.path = list(map(str, path)) + self.path = [str(p) for p in path] self.quiet = bool(quiet) self.useValgrind = bool(useValgrind) self.valgrindLeakCheck = bool(valgrindLeakCheck) @@ -70,23 +63,17 @@ class LitConfig: path.""" if self.debug: self.note('load_config from %r' % path) - return lit.TestingConfig.TestingConfig.frompath( - path, config.parent, self, mustExist = True, config = config) + config.load_from_path(path, self) + return config def getBashPath(self): """getBashPath - Get the path to 'bash'""" - import os - if self.bashPath is not None: return self.bashPath - self.bashPath = lit.Util.which('bash', os.pathsep.join(self.path)) + self.bashPath = lit.util.which('bash', os.pathsep.join(self.path)) if self.bashPath is None: - # Check some known paths. - for path in ('/bin/bash', '/usr/bin/bash', '/usr/local/bin/bash'): - if os.path.exists(path): - self.bashPath = path - break + self.bashPath = lit.util.which('bash') if self.bashPath is None: self.warning("Unable to find 'bash'.") @@ -96,13 +83,13 @@ class LitConfig: def getToolsPath(self, dir, paths, tools): if dir is not None and os.path.isabs(dir) and os.path.isdir(dir): - if not lit.Util.checkToolsPath(dir, tools): + if not lit.util.checkToolsPath(dir, tools): return None else: - dir = lit.Util.whichTools(tools, paths) + dir = lit.util.whichTools(tools, paths) # bash - self.bashPath = lit.Util.which('bash', dir) + self.bashPath = lit.util.which('bash', dir) if self.bashPath is None: self.note("Unable to find 'bash.exe'.") self.bashPath = '' @@ -110,8 +97,6 @@ class LitConfig: return dir def _write_message(self, kind, message): - import inspect, os, sys - # Get the file/line where this message was generated. f = inspect.currentframe() # Step out of _write_message, and then out of wrapper. @@ -134,6 +119,5 @@ class LitConfig: self.numErrors += 1 def fatal(self, message): - import sys self._write_message('fatal', message) sys.exit(2) diff --git a/utils/lit/lit/LitTestCase.py b/utils/lit/lit/LitTestCase.py index 8951185..e04846c 100644 --- a/utils/lit/lit/LitTestCase.py +++ b/utils/lit/lit/LitTestCase.py @@ -1,5 +1,7 @@ +from __future__ import absolute_import import unittest -import Test + +import lit.Test """ TestCase adaptor for providing a 'unittest' compatible interface to 'lit' tests. @@ -9,10 +11,10 @@ class UnresolvedError(RuntimeError): pass class LitTestCase(unittest.TestCase): - def __init__(self, test, lit_config): + def __init__(self, test, run): unittest.TestCase.__init__(self) self._test = test - self._lit_config = lit_config + self._run = run def id(self): return self._test.getFullName() @@ -21,10 +23,12 @@ class LitTestCase(unittest.TestCase): return self._test.getFullName() def runTest(self): - tr, output = self._test.config.test_format.execute( - self._test, self._lit_config) + # Run the test. + self._run.execute_test(self._test) - if tr is Test.UNRESOLVED: - raise UnresolvedError(output) - elif tr.isFailure: - self.fail(output) + # Adapt the result to unittest. + result = self._test.result + if result.code is lit.Test.UNRESOLVED: + raise UnresolvedError(result.output) + elif result.code.isFailure: + self.fail(result.output) diff --git a/utils/lit/lit/ProgressBar.py b/utils/lit/lit/ProgressBar.py index 0454ba2..e3644f1 100644 --- a/utils/lit/lit/ProgressBar.py +++ b/utils/lit/lit/ProgressBar.py @@ -5,6 +5,10 @@ import sys, re, time +def to_bytes(str): + # Encode to Latin1 to get binary data. + return str.encode('ISO-8859-1') + class TerminalController: """ A class that can be used to portably generate formatted output to @@ -116,26 +120,34 @@ class TerminalController: set_fg = self._tigetstr('setf') if set_fg: for i,color in zip(range(len(self._COLORS)), self._COLORS): - setattr(self, color, curses.tparm(set_fg, i) or '') + setattr(self, color, self._tparm(set_fg, i)) set_fg_ansi = self._tigetstr('setaf') if set_fg_ansi: for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS): - setattr(self, color, curses.tparm(set_fg_ansi, i) or '') + setattr(self, color, self._tparm(set_fg_ansi, i)) set_bg = self._tigetstr('setb') if set_bg: for i,color in zip(range(len(self._COLORS)), self._COLORS): - setattr(self, 'BG_'+color, curses.tparm(set_bg, i) or '') + setattr(self, 'BG_'+color, self._tparm(set_bg, i)) set_bg_ansi = self._tigetstr('setab') if set_bg_ansi: for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS): - setattr(self, 'BG_'+color, curses.tparm(set_bg_ansi, i) or '') + setattr(self, 'BG_'+color, self._tparm(set_bg_ansi, i)) + + def _tparm(self, arg, index): + import curses + return curses.tparm(to_bytes(arg), index).decode('ascii') or '' def _tigetstr(self, cap_name): # String capabilities can include "delays" of the form "$<2>". # For any modern terminal, we should be able to just ignore # these, so strip them out. import curses - cap = curses.tigetstr(cap_name) or '' + cap = curses.tigetstr(cap_name) + if cap is None: + cap = '' + else: + cap = cap.decode('ascii') return re.sub(r'\$<\d+>[/*]?', '', cap) def render(self, template): @@ -269,7 +281,6 @@ class ProgressBar: self.cleared = 1 def test(): - import time tc = TerminalController() p = ProgressBar(tc, 'Tests') for i in range(101): diff --git a/utils/lit/lit/ShUtil.py b/utils/lit/lit/ShUtil.py index fb0689b..1945ba7 100644 --- a/utils/lit/lit/ShUtil.py +++ b/utils/lit/lit/ShUtil.py @@ -1,7 +1,7 @@ from __future__ import absolute_import import itertools -import lit.Util +import lit.util from lit.ShCommands import Command, Pipeline, Seq class ShLexer: @@ -75,7 +75,7 @@ class ShLexer: # Outside of a string, '\\' escapes everything. self.eat() if self.pos == self.end: - lit.Util.warning( + lit.util.warning( "escape at end of quoted argument in: %r" % self.data) return str str += self.eat() @@ -93,7 +93,7 @@ class ShLexer: # Inside a '"' quoted string, '\\' only escapes the quote # character and backslash, otherwise it is preserved. if self.pos == self.end: - lit.Util.warning( + lit.util.warning( "escape at end of quoted argument in: %r" % self.data) return str c = self.eat() @@ -105,7 +105,7 @@ class ShLexer: str += '\\' + c else: str += c - lit.Util.warning("missing quote character in %r" % self.data) + lit.util.warning("missing quote character in %r" % self.data) return str def lex_arg_checked(self, c): diff --git a/utils/lit/lit/Test.py b/utils/lit/lit/Test.py index 9471e3a..b4988f5 100644 --- a/utils/lit/lit/Test.py +++ b/utils/lit/lit/Test.py @@ -1,8 +1,21 @@ import os -# Test results. +# Test result codes. + +class ResultCode(object): + """Test result codes.""" + + # We override __new__ and __getnewargs__ to ensure that pickling still + # provides unique ResultCode objects in any particular instance. + _instances = {} + def __new__(cls, name, isFailure): + res = cls._instances.get(name) + if res is None: + cls._instances[name] = res = super(ResultCode, cls).__new__(cls) + return res + def __getnewargs__(self): + return (self.name, self.isFailure) -class TestResult: def __init__(self, name, isFailure): self.name = name self.isFailure = isFailure @@ -11,20 +24,87 @@ class TestResult: return '%s%r' % (self.__class__.__name__, (self.name, self.isFailure)) -PASS = TestResult('PASS', False) -XFAIL = TestResult('XFAIL', False) -FAIL = TestResult('FAIL', True) -XPASS = TestResult('XPASS', True) -UNRESOLVED = TestResult('UNRESOLVED', True) -UNSUPPORTED = TestResult('UNSUPPORTED', False) +PASS = ResultCode('PASS', False) +XFAIL = ResultCode('XFAIL', False) +FAIL = ResultCode('FAIL', True) +XPASS = ResultCode('XPASS', True) +UNRESOLVED = ResultCode('UNRESOLVED', True) +UNSUPPORTED = ResultCode('UNSUPPORTED', False) -# Test classes. +# Test metric values. -class TestFormat: - """TestFormat - Test information provider.""" +class MetricValue(object): + def format(self): + """ + format() -> str - def __init__(self, name): - self.name = name + Convert this metric to a string suitable for displaying as part of the + console output. + """ + raise RuntimeError("abstract method") + + def todata(self): + """ + todata() -> json-serializable data + + Convert this metric to content suitable for serializing in the JSON test + output. + """ + raise RuntimeError("abstract method") + +class IntMetricValue(MetricValue): + def __init__(self, value): + self.value = value + + def format(self): + return str(self.value) + + def todata(self): + return self.value + +class RealMetricValue(MetricValue): + def __init__(self, value): + self.value = value + + def format(self): + return '%.4f' % self.value + + def todata(self): + return self.value + +# Test results. + +class Result(object): + """Wrapper for the results of executing an individual test.""" + + def __init__(self, code, output='', elapsed=None): + # The result code. + self.code = code + # The test output. + self.output = output + # The wall timing to execute the test, if timing. + self.elapsed = elapsed + # The metrics reported by this test. + self.metrics = {} + + def addMetric(self, name, value): + """ + addMetric(name, value) + + Attach a test metric to the test result, with the given name and list of + values. It is an error to attempt to attach the metrics with the same + name multiple times. + + Each value must be an instance of a MetricValue subclass. + """ + if name in self.metrics: + raise ValueError("result already includes metrics for %r" % ( + name,)) + if not isinstance(value, MetricValue): + raise TypeError("unexpected metric value: %r" % (value,)) + self.metrics[name] = value + +# Test classes. class TestSuite: """TestSuite - Information on a group of tests. @@ -52,27 +132,28 @@ class Test: self.suite = suite self.path_in_suite = path_in_suite self.config = config - # The test result code, once complete. + # A list of conditions under which this test is expected to fail. These + # can optionally be provided by test format handlers, and will be + # honored when the test result is supplied. + self.xfails = [] + # The test result, once complete. self.result = None - # Any additional output from the test, once complete. - self.output = None - # The wall time to execute this test, if timing and once complete. - self.elapsed = None - # The repeat index of this test, or None. - self.index = None - - def copyWithIndex(self, index): - import copy - res = copy.copy(self) - res.index = index - return res - def setResult(self, result, output, elapsed): - assert self.result is None, "Test result already set!" + def setResult(self, result): + if self.result is not None: + raise ArgumentError("test result already set") + if not isinstance(result, Result): + raise ArgumentError("unexpected result type") + self.result = result - self.output = output - self.elapsed = elapsed + # Apply the XFAIL handling to resolve the result exit code. + if self.isExpectedToFail(): + if self.result.code == PASS: + self.result.code = XPASS + elif self.result.code == FAIL: + self.result.code = XFAIL + def getFullName(self): return self.suite.config.name + ' :: ' + '/'.join(self.path_in_suite) @@ -81,3 +162,29 @@ class Test: def getExecPath(self): return self.suite.getExecPath(self.path_in_suite) + + def isExpectedToFail(self): + """ + isExpectedToFail() -> bool + + Check whether this test is expected to fail in the current + configuration. This check relies on the test xfails property which by + some test formats may not be computed until the test has first been + executed. + """ + + # Check if any of the xfails match an available feature or the target. + for item in self.xfails: + # If this is the wildcard, it always fails. + if item == '*': + return True + + # If this is an exact match for one of the features, it fails. + if item in self.config.available_features: + return True + + # If this is a part of the target triple, it fails. + if item in self.suite.config.target_triple: + return True + + return False diff --git a/utils/lit/lit/TestFormats.py b/utils/lit/lit/TestFormats.py deleted file mode 100644 index 9e0c7a0..0000000 --- a/utils/lit/lit/TestFormats.py +++ /dev/null @@ -1,229 +0,0 @@ -from __future__ import absolute_import -import os -import sys - -import lit.Test -import lit.TestRunner -import lit.Util - -kIsWindows = sys.platform in ['win32', 'cygwin'] - -class GoogleTest(object): - def __init__(self, test_sub_dir, test_suffix): - self.test_sub_dir = os.path.normcase(str(test_sub_dir)).split(';') - self.test_suffix = str(test_suffix) - - # On Windows, assume tests will also end in '.exe'. - if kIsWindows: - self.test_suffix += '.exe' - - def getGTestTests(self, path, litConfig, localConfig): - """getGTestTests(path) - [name] - - Return the tests available in gtest executable. - - Args: - path: String path to a gtest executable - litConfig: LitConfig instance - localConfig: TestingConfig instance""" - - try: - lines = lit.Util.capture([path, '--gtest_list_tests'], - env=localConfig.environment) - if kIsWindows: - lines = lines.replace('\r', '') - lines = lines.split('\n') - except: - litConfig.error("unable to discover google-tests in %r" % path) - raise StopIteration - - nested_tests = [] - for ln in lines: - if not ln.strip(): - continue - - prefix = '' - index = 0 - while ln[index*2:index*2+2] == ' ': - index += 1 - while len(nested_tests) > index: - nested_tests.pop() - - ln = ln[index*2:] - if ln.endswith('.'): - nested_tests.append(ln) - else: - yield ''.join(nested_tests) + ln - - # Note: path_in_suite should not include the executable name. - def getTestsInExecutable(self, testSuite, path_in_suite, execpath, - litConfig, localConfig): - if not execpath.endswith(self.test_suffix): - return - (dirname, basename) = os.path.split(execpath) - # Discover the tests in this executable. - for testname in self.getGTestTests(execpath, litConfig, localConfig): - testPath = path_in_suite + (basename, testname) - yield lit.Test.Test(testSuite, testPath, localConfig) - - def getTestsInDirectory(self, testSuite, path_in_suite, - litConfig, localConfig): - source_path = testSuite.getSourcePath(path_in_suite) - for filename in os.listdir(source_path): - filepath = os.path.join(source_path, filename) - if os.path.isdir(filepath): - # Iterate over executables in a directory. - if not os.path.normcase(filename) in self.test_sub_dir: - continue - dirpath_in_suite = path_in_suite + (filename, ) - for subfilename in os.listdir(filepath): - execpath = os.path.join(filepath, subfilename) - for test in self.getTestsInExecutable( - testSuite, dirpath_in_suite, execpath, - litConfig, localConfig): - yield test - elif ('.' in self.test_sub_dir): - for test in self.getTestsInExecutable( - testSuite, path_in_suite, filepath, - litConfig, localConfig): - yield test - - def execute(self, test, litConfig): - testPath,testName = os.path.split(test.getSourcePath()) - while not os.path.exists(testPath): - # Handle GTest parametrized and typed tests, whose name includes - # some '/'s. - testPath, namePrefix = os.path.split(testPath) - testName = os.path.join(namePrefix, testName) - - cmd = [testPath, '--gtest_filter=' + testName] - if litConfig.useValgrind: - cmd = litConfig.valgrindArgs + cmd - - if litConfig.noExecute: - return lit.Test.PASS, '' - - out, err, exitCode = lit.TestRunner.executeCommand( - cmd, env=test.config.environment) - - if not exitCode: - return lit.Test.PASS,'' - - return lit.Test.FAIL, out + err - -### - -class FileBasedTest(object): - def getTestsInDirectory(self, testSuite, path_in_suite, - litConfig, localConfig): - source_path = testSuite.getSourcePath(path_in_suite) - for filename in os.listdir(source_path): - # Ignore dot files and excluded tests. - if (filename.startswith('.') or - filename in localConfig.excludes): - continue - - filepath = os.path.join(source_path, filename) - if not os.path.isdir(filepath): - base,ext = os.path.splitext(filename) - if ext in localConfig.suffixes: - yield lit.Test.Test(testSuite, path_in_suite + (filename,), - localConfig) - -class ShTest(FileBasedTest): - def __init__(self, execute_external = False): - self.execute_external = execute_external - - def execute(self, test, litConfig): - return lit.TestRunner.executeShTest(test, litConfig, - self.execute_external) - -### - -import re -import tempfile - -class OneCommandPerFileTest: - # FIXME: Refactor into generic test for running some command on a directory - # of inputs. - - def __init__(self, command, dir, recursive=False, - pattern=".*", useTempInput=False): - if isinstance(command, str): - self.command = [command] - else: - self.command = list(command) - if dir is not None: - dir = str(dir) - self.dir = dir - self.recursive = bool(recursive) - self.pattern = re.compile(pattern) - self.useTempInput = useTempInput - - def getTestsInDirectory(self, testSuite, path_in_suite, - litConfig, localConfig): - dir = self.dir - if dir is None: - dir = testSuite.getSourcePath(path_in_suite) - - for dirname,subdirs,filenames in os.walk(dir): - if not self.recursive: - subdirs[:] = [] - - subdirs[:] = [d for d in subdirs - if (d != '.svn' and - d not in localConfig.excludes)] - - for filename in filenames: - if (filename.startswith('.') or - not self.pattern.match(filename) or - filename in localConfig.excludes): - continue - - path = os.path.join(dirname,filename) - suffix = path[len(dir):] - if suffix.startswith(os.sep): - suffix = suffix[1:] - test = lit.Test.Test( - testSuite, path_in_suite + tuple(suffix.split(os.sep)), - localConfig) - # FIXME: Hack? - test.source_path = path - yield test - - def createTempInput(self, tmp, test): - abstract - - def execute(self, test, litConfig): - if test.config.unsupported: - return (lit.Test.UNSUPPORTED, 'Test is unsupported') - - cmd = list(self.command) - - # If using temp input, create a temporary file and hand it to the - # subclass. - if self.useTempInput: - tmp = tempfile.NamedTemporaryFile(suffix='.cpp') - self.createTempInput(tmp, test) - tmp.flush() - cmd.append(tmp.name) - elif hasattr(test, 'source_path'): - cmd.append(test.source_path) - else: - cmd.append(test.getSourcePath()) - - out, err, exitCode = lit.TestRunner.executeCommand(cmd) - - diags = out + err - if not exitCode and not diags.strip(): - return lit.Test.PASS,'' - - # Try to include some useful information. - report = """Command: %s\n""" % ' '.join(["'%s'" % a - for a in cmd]) - if self.useTempInput: - report += """Temporary File: %s\n""" % tmp.name - report += "--\n%s--\n""" % open(tmp.name).read() - report += """Output:\n--\n%s--""" % diags - - return lit.Test.FAIL, report diff --git a/utils/lit/lit/TestRunner.py b/utils/lit/lit/TestRunner.py index 989a992..9752417 100644 --- a/utils/lit/lit/TestRunner.py +++ b/utils/lit/lit/TestRunner.py @@ -3,14 +3,10 @@ import os, signal, subprocess, sys import re import platform import tempfile -try: - from io import StringIO -except ImportError: - from StringIO import StringIO import lit.ShUtil as ShUtil import lit.Test as Test -import lit.Util as Util +import lit.util class InternalShellError(Exception): def __init__(self, command, message): @@ -25,25 +21,6 @@ kUseCloseFDs = not kIsWindows # Use temporary files to replace /dev/null on Windows. kAvoidDevNull = kIsWindows -def executeCommand(command, cwd=None, env=None): - # Close extra file handles on UNIX (on Windows this cannot be done while - # also redirecting input). - close_fds = not kIsWindows - - p = subprocess.Popen(command, cwd=cwd, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - env=env, close_fds=close_fds) - out,err = p.communicate() - exitCode = p.wait() - - # Detect Ctrl-C in subprocess. - if exitCode == -signal.SIGINT: - raise KeyboardInterrupt - - return out, err, exitCode - def executeShCmd(cmd, cfg, cwd, results): if isinstance(cmd, ShUtil.Seq): if cmd.op == ';': @@ -154,8 +131,8 @@ def executeShCmd(cmd, cfg, cwd, results): # Resolve the executable path ourselves. args = list(j.args) - args[0] = Util.which(args[0], cfg.environment['PATH']) - if not args[0]: + executable = lit.util.which(args[0], cfg.environment['PATH']) + if not executable: raise InternalShellError(j, '%r: command not found' % j.args[0]) # Replace uses of /dev/null with temporary files. @@ -168,6 +145,7 @@ def executeShCmd(cmd, cfg, cwd, results): args[i] = f.name procs.append(subprocess.Popen(args, cwd=cwd, + executable = executable, stdin = stdin, stdout = stdout, stderr = stderr, @@ -221,10 +199,22 @@ def executeShCmd(cmd, cfg, cwd, results): if res == -signal.SIGINT: raise KeyboardInterrupt + # Ensure the resulting output is always of string type. + try: + out = str(out.decode('ascii')) + except: + out = str(out) + try: + err = str(err.decode('ascii')) + except: + err = str(err) + results.append((cmd.commands[i], out, err, res)) if cmd.pipe_err: # Python treats the exit code as a signed char. - if res < 0: + if exitCode is None: + exitCode = res + elif res < 0: exitCode = min(exitCode, res) else: exitCode = max(exitCode, res) @@ -250,7 +240,7 @@ def executeScriptInternal(test, litConfig, tmpBase, commands, cwd): cmds.append(ShUtil.ShParser(ln, litConfig.isWindows, test.config.pipefail).parse()) except: - return (Test.FAIL, "shell parser error on: %r" % ln) + return lit.Test.Result(Test.FAIL, "shell parser error on: %r" % ln) cmd = cmds[0] for c in cmds[1:]: @@ -306,24 +296,60 @@ def executeScript(test, litConfig, tmpBase, commands, cwd): # run on clang with no real loss. command = litConfig.valgrindArgs + command - return executeCommand(command, cwd=cwd, env=test.config.environment) + return lit.util.executeCommand(command, cwd=cwd, + env=test.config.environment) -def isExpectedFail(test, xfails): - # Check if any of the xfails match an available feature or the target. - for item in xfails: - # If this is the wildcard, it always fails. - if item == '*': - return True - - # If this is an exact match for one of the features, it fails. - if item in test.config.available_features: - return True +def parseIntegratedTestScriptCommands(source_path): + """ + parseIntegratedTestScriptCommands(source_path) -> commands - # If this is a part of the target triple, it fails. - if item in test.suite.config.target_triple: - return True + Parse the commands in an integrated test script file into a list of + (line_number, command_type, line). + """ - return False + # This code is carefully written to be dual compatible with Python 2.5+ and + # Python 3 without requiring input files to always have valid codings. The + # trick we use is to open the file in binary mode and use the regular + # expression library to find the commands, with it scanning strings in + # Python2 and bytes in Python3. + # + # Once we find a match, we do require each script line to be decodable to + # ascii, so we convert the outputs to ascii before returning. This way the + # remaining code can work with "strings" agnostic of the executing Python + # version. + + def to_bytes(str): + # Encode to Latin1 to get binary data. + return str.encode('ISO-8859-1') + keywords = ('RUN:', 'XFAIL:', 'REQUIRES:', 'END.') + keywords_re = re.compile( + to_bytes("(%s)(.*)\n" % ("|".join(k for k in keywords),))) + + f = open(source_path, 'rb') + try: + # Read the entire file contents. + data = f.read() + + # Iterate over the matches. + line_number = 1 + last_match_position = 0 + for match in keywords_re.finditer(data): + # Compute the updated line number by counting the intervening + # newlines. + match_position = match.start() + line_number += data.count(to_bytes('\n'), last_match_position, + match_position) + last_match_position = match_position + + # Convert the keyword and line to ascii strings and yield the + # command. Note that we take care to return regular strings in + # Python 2, to avoid other code having to differentiate between the + # str and unicode types. + keyword,ln = match.groups() + yield (line_number, str(keyword[:-1].decode('ascii')), + str(ln.decode('ascii'))) + finally: + f.close() def parseIntegratedTestScript(test, normalize_slashes=False, extra_substitutions=[]): @@ -342,8 +368,6 @@ def parseIntegratedTestScript(test, normalize_slashes=False, execdir,execbase = os.path.split(execpath) tmpDir = os.path.join(execdir, 'Output') tmpBase = os.path.join(tmpDir, execbase) - if test.index is not None: - tmpBase += '_%d' % test.index # Normalize slashes, if requested. if normalize_slashes: @@ -364,18 +388,21 @@ def parseIntegratedTestScript(test, normalize_slashes=False, ('%T', tmpDir), ('#_MARKER_#', '%')]) + # "%/[STpst]" should be normalized. + substitutions.extend([ + ('%/s', sourcepath.replace('\\', '/')), + ('%/S', sourcedir.replace('\\', '/')), + ('%/p', sourcedir.replace('\\', '/')), + ('%/t', tmpBase.replace('\\', '/') + '.tmp'), + ('%/T', tmpDir.replace('\\', '/')), + ]) + # Collect the test lines from the script. script = [] - xfails = [] requires = [] - line_number = 0 - for ln in open(sourcepath): - line_number += 1 - if 'RUN:' in ln: - # Isolate the command to run. - index = ln.index('RUN:') - ln = ln[index+4:] - + for line_number, command_type, ln in \ + parseIntegratedTestScriptCommands(sourcepath): + if command_type == 'RUN': # Trim trailing whitespace. ln = ln.rstrip() @@ -393,16 +420,17 @@ def parseIntegratedTestScript(test, normalize_slashes=False, script[-1] = script[-1][:-1] + ln else: script.append(ln) - elif 'XFAIL:' in ln: - items = ln[ln.index('XFAIL:') + 6:].split(',') - xfails.extend([s.strip() for s in items]) - elif 'REQUIRES:' in ln: - items = ln[ln.index('REQUIRES:') + 9:].split(',') - requires.extend([s.strip() for s in items]) - elif 'END.' in ln: - # Check for END. lines. - if ln[ln.index('END.'):].strip() == 'END.': + elif command_type == 'XFAIL': + test.xfails.extend([s.strip() for s in ln.split(',')]) + elif command_type == 'REQUIRES': + requires.extend([s.strip() for s in ln.split(',')]) + elif command_type == 'END': + # END commands are only honored if the rest of the line is empty. + if not ln.strip(): break + else: + raise ValueError("unknown script command type: %r" % ( + command_type,)) # Apply substitutions to the script. Allow full regular # expression syntax. Replace each matching occurrence of regular @@ -416,45 +444,27 @@ def parseIntegratedTestScript(test, normalize_slashes=False, # Strip the trailing newline and any extra whitespace. return ln.strip() - script = map(processLine, script) + script = [processLine(ln) + for ln in script] # Verify the script contains a run line. if not script: - return (Test.UNRESOLVED, "Test has no run line!") + return lit.Test.Result(Test.UNRESOLVED, "Test has no run line!") # Check for unterminated run lines. if script[-1][-1] == '\\': - return (Test.UNRESOLVED, "Test has unterminated run lines (with '\\')") + return lit.Test.Result(Test.UNRESOLVED, + "Test has unterminated run lines (with '\\')") # Check that we have the required features: missing_required_features = [f for f in requires if f not in test.config.available_features] if missing_required_features: msg = ', '.join(missing_required_features) - return (Test.UNSUPPORTED, - "Test requires the following features: %s" % msg) - - isXFail = isExpectedFail(test, xfails) - return script,isXFail,tmpBase,execdir - -def formatTestOutput(status, out, err, exitCode, script): - output = StringIO() - output.write(u"Script:\n") - output.write(u"--\n") - output.write(u'\n'.join(script)) - output.write(u"\n--\n") - output.write(u"Exit Code: %r\n\n" % exitCode) - if out: - output.write(u"Command Output (stdout):\n") - output.write(u"--\n") - output.write(unicode(out)) - output.write(u"--\n") - if err: - output.write(u"Command Output (stderr):\n") - output.write(u"--\n") - output.write(unicode(err)) - output.write(u"--\n") - return (status, output.getvalue()) + return lit.Test.Result(Test.UNSUPPORTED, + "Test requires the following features: %s" % msg) + + return script,tmpBase,execdir def executeShTest(test, litConfig, useExternalSh, extra_substitutions=[]): @@ -462,39 +472,37 @@ def executeShTest(test, litConfig, useExternalSh, return (Test.UNSUPPORTED, 'Test is unsupported') res = parseIntegratedTestScript(test, useExternalSh, extra_substitutions) - if len(res) == 2: + if isinstance(res, lit.Test.Result): return res - - script, isXFail, tmpBase, execdir = res - if litConfig.noExecute: - return (Test.PASS, '') + return lit.Test.Result(Test.PASS) + + script, tmpBase, execdir = res # Create the output directory if it does not already exist. - Util.mkdir_p(os.path.dirname(tmpBase)) + lit.util.mkdir_p(os.path.dirname(tmpBase)) if useExternalSh: res = executeScript(test, litConfig, tmpBase, script, execdir) else: res = executeScriptInternal(test, litConfig, tmpBase, script, execdir) - if len(res) == 2: + if isinstance(res, lit.Test.Result): return res out,err,exitCode = res - if isXFail: - ok = exitCode != 0 - if ok: - status = Test.XFAIL - else: - status = Test.XPASS + if exitCode == 0: + status = Test.PASS else: - ok = exitCode == 0 - if ok: - status = Test.PASS - else: - status = Test.FAIL + status = Test.FAIL - if ok: - return (status,'') + # Form the output log. + output = """Script:\n--\n%s\n--\nExit Code: %d\n\n""" % ( + '\n'.join(script), exitCode) + + # Append the outputs, if present. + if out: + output += """Command Output (stdout):\n--\n%s\n--\n""" % (out,) + if err: + output += """Command Output (stderr):\n--\n%s\n--\n""" % (err,) - return formatTestOutput(status, out, err, exitCode, script) + return lit.Test.Result(status, output) diff --git a/utils/lit/lit/TestingConfig.py b/utils/lit/lit/TestingConfig.py index f4ff89f..4a34b77 100644 --- a/utils/lit/lit/TestingConfig.py +++ b/utils/lit/lit/TestingConfig.py @@ -9,83 +9,106 @@ class TestingConfig: """ @staticmethod - def frompath(path, parent, litConfig, mustExist, config = None): - if config is None: - # Set the environment based on the command line arguments. - environment = { - 'LIBRARY_PATH' : os.environ.get('LIBRARY_PATH',''), - 'LD_LIBRARY_PATH' : os.environ.get('LD_LIBRARY_PATH',''), - 'PATH' : os.pathsep.join(litConfig.path + - [os.environ.get('PATH','')]), - 'SYSTEMROOT' : os.environ.get('SYSTEMROOT',''), - 'TERM' : os.environ.get('TERM',''), - 'LLVM_DISABLE_CRASH_REPORT' : '1', - } - - if sys.platform == 'win32': - environment.update({ - 'INCLUDE' : os.environ.get('INCLUDE',''), - 'PATHEXT' : os.environ.get('PATHEXT',''), - 'PYTHONUNBUFFERED' : '1', - 'TEMP' : os.environ.get('TEMP',''), - 'TMP' : os.environ.get('TMP',''), - }) - - # Set the default available features based on the LitConfig. - available_features = [] - if litConfig.useValgrind: - available_features.append('valgrind') - if litConfig.valgrindLeakCheck: - available_features.append('vg_leak') - - config = TestingConfig(parent, - name = '<unnamed>', - suffixes = set(), - test_format = None, - environment = environment, - substitutions = [], - unsupported = False, - on_clone = None, - test_exec_root = None, - test_source_root = None, - excludes = [], - available_features = available_features, - pipefail = True) - - if os.path.exists(path): - # FIXME: Improve detection and error reporting of errors in the - # config file. - f = open(path) - cfg_globals = dict(globals()) - cfg_globals['config'] = config - cfg_globals['lit'] = litConfig - cfg_globals['__file__'] = path - try: - data = f.read() - if PY2: - exec("exec data in cfg_globals") - else: - exec(data, cfg_globals) - if litConfig.debug: - litConfig.note('... loaded config %r' % path) - except SystemExit: - e = sys.exc_info()[1] - # We allow normal system exit inside a config file to just - # return control without error. - if e.args: - raise - f.close() - else: - if mustExist: - litConfig.fatal('unable to load config from %r ' % path) - elif litConfig.debug: - litConfig.note('... config not found - %r' %path) + def fromdefaults(litConfig): + """ + fromdefaults(litConfig) -> TestingConfig + + Create a TestingConfig object with default values. + """ + # Set the environment based on the command line arguments. + environment = { + 'LIBRARY_PATH' : os.environ.get('LIBRARY_PATH',''), + 'LD_LIBRARY_PATH' : os.environ.get('LD_LIBRARY_PATH',''), + 'PATH' : os.pathsep.join(litConfig.path + + [os.environ.get('PATH','')]), + 'SYSTEMROOT' : os.environ.get('SYSTEMROOT',''), + 'TERM' : os.environ.get('TERM',''), + 'LLVM_DISABLE_CRASH_REPORT' : '1', + } + + if sys.platform == 'win32': + environment.update({ + 'INCLUDE' : os.environ.get('INCLUDE',''), + 'PATHEXT' : os.environ.get('PATHEXT',''), + 'PYTHONUNBUFFERED' : '1', + 'TEMP' : os.environ.get('TEMP',''), + 'TMP' : os.environ.get('TMP',''), + }) + + # The option to preserve TEMP, TMP, and TMPDIR. + # This is intended to check how many temporary files would be generated + # (and be not cleaned up) in automated builders. + if os.environ.has_key('LIT_PRESERVES_TMP'): + environment.update({ + 'TEMP' : os.environ.get('TEMP',''), + 'TMP' : os.environ.get('TMP',''), + 'TMPDIR' : os.environ.get('TMPDIR',''), + }) + + # Set the default available features based on the LitConfig. + available_features = [] + if litConfig.useValgrind: + available_features.append('valgrind') + if litConfig.valgrindLeakCheck: + available_features.append('vg_leak') + + return TestingConfig(None, + name = '<unnamed>', + suffixes = set(), + test_format = None, + environment = environment, + substitutions = [], + unsupported = False, + test_exec_root = None, + test_source_root = None, + excludes = [], + available_features = available_features, + pipefail = True) - config.finish(litConfig) - return config + def load_from_path(self, path, litConfig): + """ + load_from_path(path, litConfig) + + Load the configuration module at the provided path into the given config + object. + """ + + # Load the config script data. + f = open(path) + try: + data = f.read() + except: + litConfig.fatal('unable to load config file: %r' % (path,)) + f.close() + + # Execute the config script to initialize the object. + cfg_globals = dict(globals()) + cfg_globals['config'] = self + cfg_globals['lit_config'] = litConfig + cfg_globals['__file__'] = path + try: + if PY2: + exec("exec data in cfg_globals") + else: + exec(data, cfg_globals) + if litConfig.debug: + litConfig.note('... loaded config %r' % path) + except SystemExit: + e = sys.exc_info()[1] + # We allow normal system exit inside a config file to just + # return control without error. + if e.args: + raise + except: + import traceback + litConfig.fatal( + 'unable to parse config file %r, traceback: %s' % ( + path, traceback.format_exc())) + + self.finish(litConfig) def __init__(self, parent, name, suffixes, test_format, - environment, substitutions, unsupported, on_clone, + environment, substitutions, unsupported, test_exec_root, test_source_root, excludes, available_features, pipefail): self.parent = parent @@ -95,27 +118,12 @@ class TestingConfig: self.environment = dict(environment) self.substitutions = list(substitutions) self.unsupported = unsupported - self.on_clone = on_clone self.test_exec_root = test_exec_root self.test_source_root = test_source_root self.excludes = set(excludes) self.available_features = set(available_features) self.pipefail = pipefail - def clone(self, path): - # FIXME: Chain implementations? - # - # FIXME: Allow extra parameters? - cfg = TestingConfig(self, self.name, self.suffixes, self.test_format, - self.environment, self.substitutions, - self.unsupported, self.on_clone, - self.test_exec_root, self.test_source_root, - self.excludes, self.available_features, - self.pipefail) - if cfg.on_clone: - cfg.on_clone(self, cfg, path) - return cfg - def finish(self, litConfig): """finish() - Finish this config object, after loading is complete.""" diff --git a/utils/lit/lit/__init__.py b/utils/lit/lit/__init__.py index b9f573d..3967fdd 100644 --- a/utils/lit/lit/__init__.py +++ b/utils/lit/lit/__init__.py @@ -6,6 +6,6 @@ from .main import main __author__ = 'Daniel Dunbar' __email__ = 'daniel@zuster.org' __versioninfo__ = (0, 3, 0) -__version__ = '.'.join(map(str, __versioninfo__)) + 'dev' +__version__ = '.'.join(str(v) for v in __versioninfo__) + 'dev' __all__ = [] diff --git a/utils/lit/lit/discovery.py b/utils/lit/lit/discovery.py index f76bd22..c3c0f28 100644 --- a/utils/lit/lit/discovery.py +++ b/utils/lit/lit/discovery.py @@ -2,9 +2,11 @@ Test discovery functions. """ +import copy import os import sys +import lit.run from lit.TestingConfig import TestingConfig from lit import LitConfig, Test @@ -38,11 +40,12 @@ def getTestSuite(item, litConfig, cache): ts, relative = search(parent) return (ts, relative + (base,)) - # We found a config file, load it. + # We found a test suite, create a new config for it and load it. if litConfig.debug: litConfig.note('loading suite config %r' % cfgpath) - cfg = TestingConfig.frompath(cfgpath, None, litConfig, mustExist = True) + cfg = TestingConfig.fromdefaults(litConfig) + cfg.load_from_path(cfgpath, litConfig) source_root = os.path.realpath(cfg.test_source_root or path) exec_root = os.path.realpath(cfg.test_exec_root or path) return Test.TestSuite(cfg.name, source_root, exec_root, cfg), () @@ -78,14 +81,21 @@ def getLocalConfig(ts, path_in_suite, litConfig, cache): else: parent = search(path_in_suite[:-1]) - # Load the local configuration. + # Check if there is a local configuration file. source_path = ts.getSourcePath(path_in_suite) cfgpath = os.path.join(source_path, litConfig.local_config_name) + + # If not, just reuse the parent config. + if not os.path.exists(cfgpath): + return parent + + # Otherwise, copy the current config and load the local configuration + # file into it. + config = copy.copy(parent) if litConfig.debug: litConfig.note('loading local config %r' % cfgpath) - return TestingConfig.frompath(cfgpath, parent, litConfig, - mustExist = False, - config = parent.clone(cfgpath)) + config.load_from_path(cfgpath, litConfig) + return config def search(path_in_suite): key = (ts, path_in_suite) @@ -237,7 +247,9 @@ def load_test_suite(inputs): isWindows = (platform.system()=='Windows'), params = {}) - tests = find_tests_for_inputs(litConfig, inputs) + # Perform test discovery. + run = lit.run.Run(litConfig, find_tests_for_inputs(litConfig, inputs)) # Return a unittest test suite which just runs the tests in order. - return unittest.TestSuite([LitTestCase(test, litConfig) for test in tests]) + return unittest.TestSuite([LitTestCase(test, run) + for test in run.tests]) diff --git a/utils/lit/lit/formats/__init__.py b/utils/lit/lit/formats/__init__.py new file mode 100644 index 0000000..6862708 --- /dev/null +++ b/utils/lit/lit/formats/__init__.py @@ -0,0 +1,4 @@ +from __future__ import absolute_import +from lit.formats.base import TestFormat, FileBasedTest, OneCommandPerFileTest +from lit.formats.googletest import GoogleTest +from lit.formats.shtest import ShTest diff --git a/utils/lit/lit/formats/base.py b/utils/lit/lit/formats/base.py new file mode 100644 index 0000000..9e5420b --- /dev/null +++ b/utils/lit/lit/formats/base.py @@ -0,0 +1,118 @@ +from __future__ import absolute_import +import os +import sys + +import lit.Test +import lit.util + +class TestFormat(object): + pass + +### + +class FileBasedTest(TestFormat): + def getTestsInDirectory(self, testSuite, path_in_suite, + litConfig, localConfig): + source_path = testSuite.getSourcePath(path_in_suite) + for filename in os.listdir(source_path): + # Ignore dot files and excluded tests. + if (filename.startswith('.') or + filename in localConfig.excludes): + continue + + filepath = os.path.join(source_path, filename) + if not os.path.isdir(filepath): + base,ext = os.path.splitext(filename) + if ext in localConfig.suffixes: + yield lit.Test.Test(testSuite, path_in_suite + (filename,), + localConfig) + +### + +import re +import tempfile + +class OneCommandPerFileTest(TestFormat): + # FIXME: Refactor into generic test for running some command on a directory + # of inputs. + + def __init__(self, command, dir, recursive=False, + pattern=".*", useTempInput=False): + if isinstance(command, str): + self.command = [command] + else: + self.command = list(command) + if dir is not None: + dir = str(dir) + self.dir = dir + self.recursive = bool(recursive) + self.pattern = re.compile(pattern) + self.useTempInput = useTempInput + + def getTestsInDirectory(self, testSuite, path_in_suite, + litConfig, localConfig): + dir = self.dir + if dir is None: + dir = testSuite.getSourcePath(path_in_suite) + + for dirname,subdirs,filenames in os.walk(dir): + if not self.recursive: + subdirs[:] = [] + + subdirs[:] = [d for d in subdirs + if (d != '.svn' and + d not in localConfig.excludes)] + + for filename in filenames: + if (filename.startswith('.') or + not self.pattern.match(filename) or + filename in localConfig.excludes): + continue + + path = os.path.join(dirname,filename) + suffix = path[len(dir):] + if suffix.startswith(os.sep): + suffix = suffix[1:] + test = lit.Test.Test( + testSuite, path_in_suite + tuple(suffix.split(os.sep)), + localConfig) + # FIXME: Hack? + test.source_path = path + yield test + + def createTempInput(self, tmp, test): + abstract + + def execute(self, test, litConfig): + if test.config.unsupported: + return (lit.Test.UNSUPPORTED, 'Test is unsupported') + + cmd = list(self.command) + + # If using temp input, create a temporary file and hand it to the + # subclass. + if self.useTempInput: + tmp = tempfile.NamedTemporaryFile(suffix='.cpp') + self.createTempInput(tmp, test) + tmp.flush() + cmd.append(tmp.name) + elif hasattr(test, 'source_path'): + cmd.append(test.source_path) + else: + cmd.append(test.getSourcePath()) + + out, err, exitCode = lit.util.executeCommand(cmd) + + diags = out + err + if not exitCode and not diags.strip(): + return lit.Test.PASS,'' + + # Try to include some useful information. + report = """Command: %s\n""" % ' '.join(["'%s'" % a + for a in cmd]) + if self.useTempInput: + report += """Temporary File: %s\n""" % tmp.name + report += "--\n%s--\n""" % open(tmp.name).read() + report += """Output:\n--\n%s--""" % diags + + return lit.Test.FAIL, report diff --git a/utils/lit/lit/formats/googletest.py b/utils/lit/lit/formats/googletest.py new file mode 100644 index 0000000..b77e184 --- /dev/null +++ b/utils/lit/lit/formats/googletest.py @@ -0,0 +1,114 @@ +from __future__ import absolute_import +import os +import sys + +import lit.Test +import lit.TestRunner +import lit.util +from .base import TestFormat + +kIsWindows = sys.platform in ['win32', 'cygwin'] + +class GoogleTest(TestFormat): + def __init__(self, test_sub_dir, test_suffix): + self.test_sub_dir = os.path.normcase(str(test_sub_dir)).split(';') + self.test_suffix = str(test_suffix) + + # On Windows, assume tests will also end in '.exe'. + if kIsWindows: + self.test_suffix += '.exe' + + def getGTestTests(self, path, litConfig, localConfig): + """getGTestTests(path) - [name] + + Return the tests available in gtest executable. + + Args: + path: String path to a gtest executable + litConfig: LitConfig instance + localConfig: TestingConfig instance""" + + try: + lines = lit.util.capture([path, '--gtest_list_tests'], + env=localConfig.environment) + lines = lines.decode('ascii') + if kIsWindows: + lines = lines.replace('\r', '') + lines = lines.split('\n') + except: + litConfig.error("unable to discover google-tests in %r" % path) + raise StopIteration + + nested_tests = [] + for ln in lines: + if not ln.strip(): + continue + + prefix = '' + index = 0 + while ln[index*2:index*2+2] == ' ': + index += 1 + while len(nested_tests) > index: + nested_tests.pop() + + ln = ln[index*2:] + if ln.endswith('.'): + nested_tests.append(ln) + else: + yield ''.join(nested_tests) + ln + + # Note: path_in_suite should not include the executable name. + def getTestsInExecutable(self, testSuite, path_in_suite, execpath, + litConfig, localConfig): + if not execpath.endswith(self.test_suffix): + return + (dirname, basename) = os.path.split(execpath) + # Discover the tests in this executable. + for testname in self.getGTestTests(execpath, litConfig, localConfig): + testPath = path_in_suite + (basename, testname) + yield lit.Test.Test(testSuite, testPath, localConfig) + + def getTestsInDirectory(self, testSuite, path_in_suite, + litConfig, localConfig): + source_path = testSuite.getSourcePath(path_in_suite) + for filename in os.listdir(source_path): + filepath = os.path.join(source_path, filename) + if os.path.isdir(filepath): + # Iterate over executables in a directory. + if not os.path.normcase(filename) in self.test_sub_dir: + continue + dirpath_in_suite = path_in_suite + (filename, ) + for subfilename in os.listdir(filepath): + execpath = os.path.join(filepath, subfilename) + for test in self.getTestsInExecutable( + testSuite, dirpath_in_suite, execpath, + litConfig, localConfig): + yield test + elif ('.' in self.test_sub_dir): + for test in self.getTestsInExecutable( + testSuite, path_in_suite, filepath, + litConfig, localConfig): + yield test + + def execute(self, test, litConfig): + testPath,testName = os.path.split(test.getSourcePath()) + while not os.path.exists(testPath): + # Handle GTest parametrized and typed tests, whose name includes + # some '/'s. + testPath, namePrefix = os.path.split(testPath) + testName = os.path.join(namePrefix, testName) + + cmd = [testPath, '--gtest_filter=' + testName] + if litConfig.useValgrind: + cmd = litConfig.valgrindArgs + cmd + + if litConfig.noExecute: + return lit.Test.PASS, '' + + out, err, exitCode = lit.util.executeCommand( + cmd, env=test.config.environment) + + if not exitCode: + return lit.Test.PASS,'' + + return lit.Test.FAIL, out + err diff --git a/utils/lit/lit/formats/shtest.py b/utils/lit/lit/formats/shtest.py new file mode 100644 index 0000000..30a6a33 --- /dev/null +++ b/utils/lit/lit/formats/shtest.py @@ -0,0 +1,12 @@ +from __future__ import absolute_import + +import lit.TestRunner +from .base import FileBasedTest + +class ShTest(FileBasedTest): + def __init__(self, execute_external = False): + self.execute_external = execute_external + + def execute(self, test, litConfig): + return lit.TestRunner.executeShTest(test, litConfig, + self.execute_external) diff --git a/utils/lit/lit/main.py b/utils/lit/lit/main.py index acf6101..6f672a0 100755 --- a/utils/lit/lit/main.py +++ b/utils/lit/lit/main.py @@ -7,37 +7,23 @@ See lit.pod for more information. """ from __future__ import absolute_import -import math, os, platform, random, re, sys, time, threading, traceback +import math, os, platform, random, re, sys, time import lit.ProgressBar import lit.LitConfig import lit.Test -import lit.Util - +import lit.run +import lit.util import lit.discovery -class TestingProgressDisplay: +class TestingProgressDisplay(object): def __init__(self, opts, numTests, progressBar=None): self.opts = opts self.numTests = numTests self.current = None - self.lock = threading.Lock() self.progressBar = progressBar self.completed = 0 - def update(self, test): - # Avoid locking overhead in quiet mode - if self.opts.quiet and not test.result.isFailure: - self.completed += 1 - return - - # Output lock. - self.lock.acquire() - try: - self.handleUpdate(test) - finally: - self.lock.release() - def finish(self): if self.progressBar: self.progressBar.clear() @@ -46,123 +32,86 @@ class TestingProgressDisplay: elif self.opts.succinct: sys.stdout.write('\n') - def handleUpdate(self, test): + def update(self, test): self.completed += 1 if self.progressBar: self.progressBar.update(float(self.completed)/self.numTests, test.getFullName()) - if self.opts.succinct and not test.result.isFailure: + if not test.result.code.isFailure and \ + (self.opts.quiet or self.opts.succinct): return if self.progressBar: self.progressBar.clear() - print('%s: %s (%d of %d)' % (test.result.name, test.getFullName(), + # Show the test result line. + test_name = test.getFullName() + print('%s: %s (%d of %d)' % (test.result.code.name, test_name, self.completed, self.numTests)) - if test.result.isFailure and self.opts.showOutput: + # Show the test failure output, if requested. + if test.result.code.isFailure and self.opts.showOutput: print("%s TEST '%s' FAILED %s" % ('*'*20, test.getFullName(), '*'*20)) - print(test.output) + print(test.result.output) print("*" * 20) + # Report test metrics, if present. + if test.result.metrics: + print("%s TEST '%s' RESULTS %s" % ('*'*10, test.getFullName(), + '*'*10)) + items = sorted(test.result.metrics.items()) + for metric_name, value in items: + print('%s: %s ' % (metric_name, value.format())) + print("*" * 10) + + # Ensure the output is flushed. sys.stdout.flush() -class TestProvider: - def __init__(self, tests, maxTime): - self.maxTime = maxTime - self.iter = iter(tests) - self.lock = threading.Lock() - self.startTime = time.time() - self.canceled = False - - def cancel(self): - self.lock.acquire() - self.canceled = True - self.lock.release() - - def get(self): - # Check if we have run out of time. - if self.maxTime is not None: - if time.time() - self.startTime > self.maxTime: - return None - - # Otherwise take the next test. - self.lock.acquire() - if self.canceled: - self.lock.release() - return None - for item in self.iter: - break - else: - item = None - self.lock.release() - return item - -class Tester(threading.Thread): - def __init__(self, litConfig, provider, display): - threading.Thread.__init__(self) - self.litConfig = litConfig - self.provider = provider - self.display = display - - def run(self): - while 1: - item = self.provider.get() - if item is None: - break - self.runTest(item) - - def runTest(self, test): - result = None - startTime = time.time() - try: - result, output = test.config.test_format.execute(test, - self.litConfig) - except KeyboardInterrupt: - # This is a sad hack. Unfortunately subprocess goes - # bonkers with ctrl-c and we start forking merrily. - print('\nCtrl-C detected, goodbye.') - os.kill(0,9) - except: - if self.litConfig.debug: - raise - result = lit.Test.UNRESOLVED - output = 'Exception during script execution:\n' - output += traceback.format_exc() - output += '\n' - elapsed = time.time() - startTime - - test.setResult(result, output, elapsed) - self.display.update(test) - -def runTests(numThreads, litConfig, provider, display): - # If only using one testing thread, don't use threads at all; this lets us - # profile, among other things. - if numThreads == 1: - t = Tester(litConfig, provider, display) - t.run() - return - - # Otherwise spin up the testing threads and wait for them to finish. - testers = [Tester(litConfig, provider, display) - for i in range(numThreads)] - for t in testers: - t.start() +def write_test_results(run, lit_config, testing_time, output_path): try: - for t in testers: - t.join() - except KeyboardInterrupt: - sys.exit(2) + import json + except ImportError: + lit_config.fatal('test output unsupported with Python 2.5') + + # Construct the data we will write. + data = {} + # Encode the current lit version as a schema version. + data['__version__'] = lit.__versioninfo__ + data['elapsed'] = testing_time + # FIXME: Record some information on the lit configuration used? + # FIXME: Record information from the individual test suites? + + # Encode the tests. + data['tests'] = tests_data = [] + for test in run.tests: + test_data = { + 'name' : test.getFullName(), + 'code' : test.result.code.name, + 'output' : test.result.output, + 'elapsed' : test.result.elapsed } + + # Add test metrics, if present. + if test.result.metrics: + test_data['metrics'] = metrics_data = {} + for key, value in test.result.metrics.items(): + metrics_data[key] = value.todata() + + tests_data.append(test_data) + + # Write the output. + f = open(output_path, 'w') + try: + json.dump(data, f, indent=2, sort_keys=True) + f.write('\n') + finally: + f.close() def main(builtinParameters = {}): - # Bump the GIL check interval, its more important to get any one thread to a - # blocking operation (hopefully exec) than to try and unblock other threads. - # - # FIXME: This is a hack. - import sys - sys.setcheckinterval(1000) + # Use processes by default on Unix platforms. + isWindows = platform.system() == 'Windows' + useProcessesIsDefault = not isWindows global options from optparse import OptionParser, OptionGroup @@ -191,6 +140,9 @@ def main(builtinParameters = {}): group.add_option("-v", "--verbose", dest="showOutput", help="Show all test output", action="store_true", default=False) + group.add_option("-o", "--output", dest="output_path", + help="Write test results to the provided path", + action="store", type=str, metavar="PATH") group.add_option("", "--no-progress-bar", dest="useProgressBar", help="Do not use curses based progress bar", action="store_false", default=True) @@ -243,9 +195,12 @@ def main(builtinParameters = {}): group.add_option("", "--show-tests", dest="showTests", help="Show all discovered tests", action="store_true", default=False) - group.add_option("", "--repeat", dest="repeatTests", metavar="N", - help="Repeat tests N times (for timing)", - action="store", default=None, type=int) + group.add_option("", "--use-processes", dest="useProcesses", + help="Run tests in parallel with processes (not threads)", + action="store_true", default=useProcessesIsDefault) + group.add_option("", "--use-threads", dest="useProcesses", + help="Run tests in parallel with threads (not processes)", + action="store_false", default=useProcessesIsDefault) parser.add_option_group(group) (opts, args) = parser.parse_args() @@ -259,7 +214,7 @@ def main(builtinParameters = {}): # I haven't seen this bug occur with 2.5.2 and later, so only enable multiple # threads by default there. if sys.hexversion >= 0x2050200: - opts.numThreads = lit.Util.detectCPUs() + opts.numThreads = lit.util.detectCPUs() else: opts.numThreads = 1 @@ -284,20 +239,22 @@ def main(builtinParameters = {}): valgrindArgs = opts.valgrindArgs, noExecute = opts.noExecute, debug = opts.debug, - isWindows = (platform.system()=='Windows'), + isWindows = isWindows, params = userParams, config_prefix = opts.configPrefix) - tests = lit.discovery.find_tests_for_inputs(litConfig, inputs) + # Perform test discovery. + run = lit.run.Run(litConfig, + lit.discovery.find_tests_for_inputs(litConfig, inputs)) if opts.showSuites or opts.showTests: # Aggregate the tests by suite. suitesAndTests = {} - for t in tests: + for t in run.tests: if t.suite not in suitesAndTests: suitesAndTests[t.suite] = [] suitesAndTests[t.suite].append(t) - suitesAndTests = suitesAndTests.items() + suitesAndTests = list(suitesAndTests.items()) suitesAndTests.sort(key = lambda item: item[0].name) # Show the suites, if requested. @@ -315,9 +272,12 @@ def main(builtinParameters = {}): ts_tests.sort(key = lambda test: test.path_in_suite) for test in ts_tests: print(' %s' % (test.getFullName(),)) - + + # Exit. + sys.exit(0) + # Select and order the tests. - numTotalTests = len(tests) + numTotalTests = len(run.tests) # First, select based on the filter expression if given. if opts.filter: @@ -326,33 +286,28 @@ def main(builtinParameters = {}): except: parser.error("invalid regular expression for --filter: %r" % ( opts.filter)) - tests = [t for t in tests - if rex.search(t.getFullName())] + run.tests = [t for t in run.tests + if rex.search(t.getFullName())] # Then select the order. if opts.shuffle: - random.shuffle(tests) + random.shuffle(run.tests) else: - tests.sort(key = lambda t: t.getFullName()) + run.tests.sort(key = lambda t: t.getFullName()) # Finally limit the number of tests, if desired. if opts.maxTests is not None: - tests = tests[:opts.maxTests] + run.tests = run.tests[:opts.maxTests] # Don't create more threads than tests. - opts.numThreads = min(len(tests), opts.numThreads) + opts.numThreads = min(len(run.tests), opts.numThreads) extra = '' - if len(tests) != numTotalTests: + if len(run.tests) != numTotalTests: extra = ' of %d' % numTotalTests - header = '-- Testing: %d%s tests, %d threads --'%(len(tests),extra, + header = '-- Testing: %d%s tests, %d threads --'%(len(run.tests), extra, opts.numThreads) - if opts.repeatTests: - tests = [t.copyWithIndex(i) - for t in tests - for i in range(opts.repeatTests)] - progressBar = None if not opts.quiet: if opts.succinct and opts.useProgressBar: @@ -366,63 +321,50 @@ def main(builtinParameters = {}): print(header) startTime = time.time() - display = TestingProgressDisplay(opts, len(tests), progressBar) - provider = TestProvider(tests, opts.maxTime) - + display = TestingProgressDisplay(opts, len(run.tests), progressBar) try: - import win32api - except ImportError: - pass - else: - def console_ctrl_handler(type): - provider.cancel() - return True - win32api.SetConsoleCtrlHandler(console_ctrl_handler, True) - - runTests(opts.numThreads, litConfig, provider, display) + run.execute_tests(display, opts.numThreads, opts.maxTime, + opts.useProcesses) + except KeyboardInterrupt: + sys.exit(2) display.finish() + testing_time = time.time() - startTime if not opts.quiet: - print('Testing Time: %.2fs'%(time.time() - startTime)) + print('Testing Time: %.2fs' % (testing_time,)) - # Update results for any tests which weren't run. - for t in tests: - if t.result is None: - t.setResult(lit.Test.UNRESOLVED, '', 0.0) + # Write out the test data, if requested. + if opts.output_path is not None: + write_test_results(run, litConfig, testing_time, opts.output_path) # List test results organized by kind. hasFailures = False byCode = {} - for t in tests: - if t.result not in byCode: - byCode[t.result] = [] - byCode[t.result].append(t) - if t.result.isFailure: + for test in run.tests: + if test.result.code not in byCode: + byCode[test.result.code] = [] + byCode[test.result.code].append(test) + if test.result.code.isFailure: hasFailures = True - # FIXME: Show unresolved and (optionally) unsupported tests. + # Print each test in any of the failing groups. for title,code in (('Unexpected Passing Tests', lit.Test.XPASS), - ('Failing Tests', lit.Test.FAIL)): + ('Failing Tests', lit.Test.FAIL), + ('Unresolved Tests', lit.Test.UNRESOLVED)): elts = byCode.get(code) if not elts: continue print('*'*20) print('%s (%d):' % (title, len(elts))) - for t in elts: - print(' %s' % t.getFullName()) + for test in elts: + print(' %s' % test.getFullName()) sys.stdout.write('\n') - if opts.timeTests: - # Collate, in case we repeated tests. - times = {} - for t in tests: - key = t.getFullName() - times[key] = times.get(key, 0.) + t.elapsed - - byTime = list(times.items()) - byTime.sort(key = lambda item: item[1]) - if byTime: - lit.Util.printHistogram(byTime, title='Tests') + if opts.timeTests and run.tests: + # Order by time. + test_times = [(test.getFullName(), test.result.elapsed) + for test in run.tests] + lit.util.printHistogram(test_times, title='Tests') for name,code in (('Expected Passes ', lit.Test.PASS), ('Expected Failures ', lit.Test.XFAIL), diff --git a/utils/lit/lit/run.py b/utils/lit/lit/run.py new file mode 100644 index 0000000..27c414d --- /dev/null +++ b/utils/lit/lit/run.py @@ -0,0 +1,277 @@ +import os +import threading +import time +import traceback +try: + import Queue as queue +except ImportError: + import queue + +try: + import win32api +except ImportError: + win32api = None + +try: + import multiprocessing +except ImportError: + multiprocessing = None + +import lit.Test + +### +# Test Execution Implementation + +class LockedValue(object): + def __init__(self, value): + self.lock = threading.Lock() + self._value = value + + def _get_value(self): + self.lock.acquire() + try: + return self._value + finally: + self.lock.release() + + def _set_value(self, value): + self.lock.acquire() + try: + self._value = value + finally: + self.lock.release() + + value = property(_get_value, _set_value) + +class TestProvider(object): + def __init__(self, tests, num_jobs, queue_impl, canceled_flag): + self.canceled_flag = canceled_flag + + # Create a shared queue to provide the test indices. + self.queue = queue_impl() + for i in range(len(tests)): + self.queue.put(i) + for i in range(num_jobs): + self.queue.put(None) + + def cancel(self): + self.canceled_flag.value = 1 + + def get(self): + # Check if we are canceled. + if self.canceled_flag.value: + return None + + # Otherwise take the next test. + return self.queue.get() + +class Tester(object): + def __init__(self, run_instance, provider, consumer): + self.run_instance = run_instance + self.provider = provider + self.consumer = consumer + + def run(self): + while True: + item = self.provider.get() + if item is None: + break + self.run_test(item) + self.consumer.task_finished() + + def run_test(self, test_index): + test = self.run_instance.tests[test_index] + try: + self.run_instance.execute_test(test) + except KeyboardInterrupt: + # This is a sad hack. Unfortunately subprocess goes + # bonkers with ctrl-c and we start forking merrily. + print('\nCtrl-C detected, goodbye.') + os.kill(0,9) + self.consumer.update(test_index, test) + +class ThreadResultsConsumer(object): + def __init__(self, display): + self.display = display + self.lock = threading.Lock() + + def update(self, test_index, test): + self.lock.acquire() + try: + self.display.update(test) + finally: + self.lock.release() + + def task_finished(self): + pass + + def handle_results(self): + pass + +class MultiprocessResultsConsumer(object): + def __init__(self, run, display, num_jobs): + self.run = run + self.display = display + self.num_jobs = num_jobs + self.queue = multiprocessing.Queue() + + def update(self, test_index, test): + # This method is called in the child processes, and communicates the + # results to the actual display implementation via an output queue. + self.queue.put((test_index, test.result)) + + def task_finished(self): + # This method is called in the child processes, and communicates that + # individual tasks are complete. + self.queue.put(None) + + def handle_results(self): + # This method is called in the parent, and consumes the results from the + # output queue and dispatches to the actual display. The method will + # complete after each of num_jobs tasks has signalled completion. + completed = 0 + while completed != self.num_jobs: + # Wait for a result item. + item = self.queue.get() + if item is None: + completed += 1 + continue + + # Update the test result in the parent process. + index,result = item + test = self.run.tests[index] + test.result = result + + self.display.update(test) + +def run_one_tester(run, provider, display): + tester = Tester(run, provider, display) + tester.run() + +### + +class Run(object): + """ + This class represents a concrete, configured testing run. + """ + + def __init__(self, lit_config, tests): + self.lit_config = lit_config + self.tests = tests + + def execute_test(self, test): + result = None + start_time = time.time() + try: + result = test.config.test_format.execute(test, self.lit_config) + + # Support deprecated result from execute() which returned the result + # code and additional output as a tuple. + if isinstance(result, tuple): + code, output = result + result = lit.Test.Result(code, output) + elif not isinstance(result, lit.Test.Result): + raise ValueError("unexpected result from test execution") + except KeyboardInterrupt: + raise + except: + if self.lit_config.debug: + raise + output = 'Exception during script execution:\n' + output += traceback.format_exc() + output += '\n' + result = lit.Test.Result(lit.Test.UNRESOLVED, output) + result.elapsed = time.time() - start_time + + test.setResult(result) + + def execute_tests(self, display, jobs, max_time=None, + use_processes=False): + """ + execute_tests(display, jobs, [max_time]) + + Execute each of the tests in the run, using up to jobs number of + parallel tasks, and inform the display of each individual result. The + provided tests should be a subset of the tests available in this run + object. + + If max_time is non-None, it should be a time in seconds after which to + stop executing tests. + + The display object will have its update method called with each test as + it is completed. The calls are guaranteed to be locked with respect to + one another, but are *not* guaranteed to be called on the same thread as + this method was invoked on. + + Upon completion, each test in the run will have its result + computed. Tests which were not actually executed (for any reason) will + be given an UNRESOLVED result. + """ + + # Choose the appropriate parallel execution implementation. + consumer = None + if jobs != 1 and use_processes and multiprocessing: + try: + task_impl = multiprocessing.Process + queue_impl = multiprocessing.Queue + canceled_flag = multiprocessing.Value('i', 0) + consumer = MultiprocessResultsConsumer(self, display, jobs) + except: + # multiprocessing fails to initialize with certain OpenBSD and + # FreeBSD Python versions: http://bugs.python.org/issue3770 + # Unfortunately the error raised also varies by platform. + self.lit_config.note('failed to initialize multiprocessing') + consumer = None + if not consumer: + task_impl = threading.Thread + queue_impl = queue.Queue + canceled_flag = LockedValue(0) + consumer = ThreadResultsConsumer(display) + + # Create the test provider. + provider = TestProvider(self.tests, jobs, queue_impl, canceled_flag) + + # Install a console-control signal handler on Windows. + if win32api is not None: + def console_ctrl_handler(type): + provider.cancel() + return True + win32api.SetConsoleCtrlHandler(console_ctrl_handler, True) + + # Install a timeout handler, if requested. + if max_time is not None: + def timeout_handler(): + provider.cancel() + timeout_timer = threading.Timer(max_time, timeout_handler) + timeout_timer.start() + + # If not using multiple tasks, just run the tests directly. + if jobs == 1: + run_one_tester(self, provider, consumer) + else: + # Otherwise, execute the tests in parallel + self._execute_tests_in_parallel(task_impl, provider, consumer, jobs) + + # Cancel the timeout handler. + if max_time is not None: + timeout_timer.cancel() + + # Update results for any tests which weren't run. + for test in self.tests: + if test.result is None: + test.setResult(lit.Test.Result(lit.Test.UNRESOLVED, '', 0.0)) + + def _execute_tests_in_parallel(self, task_impl, provider, consumer, jobs): + # Start all of the tasks. + tasks = [task_impl(target=run_one_tester, + args=(self, provider, consumer)) + for i in range(jobs)] + for t in tasks: + t.start() + + # Allow the consumer to handle results, if necessary. + consumer.handle_results() + + # Wait for all the tasks to complete. + for t in tasks: + t.join() diff --git a/utils/lit/lit/Util.py b/utils/lit/lit/util.py index 6f09eed..2b1010c 100644 --- a/utils/lit/lit/Util.py +++ b/utils/lit/lit/util.py @@ -1,4 +1,11 @@ -import os, sys +import errno +import itertools +import math +import os +import platform +import signal +import subprocess +import sys def detectCPUs(): """ @@ -6,7 +13,7 @@ def detectCPUs(): """ # Linux, Unix and MacOS: if hasattr(os, "sysconf"): - if os.sysconf_names.has_key("SC_NPROCESSORS_ONLN"): + if "SC_NPROCESSORS_ONLN" in os.sysconf_names: # Linux & Unix: ncpus = os.sysconf("SC_NPROCESSORS_ONLN") if isinstance(ncpus, int) and ncpus > 0: @@ -14,7 +21,7 @@ def detectCPUs(): else: # OSX: return int(capture(['sysctl', '-n', 'hw.ncpu'])) # Windows: - if os.environ.has_key("NUMBER_OF_PROCESSORS"): + if "NUMBER_OF_PROCESSORS" in os.environ: ncpus = int(os.environ["NUMBER_OF_PROCESSORS"]) if ncpus > 0: return ncpus @@ -23,8 +30,6 @@ def detectCPUs(): def mkdir_p(path): """mkdir_p(path) - Make the "path" directory, if it does not exist; this will also make directories for any missing parent directories.""" - import errno - if not path or os.path.exists(path): return @@ -41,7 +46,6 @@ def mkdir_p(path): raise def capture(args, env=None): - import subprocess """capture(command) - Run the given command (or argv list) in a shell and return the standard output.""" p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, @@ -93,8 +97,6 @@ def whichTools(tools, paths): return None def printHistogram(items, title = 'Items'): - import itertools, math - items.sort(key = lambda item: item[1]) maxValue = max([v for _,v in items]) @@ -138,3 +140,30 @@ def printHistogram(items, title = 'Items'): pDigits, pfDigits, i*barH, pDigits, pfDigits, (i+1)*barH, '*'*w, ' '*(barW-w), cDigits, len(row), cDigits, len(items))) +# Close extra file handles on UNIX (on Windows this cannot be done while +# also redirecting input). +kUseCloseFDs = not (platform.system() == 'Windows') +def executeCommand(command, cwd=None, env=None): + p = subprocess.Popen(command, cwd=cwd, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=env, close_fds=kUseCloseFDs) + out,err = p.communicate() + exitCode = p.wait() + + # Detect Ctrl-C in subprocess. + if exitCode == -signal.SIGINT: + raise KeyboardInterrupt + + # Ensure the resulting output is always of string type. + try: + out = str(out.decode('ascii')) + except: + out = str(out) + try: + err = str(err.decode('ascii')) + except: + err = str(err) + + return out, err, exitCode diff --git a/utils/lit/setup.py b/utils/lit/setup.py index a94e6ea..10de6bb 100644 --- a/utils/lit/setup.py +++ b/utils/lit/setup.py @@ -1,7 +1,14 @@ import lit +import os -# FIXME: Support distutils? from setuptools import setup, find_packages + +# setuptools expects to be invoked from within the directory of setup.py, but it +# is nice to allow: +# python path/to/setup.py install +# to work (for scripts, etc.) +os.chdir(os.path.dirname(os.path.abspath(__file__))) + setup( name = "lit", version = lit.__version__, diff --git a/utils/lit/tests/Inputs/discovery/lit.cfg b/utils/lit/tests/Inputs/discovery/lit.cfg index 4049ab1..c48ca0b 100644 --- a/utils/lit/tests/Inputs/discovery/lit.cfg +++ b/utils/lit/tests/Inputs/discovery/lit.cfg @@ -1,3 +1,4 @@ +import lit.formats config.name = 'top-level-suite' config.suffixes = ['.txt'] config.test_format = lit.formats.ShTest() @@ -8,3 +9,6 @@ config.test_format = lit.formats.ShTest() # #config.test_source_root = None #config.test_exec_root = None + +# Check that arbitrary config values are copied (tested by subdir/lit.local.cfg). +config.an_extra_variable = False diff --git a/utils/lit/tests/Inputs/discovery/subdir/lit.local.cfg b/utils/lit/tests/Inputs/discovery/subdir/lit.local.cfg index 5ae6b3c..631cb60 100644 --- a/utils/lit/tests/Inputs/discovery/subdir/lit.local.cfg +++ b/utils/lit/tests/Inputs/discovery/subdir/lit.local.cfg @@ -1 +1,4 @@ config.suffixes = ['.py'] + +# Check that the arbitrary config values in our parent was inherited. +assert hasattr(config, 'an_extra_variable') diff --git a/utils/lit/tests/Inputs/discovery/subsuite/lit.cfg b/utils/lit/tests/Inputs/discovery/subsuite/lit.cfg index 0c2979d..b49329a 100644 --- a/utils/lit/tests/Inputs/discovery/subsuite/lit.cfg +++ b/utils/lit/tests/Inputs/discovery/subsuite/lit.cfg @@ -1,3 +1,4 @@ +import lit.formats config.name = 'sub-suite' config.suffixes = ['.txt'] config.test_format = lit.formats.ShTest() diff --git a/utils/lit/tests/Inputs/exec-discovery-in-tree/lit.cfg b/utils/lit/tests/Inputs/exec-discovery-in-tree/lit.cfg index 342b2fd..ae25b4f 100644 --- a/utils/lit/tests/Inputs/exec-discovery-in-tree/lit.cfg +++ b/utils/lit/tests/Inputs/exec-discovery-in-tree/lit.cfg @@ -1,6 +1,8 @@ +import lit.formats + # Verify that the site configuration was loaded. if config.test_source_root is None or config.test_exec_root is None: - lit.fatal("No site specific configuration") + lit_config.fatal("No site specific configuration") config.name = 'exec-discovery-in-tree-suite' config.suffixes = ['.txt'] diff --git a/utils/lit/tests/Inputs/exec-discovery-in-tree/obj/lit.site.cfg b/utils/lit/tests/Inputs/exec-discovery-in-tree/obj/lit.site.cfg index de9a3d0..4061c89 100644 --- a/utils/lit/tests/Inputs/exec-discovery-in-tree/obj/lit.site.cfg +++ b/utils/lit/tests/Inputs/exec-discovery-in-tree/obj/lit.site.cfg @@ -1,4 +1,4 @@ import os config.test_exec_root = os.path.dirname(__file__) config.test_source_root = os.path.dirname(config.test_exec_root) -lit.load_config(config, os.path.join(config.test_source_root, "lit.cfg"))
\ No newline at end of file +lit_config.load_config(config, os.path.join(config.test_source_root, "lit.cfg"))
\ No newline at end of file diff --git a/utils/lit/tests/Inputs/exec-discovery/lit.site.cfg b/utils/lit/tests/Inputs/exec-discovery/lit.site.cfg index 796569a..ac273c7 100644 --- a/utils/lit/tests/Inputs/exec-discovery/lit.site.cfg +++ b/utils/lit/tests/Inputs/exec-discovery/lit.site.cfg @@ -2,4 +2,4 @@ import os config.test_exec_root = os.path.dirname(__file__) config.test_source_root = os.path.join(os.path.dirname(config.test_exec_root), "discovery") -lit.load_config(config, os.path.join(config.test_source_root, "lit.cfg")) +lit_config.load_config(config, os.path.join(config.test_source_root, "lit.cfg")) diff --git a/utils/lit/tests/Inputs/googletest-format/DummySubDir/OneTest b/utils/lit/tests/Inputs/googletest-format/DummySubDir/OneTest new file mode 100755 index 0000000..9dff137 --- /dev/null +++ b/utils/lit/tests/Inputs/googletest-format/DummySubDir/OneTest @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +import sys + +if len(sys.argv) != 2: + raise ValueError("unexpected number of args") + +if sys.argv[1] == "--gtest_list_tests": + print("""\ +FirstTest. + subTestA + subTestB +ParameterizedTest/0. + subTest +ParameterizedTest/1. + subTest""") + sys.exit(0) +elif not sys.argv[1].startswith("--gtest_filter="): + raise ValueError("unexpected argument: %r" % (sys.argv[1])) + +test_name = sys.argv[1].split('=',1)[1] +if test_name == 'FirstTest.subTestA': + print('I am subTest A, I PASS') + sys.exit(0) +elif test_name == 'FirstTest.subTestB': + print('I am subTest B, I FAIL') + print('And I have two lines of output') + sys.exit(1) +elif test_name in ('ParameterizedTest/0.subTest', + 'ParameterizedTest/1.subTest'): + print('I am a parameterized test, I also PASS') + sys.exit(0) +else: + raise SystemExit("error: invalid test name: %r" % (test_name,)) diff --git a/utils/lit/tests/Inputs/googletest-format/lit.cfg b/utils/lit/tests/Inputs/googletest-format/lit.cfg new file mode 100644 index 0000000..f2f6cda --- /dev/null +++ b/utils/lit/tests/Inputs/googletest-format/lit.cfg @@ -0,0 +1,3 @@ +import lit.formats +config.name = 'googletest-format' +config.test_format = lit.formats.GoogleTest('DummySubDir', 'Test') diff --git a/utils/lit/tests/Inputs/progress-bar/lit.cfg b/utils/lit/tests/Inputs/progress-bar/lit.cfg index 4878b65..7f31129 100644 --- a/utils/lit/tests/Inputs/progress-bar/lit.cfg +++ b/utils/lit/tests/Inputs/progress-bar/lit.cfg @@ -1,3 +1,4 @@ +import lit.formats config.name = 'shtest-shell' config.suffixes = ['.txt'] config.test_format = lit.formats.ShTest() diff --git a/utils/lit/tests/Inputs/shtest-format/argv0.txt b/utils/lit/tests/Inputs/shtest-format/argv0.txt new file mode 100644 index 0000000..2ff2890 --- /dev/null +++ b/utils/lit/tests/Inputs/shtest-format/argv0.txt @@ -0,0 +1,6 @@ +# Check that we route argv[0] as it was written, instead of the resolved +# path. This is important for some tools, in particular '[' which at least on OS +# X only recognizes that it is in '['-mode when its argv[0] is exactly +# '['. Otherwise it will refuse to accept the trailing closing bracket. +# +# RUN: [ "A" = "A" ] diff --git a/utils/lit/tests/Inputs/shtest-format/external_shell/fail.txt b/utils/lit/tests/Inputs/shtest-format/external_shell/fail.txt index 1e74be5..069e376 100644 --- a/utils/lit/tests/Inputs/shtest-format/external_shell/fail.txt +++ b/utils/lit/tests/Inputs/shtest-format/external_shell/fail.txt @@ -1,3 +1,5 @@ # Run a command that fails with error on stdout. # +# RUN: echo "line 1: failed test output on stdout" +# RUN: echo "line 2: failed test output on stdout" # RUN: cat "does-not-exist" diff --git a/utils/lit/tests/Inputs/shtest-format/external_shell/fail_with_bad_encoding.txt b/utils/lit/tests/Inputs/shtest-format/external_shell/fail_with_bad_encoding.txt new file mode 100644 index 0000000..f6157e6 --- /dev/null +++ b/utils/lit/tests/Inputs/shtest-format/external_shell/fail_with_bad_encoding.txt @@ -0,0 +1,5 @@ +# Run a command that fails with error on stdout. +# +# RUN: %S/write-bad-encoding.sh +# RUN: false + diff --git a/utils/lit/tests/Inputs/shtest-format/external_shell/lit.local.cfg b/utils/lit/tests/Inputs/shtest-format/external_shell/lit.local.cfg index d14d147..5e87c72 100644 --- a/utils/lit/tests/Inputs/shtest-format/external_shell/lit.local.cfg +++ b/utils/lit/tests/Inputs/shtest-format/external_shell/lit.local.cfg @@ -1 +1,2 @@ +import lit.formats config.test_format = lit.formats.ShTest(execute_external=True) diff --git a/utils/lit/tests/Inputs/shtest-format/external_shell/write-bad-encoding.sh b/utils/lit/tests/Inputs/shtest-format/external_shell/write-bad-encoding.sh new file mode 100755 index 0000000..6b622cb --- /dev/null +++ b/utils/lit/tests/Inputs/shtest-format/external_shell/write-bad-encoding.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "a line with bad encoding: Â." diff --git a/utils/lit/tests/Inputs/shtest-format/fail.txt b/utils/lit/tests/Inputs/shtest-format/fail.txt index 49932c3..8c305eb 100644 --- a/utils/lit/tests/Inputs/shtest-format/fail.txt +++ b/utils/lit/tests/Inputs/shtest-format/fail.txt @@ -1 +1,2 @@ +# RUN: printf "line 1: failed test output on stdout\nline 2: failed test output on stdout" # RUN: false diff --git a/utils/lit/tests/Inputs/shtest-format/lit.cfg b/utils/lit/tests/Inputs/shtest-format/lit.cfg index 78dd1bf..9b47985 100644 --- a/utils/lit/tests/Inputs/shtest-format/lit.cfg +++ b/utils/lit/tests/Inputs/shtest-format/lit.cfg @@ -1,3 +1,4 @@ +import lit.formats config.name = 'shtest-format' config.suffixes = ['.txt'] config.test_format = lit.formats.ShTest() diff --git a/utils/lit/tests/Inputs/shtest-shell/lit.cfg b/utils/lit/tests/Inputs/shtest-shell/lit.cfg index 4878b65..7f31129 100644 --- a/utils/lit/tests/Inputs/shtest-shell/lit.cfg +++ b/utils/lit/tests/Inputs/shtest-shell/lit.cfg @@ -1,3 +1,4 @@ +import lit.formats config.name = 'shtest-shell' config.suffixes = ['.txt'] config.test_format = lit.formats.ShTest() diff --git a/utils/lit/tests/Inputs/test-data/lit.cfg b/utils/lit/tests/Inputs/test-data/lit.cfg new file mode 100644 index 0000000..f5aba7b --- /dev/null +++ b/utils/lit/tests/Inputs/test-data/lit.cfg @@ -0,0 +1,44 @@ +import os +try: + import ConfigParser +except ImportError: + import configparser as ConfigParser + +import lit.formats +import lit.Test + +class DummyFormat(lit.formats.FileBasedTest): + def execute(self, test, lit_config): + # In this dummy format, expect that each test file is actually just a + # .ini format dump of the results to report. + + source_path = test.getSourcePath() + + cfg = ConfigParser.ConfigParser() + cfg.read(source_path) + + # Create the basic test result. + result_code = cfg.get('global', 'result_code') + result_output = cfg.get('global', 'result_output') + result = lit.Test.Result(getattr(lit.Test, result_code), + result_output) + + # Load additional metrics. + for key,value_str in cfg.items('results'): + value = eval(value_str) + if isinstance(value, int): + metric = lit.Test.IntMetricValue(value) + elif isinstance(value, float): + metric = lit.Test.RealMetricValue(value) + else: + raise RuntimeError("unsupported result type") + result.addMetric(key, metric) + + return result + +config.name = 'test-data' +config.suffixes = ['.ini'] +config.test_format = DummyFormat() +config.test_source_root = None +config.test_exec_root = None +config.target_triple = None diff --git a/utils/lit/tests/Inputs/test-data/metrics.ini b/utils/lit/tests/Inputs/test-data/metrics.ini new file mode 100644 index 0000000..01b09c5 --- /dev/null +++ b/utils/lit/tests/Inputs/test-data/metrics.ini @@ -0,0 +1,7 @@ +[global] +result_code = PASS +result_output = Test passed. + +[results] +value0 = 1 +value1 = 2.3456
\ No newline at end of file diff --git a/utils/lit/tests/Inputs/unittest-adaptor/lit.cfg b/utils/lit/tests/Inputs/unittest-adaptor/lit.cfg index 52de709..9e08a86 100644 --- a/utils/lit/tests/Inputs/unittest-adaptor/lit.cfg +++ b/utils/lit/tests/Inputs/unittest-adaptor/lit.cfg @@ -1,3 +1,4 @@ +import lit.formats config.name = 'unittest-adaptor' config.suffixes = ['.txt'] config.test_format = lit.formats.ShTest() diff --git a/utils/lit/tests/discovery.py b/utils/lit/tests/discovery.py index be98c4b..2801089 100644 --- a/utils/lit/tests/discovery.py +++ b/utils/lit/tests/discovery.py @@ -1,7 +1,7 @@ # Check the basic discovery process, including a sub-suite. # # RUN: %{lit} %{inputs}/discovery \ -# RUN: -j 1 --debug --show-tests --show-suites --max-tests 0 \ +# RUN: -j 1 --debug --show-tests --show-suites \ # RUN: -v > %t.out 2> %t.err # RUN: FileCheck --check-prefix=CHECK-BASIC-OUT < %t.out %s # RUN: FileCheck --check-prefix=CHECK-BASIC-ERR < %t.err %s @@ -24,7 +24,6 @@ # CHECK-BASIC-OUT: top-level-suite :: subdir/test-three # CHECK-BASIC-OUT: top-level-suite :: test-one # CHECK-BASIC-OUT: top-level-suite :: test-two -# CHECK-BASIC-OUT: -- Testing: 0 # Check discovery when exact test names are given. @@ -32,19 +31,18 @@ # RUN: %{lit} \ # RUN: %{inputs}/discovery/subdir/test-three.py \ # RUN: %{inputs}/discovery/subsuite/test-one.txt \ -# RUN: -j 1 --show-tests --show-suites --max-tests 0 -v > %t.out +# RUN: -j 1 --show-tests --show-suites -v > %t.out # RUN: FileCheck --check-prefix=CHECK-EXACT-TEST < %t.out %s # # CHECK-EXACT-TEST: -- Available Tests -- # CHECK-EXACT-TEST: sub-suite :: test-one # CHECK-EXACT-TEST: top-level-suite :: subdir/test-three -# CHECK-EXACT-TEST: -- Testing: 0 # Check discovery when using an exec path. # # RUN: %{lit} %{inputs}/exec-discovery \ -# RUN: -j 1 --debug --show-tests --show-suites --max-tests 0 \ +# RUN: -j 1 --debug --show-tests --show-suites \ # RUN: -v > %t.out 2> %t.err # RUN: FileCheck --check-prefix=CHECK-ASEXEC-OUT < %t.out %s # RUN: FileCheck --check-prefix=CHECK-ASEXEC-ERR < %t.err %s @@ -70,7 +68,6 @@ # CHECK-ASEXEC-OUT: top-level-suite :: subdir/test-three # CHECK-ASEXEC-OUT: top-level-suite :: test-one # CHECK-ASEXEC-OUT: top-level-suite :: test-two -# CHECK-ASEXEC-OUT: -- Testing: 0 # Check discovery when exact test names are given. # @@ -78,12 +75,11 @@ # # RUN: %{lit} \ # RUN: %{inputs}/exec-discovery/subdir/test-three.py \ -# RUN: -j 1 --show-tests --show-suites --max-tests 0 -v > %t.out +# RUN: -j 1 --show-tests --show-suites -v > %t.out # RUN: FileCheck --check-prefix=CHECK-ASEXEC-EXACT-TEST < %t.out %s # # CHECK-ASEXEC-EXACT-TEST: -- Available Tests -- # CHECK-ASEXEC-EXACT-TEST: top-level-suite :: subdir/test-three -# CHECK-ASEXEC-EXACT-TEST: -- Testing: 0 # Check that we don't recurse infinitely when loading an site specific test @@ -91,7 +87,7 @@ # # RUN: %{lit} \ # RUN: %{inputs}/exec-discovery-in-tree/obj/ \ -# RUN: -j 1 --show-tests --show-suites --max-tests 0 -v > %t.out +# RUN: -j 1 --show-tests --show-suites -v > %t.out # RUN: FileCheck --check-prefix=CHECK-ASEXEC-INTREE < %t.out %s # # CHECK-ASEXEC-INTREE: exec-discovery-in-tree-suite - 1 tests @@ -99,4 +95,3 @@ # CHECK-ASEXEC-INTREE-NEXT: Exec Root : {{.*/exec-discovery-in-tree/obj$}} # CHECK-ASEXEC-INTREE-NEXT: -- Available Tests -- # CHECK-ASEXEC-INTREE-NEXT: exec-discovery-in-tree-suite :: test-one -# CHECK-ASEXEC-INTREE: -- Testing: 0 diff --git a/utils/lit/tests/googletest-format.py b/utils/lit/tests/googletest-format.py new file mode 100644 index 0000000..a62fd1b --- /dev/null +++ b/utils/lit/tests/googletest-format.py @@ -0,0 +1,20 @@ +# Check the various features of the GoogleTest format. +# +# RUN: not %{lit} -j 1 -v %{inputs}/googletest-format > %t.out +# RUN: FileCheck < %t.out %s +# +# END. + +# CHECK: -- Testing: +# CHECK: PASS: googletest-format :: DummySubDir/OneTest/FirstTest.subTestA +# CHECK: FAIL: googletest-format :: DummySubDir/OneTest/FirstTest.subTestB +# CHECK-NEXT: *** TEST 'googletest-format :: DummySubDir/OneTest/FirstTest.subTestB' FAILED *** +# CHECK-NEXT: I am subTest B, I FAIL +# CHECK-NEXT: And I have two lines of output +# CHECK: *** +# CHECK: PASS: googletest-format :: DummySubDir/OneTest/ParameterizedTest/0.subTest +# CHECK: PASS: googletest-format :: DummySubDir/OneTest/ParameterizedTest/1.subTest +# CHECK: Failing Tests (1) +# CHECK: Expected Passes : 3 +# CHECK: Unexpected Failures: 1 + diff --git a/utils/lit/tests/lit.cfg b/utils/lit/tests/lit.cfg index 32760ce..2111b72 100644 --- a/utils/lit/tests/lit.cfg +++ b/utils/lit/tests/lit.cfg @@ -1,6 +1,9 @@ # -*- Python -*- import os +import sys + +import lit.formats # Configuration file for the 'lit' test runner. @@ -20,17 +23,23 @@ config.excludes = ['Inputs'] config.test_source_root = os.path.dirname(__file__) config.test_exec_root = config.test_source_root -config.target_triple = None +config.target_triple = '(unused)' src_root = os.path.join(config.test_source_root, '..') config.environment['PYTHONPATH'] = src_root config.substitutions.append(('%{src_root}', src_root)) config.substitutions.append(('%{inputs}', os.path.join( src_root, 'tests', 'Inputs'))) -config.substitutions.append(('%{lit}', os.path.join(src_root, 'lit.py'))) +config.substitutions.append(('%{lit}', "%%{python} %s" % ( + os.path.join(src_root, 'lit.py'),))) +config.substitutions.append(('%{python}', sys.executable)) # Enable coverage.py reporting, assuming the coverage module has been installed # and sitecustomize.py in the virtualenv has been modified appropriately. -if lit.params.get('check-coverage', None): +if lit_config.params.get('check-coverage', None): config.environment['COVERAGE_PROCESS_START'] = os.path.join( os.path.dirname(__file__), ".coveragerc") + +# Add a feature to detect the Python version. +config.available_features.add("python%d.%d" % (sys.version_info[0], + sys.version_info[1])) diff --git a/utils/lit/tests/shell-parsing.py b/utils/lit/tests/shell-parsing.py index f644132..a07e988 100644 --- a/utils/lit/tests/shell-parsing.py +++ b/utils/lit/tests/shell-parsing.py @@ -1,3 +1,3 @@ # Just run the ShUtil unit tests. # -# RUN: python -m lit.ShUtil +# RUN: %{python} -m lit.ShUtil diff --git a/utils/lit/tests/shtest-encoding.py b/utils/lit/tests/shtest-encoding.py new file mode 100644 index 0000000..dfc987f --- /dev/null +++ b/utils/lit/tests/shtest-encoding.py @@ -0,0 +1,3 @@ +# RUN: true + +# Here is a string that cannot be decoded in line mode: Â. diff --git a/utils/lit/tests/shtest-format.py b/utils/lit/tests/shtest-format.py index 4b36873..751f0d7 100644 --- a/utils/lit/tests/shtest-format.py +++ b/utils/lit/tests/shtest-format.py @@ -7,15 +7,43 @@ # CHECK: -- Testing: +# CHECK: PASS: shtest-format :: argv0.txt # CHECK: FAIL: shtest-format :: external_shell/fail.txt -# CHECK: *** TEST 'shtest-format :: external_shell/fail.txt' FAILED *** +# CHECK-NEXT: *** TEST 'shtest-format :: external_shell/fail.txt' FAILED *** +# CHECK: Command Output (stdout): +# CHECK-NEXT: -- +# CHECK-NEXT: line 1: failed test output on stdout +# CHECK-NEXT: line 2: failed test output on stdout # CHECK: Command Output (stderr): -# CHECK: cat: does-not-exist: No such file or directory +# CHECK-NEXT: -- +# CHECK-NEXT: cat: does-not-exist: No such file or directory +# CHECK: -- + +# CHECK: FAIL: shtest-format :: external_shell/fail_with_bad_encoding.txt +# CHECK-NEXT: *** TEST 'shtest-format :: external_shell/fail_with_bad_encoding.txt' FAILED *** +# CHECK: Command Output (stdout): +# CHECK-NEXT: -- +# CHECK-NEXT: a line with bad encoding: # CHECK: -- # CHECK: PASS: shtest-format :: external_shell/pass.txt # CHECK: FAIL: shtest-format :: fail.txt +# CHECK-NEXT: *** TEST 'shtest-format :: fail.txt' FAILED *** +# CHECK-NEXT: Script: +# CHECK-NEXT: -- +# CHECK-NEXT: printf "line 1 +# CHECK-NEXT: false +# CHECK-NEXT: -- +# CHECK-NEXT: Exit Code: 1 +# +# CHECK: Command Output (stdout): +# CHECK-NEXT: -- +# CHECK-NEXT: Command 0: "printf" +# CHECK-NEXT: Command 0 Result: 0 +# CHECK-NEXT: Command 0 Output: +# CHECK-NEXT: line 1: failed test output on stdout +# CHECK-NEXT: line 2: failed test output on stdout # CHECK: UNRESOLVED: shtest-format :: no-test-line.txt # CHECK: PASS: shtest-format :: pass.txt @@ -26,18 +54,24 @@ # CHECK: XFAIL: shtest-format :: xfail-target.txt # CHECK: XFAIL: shtest-format :: xfail.txt # CHECK: XPASS: shtest-format :: xpass.txt +# CHECK-NEXT: *** TEST 'shtest-format :: xpass.txt' FAILED *** +# CHECK-NEXT: Script +# CHECK-NEXT: -- +# CHECK-NEXT: true +# CHECK-NEXT: -- # CHECK: Testing Time # CHECK: Unexpected Passing Tests (1) # CHECK: shtest-format :: xpass.txt -# CHECK: Failing Tests (2) +# CHECK: Failing Tests (3) # CHECK: shtest-format :: external_shell/fail.txt +# CHECK: shtest-format :: external_shell/fail_with_bad_encoding.txt # CHECK: shtest-format :: fail.txt -# CHECK: Expected Passes : 3 +# CHECK: Expected Passes : 4 # CHECK: Expected Failures : 3 # CHECK: Unsupported Tests : 2 # CHECK: Unresolved Tests : 1 # CHECK: Unexpected Passes : 1 -# CHECK: Unexpected Failures: 2 +# CHECK: Unexpected Failures: 3 diff --git a/utils/lit/tests/test-data.py b/utils/lit/tests/test-data.py new file mode 100644 index 0000000..54909d7 --- /dev/null +++ b/utils/lit/tests/test-data.py @@ -0,0 +1,12 @@ +# Test features related to formats which support reporting additional test data. + +# RUN: %{lit} -j 1 -v %{inputs}/test-data > %t.out +# RUN: FileCheck < %t.out %s + +# CHECK: -- Testing: + +# CHECK: PASS: test-data :: metrics.ini +# CHECK-NEXT: *** TEST 'test-data :: metrics.ini' RESULTS *** +# CHECK-NEXT: value0: 1 +# CHECK-NEXT: value1: 2.3456 +# CHECK-NEXT: *** diff --git a/utils/lit/tests/test-output.py b/utils/lit/tests/test-output.py new file mode 100644 index 0000000..adfbcd8 --- /dev/null +++ b/utils/lit/tests/test-output.py @@ -0,0 +1,21 @@ +# XFAIL: python2.5 + +# RUN: %{lit} -j 1 -v %{inputs}/test-data --output %t.results.out > %t.out +# RUN: FileCheck < %t.results.out %s + +# CHECK: { +# CHECK: "__version__" +# CHECK: "elapsed" +# CHECK-NEXT: "tests": [ +# CHECK-NEXT: { +# CHECK-NEXT: "code": "PASS", +# CHECK-NEXT: "elapsed": {{[0-9.]+}}, +# CHECK-NEXT: "metrics": { +# CHECK-NEXT: "value0": 1, +# CHECK-NEXT: "value1": 2.3456 +# CHECK-NEXT: } +# CHECK-NEXT: "name": "test-data :: metrics.ini", +# CHECK-NEXT: "output": "Test passed." +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: } diff --git a/utils/lit/tests/unittest-adaptor.py b/utils/lit/tests/unittest-adaptor.py index 243dd41..7435dda 100644 --- a/utils/lit/tests/unittest-adaptor.py +++ b/utils/lit/tests/unittest-adaptor.py @@ -1,6 +1,6 @@ # Check the lit adaption to run under unittest. # -# RUN: python %s %{inputs}/unittest-adaptor 2> %t.err +# RUN: %{python} %s %{inputs}/unittest-adaptor 2> %t.err # RUN: FileCheck < %t.err %s # # CHECK: unittest-adaptor :: test-one.txt ... ok diff --git a/utils/llvm-build/llvmbuild/__init__.py b/utils/llvm-build/llvmbuild/__init__.py index 7760218..eb20d09 100644 --- a/utils/llvm-build/llvmbuild/__init__.py +++ b/utils/llvm-build/llvmbuild/__init__.py @@ -1 +1 @@ -from main import main +from llvmbuild.main import main diff --git a/utils/llvm-build/llvmbuild/componentinfo.py b/utils/llvm-build/llvmbuild/componentinfo.py index e684ac2..eda3a48 100644 --- a/utils/llvm-build/llvmbuild/componentinfo.py +++ b/utils/llvm-build/llvmbuild/componentinfo.py @@ -2,11 +2,14 @@ Descriptor objects for entities that are part of the LLVM project. """ -import ConfigParser -import StringIO +from __future__ import absolute_import +try: + import configparser +except: + import ConfigParser as configparser import sys -from util import * +from llvmbuild.util import * class ParseError(Exception): pass @@ -29,7 +32,7 @@ class ComponentInfo(object): def __init__(self, subpath, name, dependencies, parent): if not subpath.startswith('/'): - raise ValueError,"invalid subpath: %r" % subpath + raise ValueError("invalid subpath: %r" % subpath) self.subpath = subpath self.name = name self.dependencies = list(dependencies) @@ -100,11 +103,11 @@ class GroupComponentInfo(ComponentInfo): ComponentInfo.__init__(self, subpath, name, [], parent) def get_llvmbuild_fragment(self): - result = StringIO.StringIO() - print >>result, 'type = %s' % self.type_name - print >>result, 'name = %s' % self.name - print >>result, 'parent = %s' % self.parent - return result.getvalue() + return """\ +type = %s +name = %s +parent = %s +""" % (self.type_name, self.name, self.parent) class LibraryComponentInfo(ComponentInfo): type_name = 'Library' @@ -152,21 +155,22 @@ class LibraryComponentInfo(ComponentInfo): yield ('library group', r) def get_llvmbuild_fragment(self): - result = StringIO.StringIO() - print >>result, 'type = %s' % self.type_name - print >>result, 'name = %s' % self.name - print >>result, 'parent = %s' % self.parent + result = """\ +type = %s +name = %s +parent = %s +""" % (self.type_name, self.name, self.parent) if self.library_name is not None: - print >>result, 'library_name = %s' % self.library_name + result += 'library_name = %s\n' % self.library_name if self.required_libraries: - print >>result, 'required_libraries = %s' % ' '.join( + result += 'required_libraries = %s\n' % ' '.join( self.required_libraries) if self.add_to_library_groups: - print >>result, 'add_to_library_groups = %s' % ' '.join( + result += 'add_to_library_groups = %s\n' % ' '.join( self.add_to_library_groups) if not self.installed: - print >>result, 'installed = 0' - return result.getvalue() + result += 'installed = 0\n' + return result def get_library_name(self): return self.library_name or self.name @@ -237,17 +241,18 @@ class LibraryGroupComponentInfo(ComponentInfo): yield ('library group', r) def get_llvmbuild_fragment(self): - result = StringIO.StringIO() - print >>result, 'type = %s' % self.type_name - print >>result, 'name = %s' % self.name - print >>result, 'parent = %s' % self.parent + result = """\ +type = %s +name = %s +parent = %s +""" % (self.type_name, self.name, self.parent) if self.required_libraries and not self._is_special_group: - print >>result, 'required_libraries = %s' % ' '.join( + result += 'required_libraries = %s\n' % ' '.join( self.required_libraries) if self.add_to_library_groups: - print >>result, 'add_to_library_groups = %s' % ' '.join( + result += 'add_to_library_groups = %s\n' % ' '.join( self.add_to_library_groups) - return result.getvalue() + return result def get_llvmconfig_component_name(self): return self.name.lower() @@ -309,21 +314,22 @@ class TargetGroupComponentInfo(ComponentInfo): yield ('library group', r) def get_llvmbuild_fragment(self): - result = StringIO.StringIO() - print >>result, 'type = %s' % self.type_name - print >>result, 'name = %s' % self.name - print >>result, 'parent = %s' % self.parent + result = """\ +type = %s +name = %s +parent = %s +""" % (self.type_name, self.name, self.parent) if self.required_libraries: - print >>result, 'required_libraries = %s' % ' '.join( + result += 'required_libraries = %s\n' % ' '.join( self.required_libraries) if self.add_to_library_groups: - print >>result, 'add_to_library_groups = %s' % ' '.join( + result += 'add_to_library_groups = %s\n' % ' '.join( self.add_to_library_groups) for bool_key in ('has_asmparser', 'has_asmprinter', 'has_disassembler', 'has_jit'): if getattr(self, bool_key): - print >>result, '%s = 1' % (bool_key,) - return result.getvalue() + result += '%s = 1\n' % (bool_key,) + return result def get_llvmconfig_component_name(self): return self.name.lower() @@ -352,13 +358,13 @@ class ToolComponentInfo(ComponentInfo): yield ('required library', r) def get_llvmbuild_fragment(self): - result = StringIO.StringIO() - print >>result, 'type = %s' % self.type_name - print >>result, 'name = %s' % self.name - print >>result, 'parent = %s' % self.parent - print >>result, 'required_libraries = %s' % ' '.join( - self.required_libraries) - return result.getvalue() + return """\ +type = %s +name = %s +parent = %s +required_libraries = %s +""" % (self.type_name, self.name, self.parent, + ' '.join(self.required_libraries)) class BuildToolComponentInfo(ToolComponentInfo): type_name = 'BuildTool' @@ -418,7 +424,7 @@ _component_type_map = dict( TargetGroupComponentInfo, OptionalLibraryComponentInfo)) def load_from_path(path, subpath): # Load the LLVMBuild.txt file as an .ini format file. - parser = ConfigParser.RawConfigParser() + parser = configparser.RawConfigParser() parser.read(path) # Extract the common section. @@ -459,8 +465,9 @@ def _read_components_from_parser(parser, path, subpath): section, path, "unable to instantiate: %r" % type_name) import traceback traceback.print_exc() - raise SystemExit, 1 - except ParseError,e: + raise SystemExit(1) + except ParseError: + e = sys.exc_info()[1] fatal("unable to load component %r in %r: %s" % ( section, path, e.message)) diff --git a/utils/llvm-build/llvmbuild/main.py b/utils/llvm-build/llvmbuild/main.py index 87e8819..eacefdf 100644 --- a/utils/llvm-build/llvmbuild/main.py +++ b/utils/llvm-build/llvmbuild/main.py @@ -1,11 +1,11 @@ -import StringIO +from __future__ import absolute_import import os import sys -import componentinfo -import configutil +import llvmbuild.componentinfo as componentinfo +import llvmbuild.configutil as configutil -from util import * +from llvmbuild.util import * ### @@ -186,7 +186,7 @@ class LLVMProjectInfo(object): set(self.component_infos), key = lambda c: c.name) while components_to_visit: - visit_component_info(iter(components_to_visit).next(), [], set()) + visit_component_info(components_to_visit[0], [], set()) # Canonicalize children lists. for c in self.ordered_component_infos: @@ -194,7 +194,7 @@ class LLVMProjectInfo(object): def print_tree(self): def visit(node, depth = 0): - print '%s%-40s (%s)' % (' '*depth, node.name, node.type_name) + print('%s%-40s (%s)' % (' '*depth, node.name, node.type_name)) for c in node.children: visit(c, depth + 1) visit(self.component_info_map['$ROOT']) @@ -283,7 +283,7 @@ subdirectories = %s header_name = '.' + os.path.join(subpath, 'LLVMBuild.txt') header_pad = '-' * (80 - len(header_fmt % (header_name, ''))) header_string = header_fmt % (header_name, header_pad) - print >>f, """\ + f.write("""\ %s ; ; The LLVM Compiler Infrastructure @@ -300,17 +300,18 @@ subdirectories = %s ; http://llvm.org/docs/LLVMBuild.html ; ;===------------------------------------------------------------------------===; -""" % header_string + +""" % header_string) # Write out each fragment.each component fragment. for name,fragment in fragments: comment = comments_map.get(name) if comment is not None: f.write(comment) - print >>f, "[%s]" % name + f.write("[%s]\n" % name) f.write(fragment) if fragment is not fragments[-1][1]: - print >>f + f.write('\n') f.close() @@ -363,7 +364,7 @@ subdirectories = %s is_installed) # Convert to a list of entries and sort by name. - entries = entries.values() + entries = list(entries.values()) # Create an 'all' pseudo component. We keep the dependency list small by # only listing entries that have no other dependents. @@ -382,7 +383,7 @@ subdirectories = %s # Write out the library table. make_install_dir(os.path.dirname(output_path)) f = open(output_path, 'w') - print >>f, """\ + f.write("""\ //===- llvm-build generated file --------------------------------*- C++ -*-===// // // Component Library Depenedency Table @@ -390,32 +391,33 @@ subdirectories = %s // Automatically generated file, do not edit! // //===----------------------------------------------------------------------===// -""" - print >>f, 'struct AvailableComponent {' - print >>f, ' /// The name of the component.' - print >>f, ' const char *Name;' - print >>f, '' - print >>f, ' /// The name of the library for this component (or NULL).' - print >>f, ' const char *Library;' - print >>f, '' - print >>f, ' /// Whether the component is installed.' - print >>f, ' bool IsInstalled;' - print >>f, '' - print >>f, '\ - /// The list of libraries required when linking this component.' - print >>f, ' const char *RequiredLibraries[%d];' % ( - max_required_libraries) - print >>f, '} AvailableComponents[%d] = {' % len(entries) + +""") + f.write('struct AvailableComponent {\n') + f.write(' /// The name of the component.\n') + f.write(' const char *Name;\n') + f.write('\n') + f.write(' /// The name of the library for this component (or NULL).\n') + f.write(' const char *Library;\n') + f.write('\n') + f.write(' /// Whether the component is installed.\n') + f.write(' bool IsInstalled;\n') + f.write('\n') + f.write('\ + /// The list of libraries required when linking this component.\n') + f.write(' const char *RequiredLibraries[%d];\n' % ( + max_required_libraries)) + f.write('} AvailableComponents[%d] = {\n' % len(entries)) for name,library_name,required_names,is_installed in entries: if library_name is None: library_name_as_cstr = '0' else: library_name_as_cstr = '"lib%s.a"' % library_name - print >>f, ' { "%s", %s, %d, { %s } },' % ( + f.write(' { "%s", %s, %d, { %s } },\n' % ( name, library_name_as_cstr, is_installed, ', '.join('"%s"' % dep - for dep in required_names)) - print >>f, '};' + for dep in required_names))) + f.write('};\n') f.close() def get_required_libraries_for_component(self, ci, traverse_groups = False): @@ -512,7 +514,7 @@ subdirectories = %s header_name = os.path.basename(output_path) header_pad = '-' * (80 - len(header_fmt % (header_name, ''))) header_string = header_fmt % (header_name, header_pad) - print >>f, """\ + f.write("""\ %s # # The LLVM Compiler Infrastructure @@ -528,10 +530,11 @@ subdirectories = %s # This file is autogenerated by llvm-build, do not edit! # #===------------------------------------------------------------------------===# -""" % header_string + +""" % header_string) # Write the dependency information in the best way we can. - print >>f, """ + f.write(""" # LLVMBuild CMake fragment dependencies. # # CMake has no builtin way to declare that the configuration depends on @@ -541,30 +544,32 @@ subdirectories = %s # CMake. # # FIXME: File a CMake RFE to get a properly supported version of this -# feature.""" +# feature. +""") for dep in dependencies: - print >>f, """\ + f.write("""\ configure_file(\"%s\" - ${CMAKE_CURRENT_BINARY_DIR}/DummyConfigureOutput)""" % ( - cmake_quote_path(dep),) + ${CMAKE_CURRENT_BINARY_DIR}/DummyConfigureOutput)\n""" % ( + cmake_quote_path(dep),)) # Write the properties we use to encode the required library dependency # information in a form CMake can easily use directly. - print >>f, """ + f.write(""" # Explicit library dependency information. # # The following property assignments effectively create a map from component -# names to required libraries, in a way that is easily accessed from CMake.""" +# names to required libraries, in a way that is easily accessed from CMake. +""") for ci in self.ordered_component_infos: # We only write the information for libraries currently. if ci.type_name != 'Library': continue - print >>f, """\ -set_property(GLOBAL PROPERTY LLVMBUILD_LIB_DEPS_%s %s)""" % ( + f.write("""\ +set_property(GLOBAL PROPERTY LLVMBUILD_LIB_DEPS_%s %s)\n""" % ( ci.get_prefixed_library_name(), " ".join(sorted( dep.get_prefixed_library_name() - for dep in self.get_required_libraries_for_component(ci)))) + for dep in self.get_required_libraries_for_component(ci))))) f.close() @@ -590,7 +595,7 @@ set_property(GLOBAL PROPERTY LLVMBUILD_LIB_DEPS_%s %s)""" % ( header_name = os.path.basename(output_path) header_pad = '-' * (80 - len(header_fmt % (header_name, ''))) header_string = header_fmt % (header_name, header_pad) - print >>f, """\ + f.write("""\ %s # # The LLVM Compiler Infrastructure @@ -606,30 +611,33 @@ set_property(GLOBAL PROPERTY LLVMBUILD_LIB_DEPS_%s %s)""" % ( # This file is autogenerated by llvm-build, do not edit! # #===------------------------------------------------------------------------===# -""" % header_string + +""" % header_string) # Write the dependencies for the fragment. # # FIXME: Technically, we need to properly quote for Make here. - print >>f, """\ + f.write("""\ # Clients must explicitly enable LLVMBUILD_INCLUDE_DEPENDENCIES to get # these dependencies. This is a compromise to help improve the -# performance of recursive Make systems.""" - print >>f, 'ifeq ($(LLVMBUILD_INCLUDE_DEPENDENCIES),1)' - print >>f, "# The dependencies for this Makefile fragment itself." - print >>f, "%s: \\" % (mk_quote_string_for_target(output_path),) +# performance of recursive Make systems. +""") + f.write('ifeq ($(LLVMBUILD_INCLUDE_DEPENDENCIES),1)\n') + f.write("# The dependencies for this Makefile fragment itself.\n") + f.write("%s: \\\n" % (mk_quote_string_for_target(output_path),)) for dep in dependencies: - print >>f, "\t%s \\" % (dep,) - print >>f + f.write("\t%s \\\n" % (dep,)) + f.write('\n') # Generate dummy rules for each of the dependencies, so that things # continue to work correctly if any of those files are moved or removed. - print >>f, """\ + f.write("""\ # The dummy targets to allow proper regeneration even when files are moved or -# removed.""" +# removed. +""") for dep in dependencies: - print >>f, "%s:" % (mk_quote_string_for_target(dep),) - print >>f, 'endif' + f.write("%s:\n" % (mk_quote_string_for_target(dep),)) + f.write('endif\n') f.close() @@ -801,7 +809,7 @@ given by --build-root) at the same SUBPATH""", dest="optional_components", metavar="NAMES", help=("Enable the given space or semi-colon separated " "list of optional components"), - action="store", default=None) + action="store", default="") parser.add_option_group(group) (opts, args) = parser.parse_args() diff --git a/utils/llvm-build/llvmbuild/util.py b/utils/llvm-build/llvmbuild/util.py index e581af2..ca021c4 100644 --- a/utils/llvm-build/llvmbuild/util.py +++ b/utils/llvm-build/llvmbuild/util.py @@ -3,7 +3,7 @@ import sys def _write_message(kind, message): program = os.path.basename(sys.argv[0]) - print >>sys.stderr, '%s: %s: %s' % (program, kind, message) + sys.stderr.write('%s: %s: %s\n' % (program, kind, message)) note = lambda message: _write_message('note', message) warning = lambda message: _write_message('warning', message) diff --git a/utils/llvm-lit/CMakeLists.txt b/utils/llvm-lit/CMakeLists.txt index 602cc88..b535eae 100644 --- a/utils/llvm-lit/CMakeLists.txt +++ b/utils/llvm-lit/CMakeLists.txt @@ -2,11 +2,3 @@ configure_file( llvm-lit.in ${LLVM_TOOLS_BINARY_DIR}/llvm-lit ) - -install(FILES - ${LLVM_TOOLS_BINARY_DIR}/llvm-lit - DESTINATION bin - PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE - GROUP_READ GROUP_EXECUTE - WORLD_READ WORLD_EXECUTE - ) diff --git a/utils/not/CMakeLists.txt b/utils/not/CMakeLists.txt index f4c0229..5ff14d6 100644 --- a/utils/not/CMakeLists.txt +++ b/utils/not/CMakeLists.txt @@ -4,7 +4,7 @@ add_llvm_utility(not target_link_libraries(not LLVMSupport) if( MINGW ) - target_link_libraries(not imagehlp psapi) + target_link_libraries(not imagehlp psapi shell32) endif( MINGW ) if( LLVM_ENABLE_THREADS AND HAVE_LIBPTHREAD ) target_link_libraries(not pthread) diff --git a/utils/profile.pl b/utils/profile.pl deleted file mode 100755 index 782e5dc..0000000 --- a/utils/profile.pl +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/perl -w -# -# Program: profile.pl -# -# Synopsis: Insert instrumentation code into a program, run it with the JIT, -# then print out a profile report. -# -# Syntax: profile.pl [OPTIONS] bitcodefile <arguments> -# -# OPTIONS may include one or more of the following: -# -block - Enable basicblock profiling -# -edge - Enable edge profiling -# -function - Enable function profiling -# -o <filename> - Emit profiling information to the specified file, instead -# of llvmprof.out -# -# Any unrecognized options are passed into the invocation of llvm-prof -# - -my $ProfilePass = "-insert-edge-profiling"; - -my $LLVMProfOpts = ""; -my $ProgramOpts = ""; -my $ProfileFile = ""; - -# Parse arguments... -while (scalar(@ARGV) and ($_ = $ARGV[0], /^[-+]/)) { - shift; - last if /^--$/; # Stop processing arguments on -- - - # List command line options here... - if (/^-?-block$/) { $ProfilePass = "-insert-block-profiling"; next; } - if (/^-?-edge$/) { $ProfilePass = "-insert-edge-profiling"; next; } - if (/^-?-function$/) { $ProfilePass = "-insert-function-profiling"; next; } - if (/^-?-o$/) { # Read -o filename... - die "-o option requires a filename argument!" if (!scalar(@ARGV)); - $ProgramOpts .= " -llvmprof-output $ARGV[0]"; - $ProfileFile = $ARGV[0]; - shift; - next; - } - if (/^-?-help$/) { - print "OVERVIEW: profile.pl - Instrumentation and profile printer.\n\n"; - print "USAGE: profile.pl [options] program.bc <program args>\n\n"; - print "OPTIONS:\n"; - print " -block - Enable basicblock profiling\n"; - print " -edge - Enable edge profiling\n"; - print " -function - Enable function profiling\n"; - print " -o <file> - Specify an output file other than llvm-prof.out.\n"; - print " -help - Print this usage information\n"; - print "\nAll other options are passed into llvm-prof.\n"; - exit 1; - } - - # Otherwise, pass the option on to llvm-prof - $LLVMProfOpts .= " " . $_; -} - -die "Must specify LLVM bitcode file as first argument!" if (@ARGV == 0); - -my $BytecodeFile = $ARGV[0]; - -shift @ARGV; - -my $libdir = `llvm-config --libdir`; -chomp $libdir; - -my $LibProfPath = $libdir . "/libprofile_rt.so"; - -system "opt -q -f $ProfilePass $BytecodeFile -o $BytecodeFile.inst"; -system "lli -fake-argv0 '$BytecodeFile' -load $LibProfPath " . - "$BytecodeFile.inst $ProgramOpts " . (join ' ', @ARGV); -system "rm $BytecodeFile.inst"; -system "llvm-prof $LLVMProfOpts $BytecodeFile $ProfileFile"; diff --git a/utils/release/tag.sh b/utils/release/tag.sh index 5e3f69a..6c5039d 100755 --- a/utils/release/tag.sh +++ b/utils/release/tag.sh @@ -17,7 +17,7 @@ set -e release="" rc="" rebranch="no" -projects="llvm cfe dragonegg test-suite compiler-rt libcxx clang-tools-extra polly lldb" +projects="llvm cfe dragonegg test-suite compiler-rt libcxx clang-tools-extra polly lldb lld" base_url="https://llvm.org/svn/llvm-project" diff --git a/utils/release/test-release.sh b/utils/release/test-release.sh index 6c17b46..91dac4c 100755 --- a/utils/release/test-release.sh +++ b/utils/release/test-release.sh @@ -26,6 +26,8 @@ Base_url="http://llvm.org/svn/llvm-project" Release="" Release_no_dot="" RC="" +Triple="" +use_gzip="no" do_checkout="yes" do_ada="no" do_clang="yes" @@ -44,6 +46,7 @@ function usage() { echo " -release X.Y The release number to test." echo " -rc NUM The pre-release candidate number." echo " -final The final release candidate." + echo " -triple TRIPLE The target triple for this machine." echo " -j NUM Number of compile jobs to run. [default: 3]" echo " -build-dir DIR Directory to perform testing in. [default: pwd]" echo " -no-checkout Don't checkout the sources from SVN." @@ -56,6 +59,7 @@ function usage() { echo " -test-debug Test the debug build. [default: no]" echo " -test-asserts Test with asserts on. [default: no]" echo " -no-compare-files Don't test that phase 2 and 3 files are identical." + echo " -use-gzip Use gzip instead of xz." } while [ $# -gt 0 ]; do @@ -72,6 +76,10 @@ while [ $# -gt 0 ]; do -final | --final ) RC=final ;; + -triple | --triple ) + shift + Triple="$1" + ;; -j* ) NumJobs="`echo $1 | sed -e 's,-j\([0-9]*\),\1,g'`" if [ -z "$NumJobs" ]; then @@ -113,6 +121,9 @@ while [ $# -gt 0 ]; do -no-compare-files | --no-compare-files ) do_compare="no" ;; + -use-gzip | --use-gzip ) + use_gzip="yes" + ;; -help | --help | -h | --h | -\? ) usage exit 0 @@ -135,6 +146,10 @@ if [ -z "$RC" ]; then echo "error: no release candidate number specified" exit 1 fi +if [ -z "$Triple" ]; then + echo "error: no target triple specified" + exit 1 +fi # Figure out how many make processes to run. if [ -z "$NumJobs" ]; then @@ -159,6 +174,13 @@ cd $BuildDir LogDir=$BuildDir/logs mkdir -p $LogDir +# Final package name. +Package=clang+llvm-$Release +if [ $RC != "final" ]; then + Package=$Package-$RC +fi +Package=$Package-$Triple + # Find compilers. if [ "$do_dragonegg" = "yes" ]; then gcc_compiler="$GCC" @@ -180,6 +202,20 @@ if [ "$do_dragonegg" = "yes" ]; then fi fi +# Make sure that a required program is available +function check_program_exists() { + local program="$1" + if ! type -P $program > /dev/null 2>&1 ; then + echo "program '$1' not found !" + exit 1 + fi +} + +if [ `uname -s` != "Darwin" ]; then + check_program_exists 'chrpath' + check_program_exists 'file' + check_program_exists 'objdump' +fi # Make sure that the URLs are valid. function check_valid_urls() { @@ -328,6 +364,38 @@ function test_llvmCore() { cd $BuildDir } +# Clean RPATH. Libtool adds the build directory to the search path, which is +# not necessary --- and even harmful --- for the binary packages we release. +function clean_RPATH() { + if [ `uname -s` = "Darwin" ]; then + return + fi + local InstallPath="$1" + for Candidate in `find $InstallPath/{bin,lib} -type f`; do + if file $Candidate | grep ELF | egrep 'executable|shared object' > /dev/null 2>&1 ; then + rpath=`objdump -x $Candidate | grep 'RPATH' | sed -e's/^ *RPATH *//'` + if [ -n "$rpath" ]; then + newrpath=`echo $rpath | sed -e's/.*\(\$ORIGIN[^:]*\).*/\1/'` + chrpath -r $newrpath $Candidate 2>&1 > /dev/null 2>&1 + fi + fi + done +} + +# Create a package of the release binaries. +function package_release() { + cwd=`pwd` + cd $BuildDir/Phase3/Release + mv llvmCore-$Release-$RC.install $Package + if [ "$use_gzip" = "yes" ]; then + tar cfz $BuildDir/$Package.tar.gz $Package + else + tar cfJ $BuildDir/$Package.tar.xz $Package + fi + mv $Package llvmCore-$Release-$RC.install + cd $cwd +} + set -e # Exit if any command fails if [ "$do_checkout" = "yes" ]; then @@ -415,6 +483,7 @@ for Flavor in $Flavors ; do $llvmCore_phase1_objdir $llvmCore_phase1_installdir build_llvmCore 1 $Flavor \ $llvmCore_phase1_objdir + clean_RPATH $llvmCore_phase1_installdir # Test clang if [ "$do_clang" = "yes" ]; then @@ -427,6 +496,7 @@ for Flavor in $Flavors ; do $llvmCore_phase2_objdir $llvmCore_phase2_installdir build_llvmCore 2 $Flavor \ $llvmCore_phase2_objdir + clean_RPATH $llvmCore_phase2_installdir ######################################################################## # Phase 3: Build llvmCore with newly built clang from phase 2. @@ -437,6 +507,7 @@ for Flavor in $Flavors ; do $llvmCore_phase3_objdir $llvmCore_phase3_installdir build_llvmCore 3 $Flavor \ $llvmCore_phase3_objdir + clean_RPATH $llvmCore_phase3_installdir ######################################################################## # Testing: Test phase 3 @@ -478,6 +549,7 @@ for Flavor in $Flavors ; do build_llvmCore 2 $Flavor \ $llvmCore_de_phase2_objdir build_dragonegg 2 $Flavor $llvmCore_de_phase2_installdir $dragonegg_phase2_objdir + clean_RPATH $llvmCore_de_phase2_installdir ######################################################################## # Phase 3: Build llvmCore with newly built dragonegg from phase 2. @@ -489,6 +561,7 @@ for Flavor in $Flavors ; do build_llvmCore 3 $Flavor \ $llvmCore_de_phase3_objdir build_dragonegg 3 $Flavor $llvmCore_de_phase3_installdir $dragonegg_phase3_objdir + clean_RPATH $llvmCore_de_phase3_installdir ######################################################################## # Testing: Test phase 3 @@ -518,9 +591,16 @@ for Flavor in $Flavors ; do done ) 2>&1 | tee $LogDir/testing.$Release-$RC.log +package_release + set +e # Woo hoo! echo "### Testing Finished ###" +if [ "$use_gzip" = "yes" ]; then + echo "### Package: $Package.tar.gz" +else + echo "### Package: $Package.tar.xz" +fi echo "### Logs: $LogDir" exit 0 diff --git a/utils/test_debuginfo.pl b/utils/test_debuginfo.pl index 0832a33..a6b6137 100755 --- a/utils/test_debuginfo.pl +++ b/utils/test_debuginfo.pl @@ -11,8 +11,13 @@ # and run the script generated from source program comments. Finally, # the debugger output is checked, using FileCheck, to validate # debugging information. +# +# On Darwin the default is to use the llgdb.py wrapper script which +# translates gdb commands into their lldb equivalents. use File::Basename; +use Config; +use Cwd; my $testcase_file = $ARGV[0]; my $executable_file = $ARGV[1]; @@ -23,6 +28,11 @@ my $output_dir = dirname $executable_file; my $debugger_script_file = "$output_dir/$input_filename.debugger.script"; my $output_file = "$output_dir/$input_filename.gdb.output"; +my %cmd_map = (); +# Assume lldb to be the debugger on Darwin. +my $use_lldb = 0; +$use_lldb = 1 if ($Config{osname} eq "darwin"); + # Extract debugger commands from testcase. They are marked with DEBUGGER: # at the beginning of a comment line. open(INPUT, $testcase_file); @@ -44,8 +54,15 @@ close(OUTPUT); # setup debugger and debugger options to run a script. my $my_debugger = $ENV{'DEBUGGER'}; if (!$my_debugger) { - $my_debugger = "gdb"; + if ($use_lldb) { + my $path = dirname(Cwd::abs_path($0)); + $my_debugger = "/usr/bin/env python $path/../tools/clang/test/debuginfo-tests/llgdb.py"; + } else { + $my_debugger = "gdb"; + } } + +# quiet / exit after cmdline / no init file / execute script my $debugger_options = "-q -batch -n -x"; # run debugger and capture output. diff --git a/utils/unittest/CMakeLists.txt b/utils/unittest/CMakeLists.txt index 8bdfee1..fd1a048 100644 --- a/utils/unittest/CMakeLists.txt +++ b/utils/unittest/CMakeLists.txt @@ -14,6 +14,7 @@ # Where gtest's .h files can be found. include_directories( googletest/include + googletest ) if(WIN32) @@ -38,13 +39,7 @@ if(MSVC AND MSVC_VERSION EQUAL 1700) endif () add_llvm_library(gtest - googletest/gtest.cc - googletest/gtest-death-test.cc - googletest/gtest-filepath.cc - googletest/gtest-port.cc - googletest/gtest-printers.cc - googletest/gtest-test-part.cc - googletest/gtest-typed-test.cc + googletest/src/gtest-all.cc ) add_llvm_library(gtest_main diff --git a/utils/unittest/googletest/Makefile b/utils/unittest/googletest/Makefile index bf73670..3d85e7d 100644 --- a/utils/unittest/googletest/Makefile +++ b/utils/unittest/googletest/Makefile @@ -19,6 +19,7 @@ REQUIRES_RTTI = 1 # unittests/Makefile.unittest and ../UnitTestMain/Makefile; ensure that any # changes are made to both. CPP.Flags += -I$(LLVM_SRC_ROOT)/utils/unittest/googletest/include +CPP.Flags += -I$(LLVM_SRC_ROOT)/utils/unittest/googletest CPP.Flags += $(NO_MISSING_FIELD_INITIALIZERS) $(NO_VARIADIC_MACROS) CPP.Flags += -DGTEST_HAS_RTTI=0 # libstdc++'s TR1 <tuple> header depends on RTTI and uses C++'0x features not @@ -36,6 +37,6 @@ endif NO_INSTALL = 1 -SOURCES = $(filter-out gtest-all.cc, $(notdir $(wildcard $(PROJ_SRC_DIR)/*.cc))) +SOURCES = src/gtest-all.cc include $(LEVEL)/Makefile.common diff --git a/utils/unittest/googletest/README.LLVM b/utils/unittest/googletest/README.LLVM index 3565a32..1a6f0f5 100644 --- a/utils/unittest/googletest/README.LLVM +++ b/utils/unittest/googletest/README.LLVM @@ -10,23 +10,11 @@ Cleaned up as follows: $ rm -f aclocal* CMakeLists.txt configure* Makefile* CHANGES CONTRIBUTORS README $ rm -rf build-aux cmake codegear fused-src m4 make msvc samples scripts test xcode $ rm -f `find . -name \*\.pump` +$ rm -f src/gtest_main.cc -# Move all the source files to the current directory -$ mv src/* . -$ rmdir src - -# Move extra headers into the already-existing internal headers dir -$ mv *.h include/gtest/internal/ - -# Update paths to the included files -$ perl -pi -e 's|^#include "src/|#include "|' gtest-all.cc -$ perl -pi -e 's|^#include "src/|#include "gtest/internal/|' *.cc - -$ rm -f gtest_main.cc - +# Put the license in the consistent place for LLVM. $ mv COPYING LICENSE.TXT - Modified as follows: * To GTestStreamToHelper in include/gtest/internal/gtest-internal.h, added the ability to stream with raw_os_ostream. diff --git a/utils/unittest/googletest/include/gtest/gtest-test-part.h b/utils/unittest/googletest/include/gtest/gtest-test-part.h index 8aeea14..98e8b84 100644 --- a/utils/unittest/googletest/include/gtest/gtest-test-part.h +++ b/utils/unittest/googletest/include/gtest/gtest-test-part.h @@ -142,7 +142,7 @@ class GTEST_API_ TestPartResultArray { // This interface knows how to report a test part result. class TestPartResultReporterInterface { public: - virtual ~TestPartResultReporterInterface() {} + virtual ~TestPartResultReporterInterface(); virtual void ReportTestPartResult(const TestPartResult& result) = 0; }; diff --git a/utils/unittest/googletest/include/gtest/gtest.h b/utils/unittest/googletest/include/gtest/gtest.h index 1734c44..07ed92b 100644 --- a/utils/unittest/googletest/include/gtest/gtest.h +++ b/utils/unittest/googletest/include/gtest/gtest.h @@ -910,7 +910,7 @@ class GTEST_API_ TestCase { class Environment { public: // The d'tor is virtual as we need to subclass Environment. - virtual ~Environment() {} + virtual ~Environment(); // Override this to define how to set up the environment. virtual void SetUp() {} @@ -928,7 +928,7 @@ class Environment { // the order the corresponding events are fired. class TestEventListener { public: - virtual ~TestEventListener() {} + virtual ~TestEventListener(); // Fired before any test activity starts. virtual void OnTestProgramStart(const UnitTest& unit_test) = 0; @@ -980,6 +980,7 @@ class TestEventListener { // comments about each method please see the definition of TestEventListener // above. class EmptyTestEventListener : public TestEventListener { + virtual void anchor(); public: virtual void OnTestProgramStart(const UnitTest& /*unit_test*/) {} virtual void OnTestIterationStart(const UnitTest& /*unit_test*/, diff --git a/utils/unittest/googletest/include/gtest/internal/gtest-death-test-internal.h b/utils/unittest/googletest/include/gtest/internal/gtest-death-test-internal.h index 7bac2bd..8d53c45 100644 --- a/utils/unittest/googletest/include/gtest/internal/gtest-death-test-internal.h +++ b/utils/unittest/googletest/include/gtest/internal/gtest-death-test-internal.h @@ -79,7 +79,7 @@ class GTEST_API_ DeathTest { static bool Create(const char* statement, const RE* regex, const char* file, int line, DeathTest** test); DeathTest(); - virtual ~DeathTest() { } + virtual ~DeathTest(); // A helper class that aborts a death test when it's deleted. class ReturnSentinel { @@ -139,7 +139,7 @@ class GTEST_API_ DeathTest { // Factory interface for death tests. May be mocked out for testing. class DeathTestFactory { public: - virtual ~DeathTestFactory() { } + virtual ~DeathTestFactory(); virtual bool Create(const char* statement, const RE* regex, const char* file, int line, DeathTest** test) = 0; }; diff --git a/utils/unittest/googletest/include/gtest/internal/gtest-internal.h b/utils/unittest/googletest/include/gtest/internal/gtest-internal.h index a94bf28..63f72ac 100644 --- a/utils/unittest/googletest/include/gtest/internal/gtest-internal.h +++ b/utils/unittest/googletest/include/gtest/internal/gtest-internal.h @@ -105,6 +105,7 @@ #if !GTEST_NO_LLVM_RAW_OSTREAM namespace llvm { class convertible_fwd_ostream : public std::ostream { + virtual void anchor(); raw_os_ostream ros_; public: @@ -536,7 +537,7 @@ GTEST_API_ TypeId GetTestTypeId(); // of a Test object. class TestFactoryBase { public: - virtual ~TestFactoryBase() {} + virtual ~TestFactoryBase(); // Creates a test instance to run. The instance is both created and destroyed // within TestInfoImpl::Run() diff --git a/utils/unittest/googletest/include/gtest/internal/gtest-param-util.h b/utils/unittest/googletest/include/gtest/internal/gtest-param-util.h index 0ef9718..3bb2ffb 100644 --- a/utils/unittest/googletest/include/gtest/internal/gtest-param-util.h +++ b/utils/unittest/googletest/include/gtest/internal/gtest-param-util.h @@ -414,7 +414,7 @@ class TestMetaFactory // and calls RegisterTests() on each of them when asked. class ParameterizedTestCaseInfoBase { public: - virtual ~ParameterizedTestCaseInfoBase() {} + virtual ~ParameterizedTestCaseInfoBase(); // Base part of test case name for display purposes. virtual const string& GetTestCaseName() const = 0; diff --git a/utils/unittest/googletest/include/gtest/internal/gtest-port.h b/utils/unittest/googletest/include/gtest/internal/gtest-port.h index 58f6caf..32fd9c6 100644 --- a/utils/unittest/googletest/include/gtest/internal/gtest-port.h +++ b/utils/unittest/googletest/include/gtest/internal/gtest-port.h @@ -1116,7 +1116,7 @@ class Notification { // problem. class ThreadWithParamBase { public: - virtual ~ThreadWithParamBase() {} + virtual ~ThreadWithParamBase(); virtual void Run() = 0; }; @@ -1290,7 +1290,7 @@ typedef GTestMutexLock MutexLock; // ThreadLocalValueHolderBase. class ThreadLocalValueHolderBase { public: - virtual ~ThreadLocalValueHolderBase() {} + virtual ~ThreadLocalValueHolderBase(); }; // Called by pthread to delete thread-local data stored by diff --git a/utils/unittest/googletest/gtest-all.cc b/utils/unittest/googletest/src/gtest-all.cc index 97753e5..0a9cee5 100644 --- a/utils/unittest/googletest/gtest-all.cc +++ b/utils/unittest/googletest/src/gtest-all.cc @@ -39,10 +39,10 @@ #include "gtest/gtest.h" // The following lines pull in the real gtest *.cc files. -#include "gtest.cc" -#include "gtest-death-test.cc" -#include "gtest-filepath.cc" -#include "gtest-port.cc" -#include "gtest-printers.cc" -#include "gtest-test-part.cc" -#include "gtest-typed-test.cc" +#include "src/gtest.cc" +#include "src/gtest-death-test.cc" +#include "src/gtest-filepath.cc" +#include "src/gtest-port.cc" +#include "src/gtest-printers.cc" +#include "src/gtest-test-part.cc" +#include "src/gtest-typed-test.cc" diff --git a/utils/unittest/googletest/gtest-death-test.cc b/utils/unittest/googletest/src/gtest-death-test.cc index bf7e32c..314dba2 100644 --- a/utils/unittest/googletest/gtest-death-test.cc +++ b/utils/unittest/googletest/src/gtest-death-test.cc @@ -63,7 +63,7 @@ // prevent a user from accidentally including gtest-internal-inl.h in // his code. #define GTEST_IMPLEMENTATION_ 1 -#include "gtest/internal/gtest-internal-inl.h" +#include "src/gtest-internal-inl.h" #undef GTEST_IMPLEMENTATION_ namespace testing { @@ -300,6 +300,9 @@ DeathTest::DeathTest() { } } +// Pin the vtable to this file. +DeathTest::~DeathTest() {} + // Creates and returns a death test by dispatching to the current // death test factory. bool DeathTest::Create(const char* statement, const RE* regex, @@ -1091,6 +1094,9 @@ bool DefaultDeathTestFactory::Create(const char* statement, const RE* regex, return true; } +// Pin the vtable to this file. +DeathTestFactory::~DeathTestFactory() {} + // Splits a given string on a given delimiter, populating a given // vector with the fields. GTEST_HAS_DEATH_TEST implies that we have // ::std::string, so we can use it here. diff --git a/utils/unittest/googletest/gtest-filepath.cc b/utils/unittest/googletest/src/gtest-filepath.cc index ad1bab8..ad1bab8 100644 --- a/utils/unittest/googletest/gtest-filepath.cc +++ b/utils/unittest/googletest/src/gtest-filepath.cc diff --git a/utils/unittest/googletest/include/gtest/internal/gtest-internal-inl.h b/utils/unittest/googletest/src/gtest-internal-inl.h index 6554cfc..1bae630 100644 --- a/utils/unittest/googletest/include/gtest/internal/gtest-internal-inl.h +++ b/utils/unittest/googletest/src/gtest-internal-inl.h @@ -408,7 +408,7 @@ GTEST_API_ FilePath GetCurrentExecutableName(); class OsStackTraceGetterInterface { public: OsStackTraceGetterInterface() {} - virtual ~OsStackTraceGetterInterface() {} + virtual ~OsStackTraceGetterInterface(); // Returns the current OS stack trace as a String. Parameters: // diff --git a/utils/unittest/googletest/gtest-port.cc b/utils/unittest/googletest/src/gtest-port.cc index 3c32ff1..94fc57f 100644 --- a/utils/unittest/googletest/gtest-port.cc +++ b/utils/unittest/googletest/src/gtest-port.cc @@ -62,7 +62,7 @@ // prevent a user from accidentally including gtest-internal-inl.h in // his code. #define GTEST_IMPLEMENTATION_ 1 -#include "gtest/internal/gtest-internal-inl.h" +#include "src/gtest-internal-inl.h" #undef GTEST_IMPLEMENTATION_ namespace testing { @@ -746,5 +746,19 @@ const char* StringFromGTestEnv(const char* flag, const char* default_value) { return value == NULL ? default_value : value; } +// Pin the vtables to this file. +#if GTEST_HAS_PTHREAD +ThreadWithParamBase::~ThreadWithParamBase() {} +ThreadLocalValueHolderBase::~ThreadLocalValueHolderBase() {} +#endif +TestFactoryBase::~TestFactoryBase() {} + } // namespace internal } // namespace testing + +// Pin the vtable to this file. +#if !GTEST_NO_LLVM_RAW_OSTREAM +namespace llvm { +void convertible_fwd_ostream::anchor() {} +} +#endif diff --git a/utils/unittest/googletest/gtest-printers.cc b/utils/unittest/googletest/src/gtest-printers.cc index 205a394..205a394 100644 --- a/utils/unittest/googletest/gtest-printers.cc +++ b/utils/unittest/googletest/src/gtest-printers.cc diff --git a/utils/unittest/googletest/gtest-test-part.cc b/utils/unittest/googletest/src/gtest-test-part.cc index 1612780..5ddc67c 100644 --- a/utils/unittest/googletest/gtest-test-part.cc +++ b/utils/unittest/googletest/src/gtest-test-part.cc @@ -39,7 +39,7 @@ // prevent a user from accidentally including gtest-internal-inl.h in // his code. #define GTEST_IMPLEMENTATION_ 1 -#include "gtest/internal/gtest-internal-inl.h" +#include "src/gtest-internal-inl.h" #undef GTEST_IMPLEMENTATION_ namespace testing { diff --git a/utils/unittest/googletest/gtest-typed-test.cc b/utils/unittest/googletest/src/gtest-typed-test.cc index a5cc88f..a5cc88f 100644 --- a/utils/unittest/googletest/gtest-typed-test.cc +++ b/utils/unittest/googletest/src/gtest-typed-test.cc diff --git a/utils/unittest/googletest/gtest.cc b/utils/unittest/googletest/src/gtest.cc index eb5c68c..bf850c6 100644 --- a/utils/unittest/googletest/gtest.cc +++ b/utils/unittest/googletest/src/gtest.cc @@ -129,7 +129,7 @@ // prevent a user from accidentally including gtest-internal-inl.h in // his code. #define GTEST_IMPLEMENTATION_ 1 -#include "gtest/internal/gtest-internal-inl.h" +#include "src/gtest-internal-inl.h" #undef GTEST_IMPLEMENTATION_ #if GTEST_OS_WINDOWS @@ -4863,4 +4863,14 @@ void InitGoogleTest(int* argc, wchar_t** argv) { internal::InitGoogleTestImpl(argc, argv); } +// Pin the vtables to this file. +Environment::~Environment() {} +TestPartResultReporterInterface::~TestPartResultReporterInterface() {} +TestEventListener::~TestEventListener() {} +void EmptyTestEventListener::anchor() {} +namespace internal { +OsStackTraceGetterInterface::~OsStackTraceGetterInterface() {} +ParameterizedTestCaseInfoBase::~ParameterizedTestCaseInfoBase() {} +} + } // namespace testing diff --git a/utils/vim/llvm.vim b/utils/vim/llvm.vim index 6c87cff..c2ec57c 100644 --- a/utils/vim/llvm.vim +++ b/utils/vim/llvm.vim @@ -22,17 +22,18 @@ syn match llvmType /\<i\d\+\>/ " Instructions. " The true and false tokens can be used for comparison opcodes, but it's " much more common for these tokens to be used for boolean constants. -syn keyword llvmStatement add alloca and arcp ashr atomicrmw bitcast br call -syn keyword llvmStatement cmpxchg eq exact extractelement extractvalue fadd fast -syn keyword llvmStatement fcmp fdiv fence fmul fpext fptosi fptoui fptrunc free -syn keyword llvmStatement frem fsub getelementptr icmp inbounds indirectbr -syn keyword llvmStatement insertelement insertvalue inttoptr invoke landingpad -syn keyword llvmStatement load lshr malloc max min mul nand ne ninf nnan nsw nsz -syn keyword llvmStatement nuw oeq oge ogt ole olt one or ord phi ptrtoint resume -syn keyword llvmStatement ret sdiv select sext sge sgt shl shufflevector sitofp -syn keyword llvmStatement sle slt srem store sub switch trunc udiv ueq uge ugt -syn keyword llvmStatement uitofp ule ult umax umin une uno unreachable unwind -syn keyword llvmStatement urem va_arg xchg xor zext +syn keyword llvmStatement add addrspacecast alloca and arcp ashr atomicrmw +syn keyword llvmStatement bitcast br call cmpxchg eq exact extractelement +syn keyword llvmStatement extractvalue fadd fast fcmp fdiv fence fmul fpext +syn keyword llvmStatement fptosi fptoui fptrunc free frem fsub getelementptr +syn keyword llvmStatement icmp inbounds indirectbr insertelement insertvalue +syn keyword llvmStatement inttoptr invoke landingpad load lshr malloc max min +syn keyword llvmStatement mul nand ne ninf nnan nsw nsz nuw oeq oge ogt ole +syn keyword llvmStatement olt one or ord phi ptrtoint resume ret sdiv select +syn keyword llvmStatement sext sge sgt shl shufflevector sitofp sle slt srem +syn keyword llvmStatement store sub switch trunc udiv ueq uge ugt uitofp ule ult +syn keyword llvmStatement umax umin une uno unreachable unwind urem va_arg +syn keyword llvmStatement xchg xor zext " Keywords. syn keyword llvmKeyword acq_rel acquire sanitize_address addrspace alias align @@ -43,12 +44,12 @@ syn keyword llvmKeyword constant datalayout declare default define deplibs syn keyword llvmKeyword dllexport dllimport except extern_weak external fastcc syn keyword llvmKeyword filter gc global hidden initialexec inlinehint inreg syn keyword llvmKeyword intel_ocl_bicc inteldialect internal linker_private -syn keyword llvmKeyword linker_private_weak linker_private_weak_def_auto -syn keyword llvmKeyword linkonce linkonce_odr linkonce_odr_auto_hide +syn keyword llvmKeyword linker_private_weak +syn keyword llvmKeyword linkonce linkonce_odr syn keyword llvmKeyword localdynamic localexec minsize module monotonic syn keyword llvmKeyword msp430_intrcc naked nest noalias nocapture syn keyword llvmKeyword noimplicitfloat noinline nonlazybind noredzone noreturn -syn keyword llvmKeyword nounwind optsize personality private protected +syn keyword llvmKeyword nounwind optnone optsize personality private protected syn keyword llvmKeyword ptx_device ptx_kernel readnone readonly release syn keyword llvmKeyword returns_twice sanitize_thread sanitize_memory syn keyword llvmKeyword section seq_cst sideeffect signext singlethread diff --git a/utils/yaml-bench/YAMLBench.cpp b/utils/yaml-bench/YAMLBench.cpp index eef4a72..f335605 100644 --- a/utils/yaml-bench/YAMLBench.cpp +++ b/utils/yaml-bench/YAMLBench.cpp @@ -63,6 +63,20 @@ static raw_ostream &operator <<(raw_ostream &os, const indent &in) { return os; } +/// \brief Pretty print a tag by replacing tag:yaml.org,2002: with !!. +static std::string prettyTag(yaml::Node *N) { + std::string Tag = N->getVerbatimTag(); + if (StringRef(Tag).startswith("tag:yaml.org,2002:")) { + std::string Ret = "!!"; + Ret += StringRef(Tag).substr(18); + return llvm_move(Ret); + } + std::string Ret = "!<"; + Ret += Tag; + Ret += ">"; + return Ret; +} + static void dumpNode( yaml::Node *n , unsigned Indent = 0 , bool SuppressFirstIndent = false) { @@ -76,9 +90,9 @@ static void dumpNode( yaml::Node *n if (yaml::ScalarNode *sn = dyn_cast<yaml::ScalarNode>(n)) { SmallString<32> Storage; StringRef Val = sn->getValue(Storage); - outs() << "!!str \"" << yaml::escape(Val) << "\""; + outs() << prettyTag(n) << " \"" << yaml::escape(Val) << "\""; } else if (yaml::SequenceNode *sn = dyn_cast<yaml::SequenceNode>(n)) { - outs() << "!!seq [\n"; + outs() << prettyTag(n) << " [\n"; ++Indent; for (yaml::SequenceNode::iterator i = sn->begin(), e = sn->end(); i != e; ++i) { @@ -88,7 +102,7 @@ static void dumpNode( yaml::Node *n --Indent; outs() << indent(Indent) << "]"; } else if (yaml::MappingNode *mn = dyn_cast<yaml::MappingNode>(n)) { - outs() << "!!map {\n"; + outs() << prettyTag(n) << " {\n"; ++Indent; for (yaml::MappingNode::iterator i = mn->begin(), e = mn->end(); i != e; ++i) { @@ -104,7 +118,7 @@ static void dumpNode( yaml::Node *n } else if (yaml::AliasNode *an = dyn_cast<yaml::AliasNode>(n)){ outs() << "*" << an->getName(); } else if (dyn_cast<yaml::NullNode>(n)) { - outs() << "!!null null"; + outs() << prettyTag(n) << " null"; } } |