summaryrefslogtreecommitdiffstats
path: root/utils
diff options
context:
space:
mode:
Diffstat (limited to 'utils')
-rw-r--r--utils/FileCheck/CMakeLists.txt11
-rw-r--r--utils/FileCheck/FileCheck.cpp624
-rw-r--r--utils/FileCheck/Makefile21
-rw-r--r--utils/FileUpdate/CMakeLists.txt11
-rw-r--r--utils/FileUpdate/FileUpdate.cpp86
-rw-r--r--utils/FileUpdate/Makefile21
-rw-r--r--utils/Makefile2
-rwxr-xr-xutils/NewNightlyTest.pl21
-rw-r--r--utils/PerfectShuffle/PerfectShuffle.cpp164
-rw-r--r--utils/TableGen/AsmMatcherEmitter.cpp1545
-rw-r--r--utils/TableGen/AsmMatcherEmitter.h33
-rw-r--r--utils/TableGen/AsmWriterEmitter.cpp298
-rw-r--r--utils/TableGen/AsmWriterEmitter.h3
-rw-r--r--utils/TableGen/CMakeLists.txt1
-rw-r--r--utils/TableGen/CallingConvEmitter.cpp16
-rw-r--r--utils/TableGen/CodeEmitterGen.cpp12
-rw-r--r--utils/TableGen/CodeGenDAGPatterns.cpp198
-rw-r--r--utils/TableGen/CodeGenDAGPatterns.h57
-rw-r--r--utils/TableGen/CodeGenInstruction.cpp6
-rw-r--r--utils/TableGen/CodeGenInstruction.h2
-rw-r--r--utils/TableGen/CodeGenTarget.cpp84
-rw-r--r--utils/TableGen/CodeGenTarget.h4
-rw-r--r--utils/TableGen/DAGISelEmitter.cpp100
-rw-r--r--utils/TableGen/FastISelEmitter.cpp15
-rw-r--r--utils/TableGen/InstrInfoEmitter.cpp10
-rw-r--r--utils/TableGen/IntrinsicEmitter.cpp47
-rw-r--r--utils/TableGen/LLVMCConfigurationEmitter.cpp502
-rw-r--r--utils/TableGen/Record.cpp62
-rw-r--r--utils/TableGen/Record.h51
-rw-r--r--utils/TableGen/RegisterInfoEmitter.cpp17
-rw-r--r--utils/TableGen/StringToOffsetTable.h76
-rw-r--r--utils/TableGen/SubtargetEmitter.cpp154
-rw-r--r--utils/TableGen/SubtargetEmitter.h8
-rw-r--r--utils/TableGen/TGParser.cpp2
-rw-r--r--utils/TableGen/TGValueTypes.cpp45
-rw-r--r--utils/TableGen/TableGen.cpp24
-rwxr-xr-xutils/UpdateCMakeLists.pl118
-rw-r--r--utils/bugpoint/RemoteRunSafely.sh105
-rw-r--r--utils/buildit/GNUmakefile1
-rwxr-xr-xutils/buildit/build_llvm34
-rw-r--r--utils/count/CMakeLists.txt3
-rw-r--r--utils/count/Makefile20
-rw-r--r--utils/count/count.c48
-rwxr-xr-xutils/crosstool/ARM/build-install-linux.sh53
-rwxr-xr-xutils/crosstool/create-snapshots.sh28
-rw-r--r--utils/emacs/emacs.el7
-rw-r--r--utils/lit/LitConfig.py71
-rw-r--r--utils/lit/LitFormats.py2
-rw-r--r--utils/lit/ProgressBar.py267
-rw-r--r--utils/lit/ShCommands.py85
-rw-r--r--utils/lit/ShUtil.py346
-rw-r--r--utils/lit/TODO19
-rw-r--r--utils/lit/TclUtil.py322
-rw-r--r--utils/lit/Test.py71
-rw-r--r--utils/lit/TestFormats.py144
-rw-r--r--utils/lit/TestRunner.py505
-rw-r--r--utils/lit/TestingConfig.py96
-rw-r--r--utils/lit/Util.py124
-rwxr-xr-xutils/lit/lit.py531
-rw-r--r--utils/llvm.grm19
-rwxr-xr-xutils/llvmdo3
-rwxr-xr-xutils/llvmgrep2
-rw-r--r--utils/not/CMakeLists.txt11
-rw-r--r--utils/not/Makefile21
-rw-r--r--utils/not/not.cpp17
-rw-r--r--utils/unittest/Makefile2
-rw-r--r--utils/unittest/UnitTestMain/Makefile21
-rw-r--r--utils/unittest/UnitTestMain/TestMain.cpp15
-rw-r--r--utils/unittest/googletest/Makefile6
-rw-r--r--utils/unittest/googletest/README.LLVM5
-rw-r--r--utils/unittest/googletest/include/gtest/internal/gtest-internal.h22
-rw-r--r--utils/unittest/googletest/include/gtest/internal/gtest-port.h4
-rw-r--r--utils/valgrind/x86_64-pc-linux-gnu_gcc-4.3.3.supp23
-rw-r--r--utils/vim/llvm.vim8
74 files changed, 6688 insertions, 824 deletions
diff --git a/utils/FileCheck/CMakeLists.txt b/utils/FileCheck/CMakeLists.txt
new file mode 100644
index 0000000..8fee03f
--- /dev/null
+++ b/utils/FileCheck/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_executable(FileCheck
+ FileCheck.cpp
+ )
+
+target_link_libraries(FileCheck LLVMSupport LLVMSystem)
+if( MINGW )
+ target_link_libraries(FileCheck imagehlp psapi)
+endif( MINGW )
+if( LLVM_ENABLE_THREADS AND HAVE_LIBPTHREAD )
+ target_link_libraries(FileCheck pthread)
+endif()
diff --git a/utils/FileCheck/FileCheck.cpp b/utils/FileCheck/FileCheck.cpp
new file mode 100644
index 0000000..b4d1f84
--- /dev/null
+++ b/utils/FileCheck/FileCheck.cpp
@@ -0,0 +1,624 @@
+//===- FileCheck.cpp - Check that File's Contents match what is expected --===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// FileCheck does a line-by line check of a file that validates whether it
+// contains the expected content. This is useful for regression tests etc.
+//
+// This program exits with an error status of 2 on error, exit status of 0 if
+// the file matched the expected contents, and exit status of 1 if it did not
+// contain the expected contents.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Regex.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/System/Signals.h"
+#include "llvm/ADT/StringMap.h"
+#include <algorithm>
+using namespace llvm;
+
+static cl::opt<std::string>
+CheckFilename(cl::Positional, cl::desc("<check-file>"), cl::Required);
+
+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::opt<bool>
+NoCanonicalizeWhiteSpace("strict-whitespace",
+ cl::desc("Do not treat all horizontal whitespace as equivalent"));
+
+//===----------------------------------------------------------------------===//
+// Pattern Handling Code.
+//===----------------------------------------------------------------------===//
+
+class Pattern {
+ SMLoc PatternLoc;
+
+ /// FixedStr - If non-empty, this pattern is a fixed string match with the
+ /// specified fixed string.
+ StringRef FixedStr;
+
+ /// RegEx - If non-empty, this is a regex pattern.
+ std::string RegExStr;
+
+ /// VariableUses - Entries in this vector map to uses of a variable in the
+ /// pattern, e.g. "foo[[bar]]baz". In this case, the RegExStr will contain
+ /// "foobaz" and we'll get an entry in this vector that tells us to insert the
+ /// value of bar at offset 3.
+ std::vector<std::pair<StringRef, unsigned> > VariableUses;
+
+ /// VariableDefs - Entries in this vector map to definitions of a variable in
+ /// the pattern, e.g. "foo[[bar:.*]]baz". In this case, the RegExStr will
+ /// contain "foo(.*)baz" and VariableDefs will contain the pair "bar",1. The
+ /// index indicates what parenthesized value captures the variable value.
+ std::vector<std::pair<StringRef, unsigned> > VariableDefs;
+
+public:
+
+ Pattern() { }
+
+ bool ParsePattern(StringRef PatternStr, SourceMgr &SM);
+
+ /// 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
+ /// there is a match, the size of the matched string is returned in MatchLen.
+ ///
+ /// The VariableTable StringMap provides the current values of filecheck
+ /// variables and is updated if this match defines new values.
+ size_t Match(StringRef Buffer, size_t &MatchLen,
+ StringMap<StringRef> &VariableTable) const;
+
+private:
+ static void AddFixedStringToRegEx(StringRef FixedStr, std::string &TheStr);
+ bool AddRegExToRegEx(StringRef RegExStr, unsigned &CurParen, SourceMgr &SM);
+};
+
+
+bool Pattern::ParsePattern(StringRef PatternStr, SourceMgr &SM) {
+ PatternLoc = SMLoc::getFromPointer(PatternStr.data());
+
+ // Ignore trailing whitespace.
+ while (!PatternStr.empty() &&
+ (PatternStr.back() == ' ' || PatternStr.back() == '\t'))
+ PatternStr = PatternStr.substr(0, PatternStr.size()-1);
+
+ // Check that there is something on the line.
+ if (PatternStr.empty()) {
+ SM.PrintMessage(PatternLoc, "found empty check string with prefix '" +
+ CheckPrefix+":'", "error");
+ return true;
+ }
+
+ // Check to see if this is a fixed string, or if it has regex pieces.
+ if (PatternStr.size() < 2 ||
+ (PatternStr.find("{{") == StringRef::npos &&
+ PatternStr.find("[[") == StringRef::npos)) {
+ FixedStr = PatternStr;
+ return false;
+ }
+
+ // Paren value #0 is for the fully matched string. Any new parenthesized
+ // values add from their.
+ unsigned CurParen = 1;
+
+ // Otherwise, there is at least one regex piece. Build up the regex pattern
+ // by escaping scary characters in fixed strings, building up one big regex.
+ while (!PatternStr.empty()) {
+ // RegEx matches.
+ if (PatternStr.size() >= 2 &&
+ PatternStr[0] == '{' && PatternStr[1] == '{') {
+
+ // Otherwise, this is the start of a regex match. Scan for the }}.
+ size_t End = PatternStr.find("}}");
+ if (End == StringRef::npos) {
+ SM.PrintMessage(SMLoc::getFromPointer(PatternStr.data()),
+ "found start of regex string with no end '}}'", "error");
+ return true;
+ }
+
+ if (AddRegExToRegEx(PatternStr.substr(2, End-2), CurParen, SM))
+ return true;
+ PatternStr = PatternStr.substr(End+2);
+ continue;
+ }
+
+ // Named RegEx matches. These are of two forms: [[foo:.*]] which matches .*
+ // (or some other regex) and assigns it to the FileCheck variable 'foo'. The
+ // second form is [[foo]] which is a reference to foo. The variable name
+ // itself must be of the form "[a-zA-Z][0-9a-zA-Z]*", otherwise we reject
+ // it. This is to catch some common errors.
+ if (PatternStr.size() >= 2 &&
+ PatternStr[0] == '[' && PatternStr[1] == '[') {
+ // Verify that it is terminated properly.
+ size_t End = PatternStr.find("]]");
+ if (End == StringRef::npos) {
+ SM.PrintMessage(SMLoc::getFromPointer(PatternStr.data()),
+ "invalid named regex reference, no ]] found", "error");
+ return true;
+ }
+
+ StringRef MatchStr = PatternStr.substr(2, End-2);
+ PatternStr = PatternStr.substr(End+2);
+
+ // Get the regex name (e.g. "foo").
+ size_t NameEnd = MatchStr.find(':');
+ StringRef Name = MatchStr.substr(0, NameEnd);
+
+ if (Name.empty()) {
+ SM.PrintMessage(SMLoc::getFromPointer(Name.data()),
+ "invalid name in named regex: empty name", "error");
+ return true;
+ }
+
+ // Verify that the name is well formed.
+ for (unsigned i = 0, e = Name.size(); i != e; ++i)
+ if ((Name[i] < 'a' || Name[i] > 'z') &&
+ (Name[i] < 'A' || Name[i] > 'Z') &&
+ (Name[i] < '0' || Name[i] > '9')) {
+ SM.PrintMessage(SMLoc::getFromPointer(Name.data()+i),
+ "invalid name in named regex", "error");
+ return true;
+ }
+
+ // Name can't start with a digit.
+ if (isdigit(Name[0])) {
+ SM.PrintMessage(SMLoc::getFromPointer(Name.data()),
+ "invalid name in named regex", "error");
+ return true;
+ }
+
+ // Handle [[foo]].
+ if (NameEnd == StringRef::npos) {
+ VariableUses.push_back(std::make_pair(Name, RegExStr.size()));
+ continue;
+ }
+
+ // Handle [[foo:.*]].
+ VariableDefs.push_back(std::make_pair(Name, CurParen));
+ RegExStr += '(';
+ ++CurParen;
+
+ if (AddRegExToRegEx(MatchStr.substr(NameEnd+1), CurParen, SM))
+ return true;
+
+ RegExStr += ')';
+ }
+
+ // Handle fixed string matches.
+ // Find the end, which is the start of the next regex.
+ size_t FixedMatchEnd = PatternStr.find("{{");
+ FixedMatchEnd = std::min(FixedMatchEnd, PatternStr.find("[["));
+ AddFixedStringToRegEx(PatternStr.substr(0, FixedMatchEnd), RegExStr);
+ PatternStr = PatternStr.substr(FixedMatchEnd);
+ continue;
+ }
+
+ return false;
+}
+
+void Pattern::AddFixedStringToRegEx(StringRef FixedStr, std::string &TheStr) {
+ // Add the characters from FixedStr to the regex, escaping as needed. This
+ // avoids "leaning toothpicks" in common patterns.
+ for (unsigned i = 0, e = FixedStr.size(); i != e; ++i) {
+ switch (FixedStr[i]) {
+ // These are the special characters matched in "p_ere_exp".
+ case '(':
+ case ')':
+ case '^':
+ case '$':
+ case '|':
+ case '*':
+ case '+':
+ case '?':
+ case '.':
+ case '[':
+ case '\\':
+ case '{':
+ TheStr += '\\';
+ // FALL THROUGH.
+ default:
+ TheStr += FixedStr[i];
+ break;
+ }
+ }
+}
+
+bool Pattern::AddRegExToRegEx(StringRef RegexStr, unsigned &CurParen,
+ SourceMgr &SM) {
+ Regex R(RegexStr);
+ std::string Error;
+ if (!R.isValid(Error)) {
+ SM.PrintMessage(SMLoc::getFromPointer(RegexStr.data()),
+ "invalid regex: " + Error, "error");
+ return true;
+ }
+
+ RegExStr += RegexStr.str();
+ CurParen += R.getNumMatches();
+ return false;
+}
+
+/// 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
+/// there is a match, the size of the matched string is returned in MatchLen.
+size_t Pattern::Match(StringRef Buffer, size_t &MatchLen,
+ StringMap<StringRef> &VariableTable) const {
+ // If this is a fixed string pattern, just match it now.
+ if (!FixedStr.empty()) {
+ MatchLen = FixedStr.size();
+ return Buffer.find(FixedStr);
+ }
+
+ // Regex match.
+
+ // If there are variable uses, we need to create a temporary string with the
+ // actual value.
+ StringRef RegExToMatch = RegExStr;
+ std::string TmpStr;
+ if (!VariableUses.empty()) {
+ TmpStr = RegExStr;
+
+ unsigned InsertOffset = 0;
+ for (unsigned i = 0, e = VariableUses.size(); i != e; ++i) {
+ // Look up the value and escape it so that we can plop it into the regex.
+ std::string Value;
+ AddFixedStringToRegEx(VariableTable[VariableUses[i].first], Value);
+
+ // Plop it into the regex at the adjusted offset.
+ TmpStr.insert(TmpStr.begin()+VariableUses[i].second+InsertOffset,
+ Value.begin(), Value.end());
+ InsertOffset += Value.size();
+ }
+
+ // Match the newly constructed regex.
+ RegExToMatch = TmpStr;
+ }
+
+
+ SmallVector<StringRef, 4> MatchInfo;
+ if (!Regex(RegExToMatch, Regex::Newline).match(Buffer, &MatchInfo))
+ return StringRef::npos;
+
+ // Successful regex match.
+ assert(!MatchInfo.empty() && "Didn't get any match");
+ StringRef FullMatch = MatchInfo[0];
+
+ // If this defines any variables, remember their values.
+ for (unsigned i = 0, e = VariableDefs.size(); i != e; ++i) {
+ assert(VariableDefs[i].second < MatchInfo.size() &&
+ "Internal paren error");
+ VariableTable[VariableDefs[i].first] = MatchInfo[VariableDefs[i].second];
+ }
+
+ MatchLen = FullMatch.size();
+ return FullMatch.data()-Buffer.data();
+}
+
+
+//===----------------------------------------------------------------------===//
+// Check Strings.
+//===----------------------------------------------------------------------===//
+
+/// CheckString - This is a check that we found in the input file.
+struct CheckString {
+ /// Pat - The pattern to match.
+ Pattern Pat;
+
+ /// 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;
+
+ /// NotStrings - 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<std::pair<SMLoc, Pattern> > NotStrings;
+
+ CheckString(const Pattern &P, SMLoc L, bool isCheckNext)
+ : Pat(P), Loc(L), IsCheckNext(isCheckNext) {}
+};
+
+/// CanonicalizeInputFile - Remove duplicate horizontal space from the specified
+/// memory buffer, free it, and return a new one.
+static MemoryBuffer *CanonicalizeInputFile(MemoryBuffer *MB) {
+ SmallVector<char, 16> NewFile;
+ NewFile.reserve(MB->getBufferSize());
+
+ for (const char *Ptr = MB->getBufferStart(), *End = MB->getBufferEnd();
+ Ptr != End; ++Ptr) {
+ // If C is not a horizontal whitespace, skip it.
+ if (*Ptr != ' ' && *Ptr != '\t') {
+ NewFile.push_back(*Ptr);
+ continue;
+ }
+
+ // Otherwise, add one space and advance over neighboring space.
+ NewFile.push_back(' ');
+ while (Ptr+1 != End &&
+ (Ptr[1] == ' ' || Ptr[1] == '\t'))
+ ++Ptr;
+ }
+
+ // Free the old buffer and return a new one.
+ MemoryBuffer *MB2 =
+ MemoryBuffer::getMemBufferCopy(NewFile.data(),
+ NewFile.data() + NewFile.size(),
+ MB->getBufferIdentifier());
+
+ delete MB;
+ return MB2;
+}
+
+
+/// ReadCheckFile - Read the check file, which specifies the sequence of
+/// expected strings. The strings are added to the CheckStrings vector.
+static bool ReadCheckFile(SourceMgr &SM,
+ std::vector<CheckString> &CheckStrings) {
+ // Open the check file, and tell SourceMgr about it.
+ std::string ErrorStr;
+ MemoryBuffer *F =
+ MemoryBuffer::getFileOrSTDIN(CheckFilename.c_str(), &ErrorStr);
+ if (F == 0) {
+ errs() << "Could not open check file '" << CheckFilename << "': "
+ << ErrorStr << '\n';
+ return true;
+ }
+
+ // If we want to canonicalize whitespace, strip excess whitespace from the
+ // buffer containing the CHECK lines.
+ if (!NoCanonicalizeWhiteSpace)
+ F = CanonicalizeInputFile(F);
+
+ SM.AddNewSourceBuffer(F, SMLoc());
+
+ // Find all instances of CheckPrefix followed by : in the file.
+ StringRef Buffer = F->getBuffer();
+
+ std::vector<std::pair<SMLoc, Pattern> > NotMatches;
+
+ while (1) {
+ // See if Prefix occurs in the memory buffer.
+ Buffer = Buffer.substr(Buffer.find(CheckPrefix));
+
+ // If we didn't find a match, we're done.
+ if (Buffer.empty())
+ break;
+
+ 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;
+
+ // 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()+7);
+ IsCheckNext = true;
+ } else if (Buffer.size() > CheckPrefix.size()+5 &&
+ memcmp(Buffer.data()+CheckPrefix.size(), "-NOT:", 5) == 0) {
+ Buffer = Buffer.substr(CheckPrefix.size()+6);
+ IsCheckNot = true;
+ } else {
+ Buffer = Buffer.substr(1);
+ continue;
+ }
+
+ // 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.
+ size_t EOL = Buffer.find_first_of("\n\r");
+
+ // Parse the pattern.
+ Pattern P;
+ if (P.ParsePattern(Buffer.substr(0, EOL), SM))
+ return true;
+
+ 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),
+ "found '"+CheckPrefix+"-NEXT:' without previous '"+
+ CheckPrefix+ ": line", "error");
+ return true;
+ }
+
+ // Handle CHECK-NOT.
+ if (IsCheckNot) {
+ NotMatches.push_back(std::make_pair(SMLoc::getFromPointer(Buffer.data()),
+ P));
+ continue;
+ }
+
+
+ // Okay, add the string we captured to the output vector and move on.
+ CheckStrings.push_back(CheckString(P,
+ SMLoc::getFromPointer(Buffer.data()),
+ IsCheckNext));
+ std::swap(NotMatches, CheckStrings.back().NotStrings);
+ }
+
+ if (CheckStrings.empty()) {
+ errs() << "error: no check strings found with prefix '" << CheckPrefix
+ << ":'\n";
+ return true;
+ }
+
+ if (!NotMatches.empty()) {
+ errs() << "error: '" << CheckPrefix
+ << "-NOT:' not supported after last check line.\n";
+ return true;
+ }
+
+ return false;
+}
+
+static void PrintCheckFailed(const SourceMgr &SM, const CheckString &CheckStr,
+ StringRef Buffer) {
+ // Otherwise, we have an error, emit an error message.
+ SM.PrintMessage(CheckStr.Loc, "expected string not found in input",
+ "error");
+
+ // Print the "scanning from here" line. If the current position is at the
+ // end of a line, advance to the start of the next line.
+ Buffer = Buffer.substr(Buffer.find_first_not_of(" \t\n\r"));
+
+ SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), "scanning from here",
+ "note");
+}
+
+/// CountNumNewlinesBetween - Count the number of newlines in the specified
+/// range.
+static unsigned CountNumNewlinesBetween(StringRef Range) {
+ unsigned NumNewLines = 0;
+ while (1) {
+ // Scan for newline.
+ Range = Range.substr(Range.find_first_of("\n\r"));
+ if (Range.empty()) return NumNewLines;
+
+ ++NumNewLines;
+
+ // Handle \n\r and \r\n as a single newline.
+ if (Range.size() > 1 &&
+ (Range[1] == '\n' || Range[1] == '\r') &&
+ (Range[0] != Range[1]))
+ Range = Range.substr(1);
+ Range = Range.substr(1);
+ }
+}
+
+int main(int argc, char **argv) {
+ sys::PrintStackTraceOnErrorSignal();
+ PrettyStackTraceProgram X(argc, argv);
+ cl::ParseCommandLineOptions(argc, argv);
+
+ SourceMgr SM;
+
+ // Read the expected strings from the check file.
+ std::vector<CheckString> CheckStrings;
+ if (ReadCheckFile(SM, CheckStrings))
+ return 2;
+
+ // Open the file to check and add it to SourceMgr.
+ std::string ErrorStr;
+ MemoryBuffer *F =
+ MemoryBuffer::getFileOrSTDIN(InputFilename.c_str(), &ErrorStr);
+ if (F == 0) {
+ errs() << "Could not open input file '" << InputFilename << "': "
+ << ErrorStr << '\n';
+ return true;
+ }
+
+ // Remove duplicate spaces in the input file if requested.
+ if (!NoCanonicalizeWhiteSpace)
+ F = CanonicalizeInputFile(F);
+
+ SM.AddNewSourceBuffer(F, SMLoc());
+
+ /// VariableTable - This holds all the current filecheck variables.
+ StringMap<StringRef> VariableTable;
+
+ // Check that we have all of the expected strings, in order, in the input
+ // file.
+ StringRef Buffer = F->getBuffer();
+
+ const char *LastMatch = Buffer.data();
+
+ for (unsigned StrNo = 0, e = CheckStrings.size(); StrNo != e; ++StrNo) {
+ const CheckString &CheckStr = CheckStrings[StrNo];
+
+ StringRef SearchFrom = Buffer;
+
+ // Find StrNo in the file.
+ size_t MatchLen = 0;
+ Buffer = Buffer.substr(CheckStr.Pat.Match(Buffer, MatchLen, VariableTable));
+
+ // If we didn't find a match, reject the input.
+ if (Buffer.empty()) {
+ PrintCheckFailed(SM, CheckStr, SearchFrom);
+ return 1;
+ }
+
+ StringRef SkippedRegion(LastMatch, Buffer.data()-LastMatch);
+
+ // If this check is a "CHECK-NEXT", verify that the previous match was on
+ // the previous line (i.e. that there is one newline between them).
+ if (CheckStr.IsCheckNext) {
+ // Count the number of newlines between the previous match and this one.
+ assert(LastMatch != F->getBufferStart() &&
+ "CHECK-NEXT can't be the first check in a file");
+
+ unsigned NumNewLines = CountNumNewlinesBetween(SkippedRegion);
+ if (NumNewLines == 0) {
+ SM.PrintMessage(CheckStr.Loc,
+ CheckPrefix+"-NEXT: is on the same line as previous match",
+ "error");
+ SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()),
+ "'next' match was here", "note");
+ SM.PrintMessage(SMLoc::getFromPointer(LastMatch),
+ "previous match was here", "note");
+ return 1;
+ }
+
+ if (NumNewLines != 1) {
+ SM.PrintMessage(CheckStr.Loc,
+ CheckPrefix+
+ "-NEXT: is not on the line after the previous match",
+ "error");
+ SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()),
+ "'next' match was here", "note");
+ SM.PrintMessage(SMLoc::getFromPointer(LastMatch),
+ "previous match was here", "note");
+ return 1;
+ }
+ }
+
+ // If this match had "not strings", verify that they don't exist in the
+ // skipped region.
+ for (unsigned ChunkNo = 0, e = CheckStr.NotStrings.size();
+ ChunkNo != e; ++ChunkNo) {
+ size_t MatchLen = 0;
+ size_t Pos = CheckStr.NotStrings[ChunkNo].second.Match(SkippedRegion,
+ MatchLen,
+ VariableTable);
+ if (Pos == StringRef::npos) continue;
+
+ SM.PrintMessage(SMLoc::getFromPointer(LastMatch+Pos),
+ CheckPrefix+"-NOT: string occurred!", "error");
+ SM.PrintMessage(CheckStr.NotStrings[ChunkNo].first,
+ CheckPrefix+"-NOT: pattern specified here", "note");
+ return 1;
+ }
+
+
+ // Otherwise, everything is good. Step over the matched text and remember
+ // the position after the match as the end of the last match.
+ Buffer = Buffer.substr(MatchLen);
+ LastMatch = Buffer.data();
+ }
+
+ return 0;
+}
diff --git a/utils/FileCheck/Makefile b/utils/FileCheck/Makefile
new file mode 100644
index 0000000..f1af5b6
--- /dev/null
+++ b/utils/FileCheck/Makefile
@@ -0,0 +1,21 @@
+##===- utils/FileCheck/Makefile ----------------------------*- Makefile -*-===##
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+##===----------------------------------------------------------------------===##
+
+LEVEL = ../..
+TOOLNAME = FileCheck
+USEDLIBS = LLVMSupport.a LLVMSystem.a
+
+# This tool has no plugins, optimize startup time.
+TOOL_NO_EXPORTS = 1
+
+# Don't install this utility
+NO_INSTALL = 1
+
+include $(LEVEL)/Makefile.common
+
diff --git a/utils/FileUpdate/CMakeLists.txt b/utils/FileUpdate/CMakeLists.txt
new file mode 100644
index 0000000..bacbd16
--- /dev/null
+++ b/utils/FileUpdate/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_executable(FileUpdate
+ FileUpdate.cpp
+ )
+
+target_link_libraries(FileUpdate LLVMSupport LLVMSystem)
+if( MINGW )
+ target_link_libraries(FileUpdate imagehlp psapi)
+endif( MINGW )
+if( LLVM_ENABLE_THREADS AND HAVE_LIBPTHREAD )
+ target_link_libraries(FileUpdate pthread)
+endif()
diff --git a/utils/FileUpdate/FileUpdate.cpp b/utils/FileUpdate/FileUpdate.cpp
new file mode 100644
index 0000000..26fd75e
--- /dev/null
+++ b/utils/FileUpdate/FileUpdate.cpp
@@ -0,0 +1,86 @@
+//===- FileUpdate.cpp - Conditionally update a file -----------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// FileUpdate is a utility for conditionally updating a file from its input
+// based on whether the input differs from the output. It is used to avoid
+// unnecessary modifications in a build system.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/System/Signals.h"
+using namespace llvm;
+
+static cl::opt<bool>
+Quiet("quiet", cl::desc("Don't print unnecessary status information"),
+ cl::init(false));
+
+static cl::opt<std::string>
+InputFilename("input-file", cl::desc("Input file (defaults to stdin)"),
+ cl::init("-"), cl::value_desc("filename"));
+
+static cl::opt<std::string>
+OutputFilename(cl::Positional, cl::desc("<output-file>"), cl::Required);
+
+int main(int argc, char **argv) {
+ sys::PrintStackTraceOnErrorSignal();
+ PrettyStackTraceProgram X(argc, argv);
+ cl::ParseCommandLineOptions(argc, argv);
+
+ // Get the input data.
+ std::string ErrorStr;
+ MemoryBuffer *In =
+ MemoryBuffer::getFileOrSTDIN(InputFilename.c_str(), &ErrorStr);
+ if (In == 0) {
+ errs() << argv[0] << ": error: Unable to get input '"
+ << InputFilename << "': " << ErrorStr << '\n';
+ return 1;
+ }
+
+ // Get the output data.
+ MemoryBuffer *Out = MemoryBuffer::getFile(OutputFilename.c_str(), &ErrorStr);
+
+ // If the output exists and the contents match, we are done.
+ if (Out && In->getBufferSize() == Out->getBufferSize() &&
+ memcmp(In->getBufferStart(), Out->getBufferStart(),
+ Out->getBufferSize()) == 0) {
+ if (!Quiet)
+ outs() << argv[0] << ": Not updating '" << OutputFilename
+ << "', contents match input.\n";
+ return 0;
+ }
+
+ delete Out;
+
+ // Otherwise, overwrite the output.
+ if (!Quiet)
+ outs() << argv[0] << ": Updating '" << OutputFilename
+ << "', contents changed.\n";
+ raw_fd_ostream OutStream(OutputFilename.c_str(), ErrorStr,
+ raw_fd_ostream::F_Binary);
+ if (!ErrorStr.empty()) {
+ errs() << argv[0] << ": Unable to write output '"
+ << OutputFilename << "': " << ErrorStr << '\n';
+ return 1;
+ }
+
+ OutStream.write(In->getBufferStart(), In->getBufferSize());
+ OutStream.close();
+
+ if (OutStream.has_error()) {
+ errs() << argv[0] << ": Could not open output file '"
+ << OutputFilename << "': " << ErrorStr << '\n';
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/utils/FileUpdate/Makefile b/utils/FileUpdate/Makefile
new file mode 100644
index 0000000..5b545c2
--- /dev/null
+++ b/utils/FileUpdate/Makefile
@@ -0,0 +1,21 @@
+##===- utils/FileUpdate/Makefile ---------------------------*- Makefile -*-===##
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+##===----------------------------------------------------------------------===##
+
+LEVEL = ../..
+TOOLNAME = FileUpdate
+USEDLIBS = LLVMSupport.a LLVMSystem.a
+
+# This tool has no plugins, optimize startup time.
+TOOL_NO_EXPORTS = 1
+
+# Don't install this utility
+NO_INSTALL = 1
+
+include $(LEVEL)/Makefile.common
+
diff --git a/utils/Makefile b/utils/Makefile
index c43086b..000705e 100644
--- a/utils/Makefile
+++ b/utils/Makefile
@@ -8,7 +8,7 @@
##===----------------------------------------------------------------------===##
LEVEL = ..
-PARALLEL_DIRS := TableGen fpcmp PerfectShuffle unittest
+PARALLEL_DIRS := TableGen fpcmp PerfectShuffle FileCheck FileUpdate count not unittest
EXTRA_DIST := cgiplotNLT.pl check-each-file codegen-diff countloc.sh cvsupdate \
DSAclean.py DSAextract.py emacs findsym.pl GenLibDeps.pl \
diff --git a/utils/NewNightlyTest.pl b/utils/NewNightlyTest.pl
index b490650..477df8f 100755
--- a/utils/NewNightlyTest.pl
+++ b/utils/NewNightlyTest.pl
@@ -55,6 +55,10 @@ use Socket;
# override the default.
# -ldflags Next argument specifies that linker options that override
# the default.
+# -test-cflags Next argument specifies that C compilation options that
+# override the default when running the testsuite.
+# -test-cxxflags Next argument specifies that C++ compilation options that
+# override the default when running the testsuite.
# -compileflags Next argument specifies extra options passed to make when
# building LLVM.
# -use-gmake Use gmake instead of the default make command to build
@@ -101,7 +105,7 @@ my $HOME = $ENV{'HOME'};
my $SVNURL = $ENV{"SVNURL"};
$SVNURL = 'http://llvm.org/svn/llvm-project' unless $SVNURL;
my $TestSVNURL = $ENV{"TestSVNURL"};
-$TestSVNURL = 'https://llvm.org/svn/llvm-project' unless $TestSVNURL;
+$TestSVNURL = 'http://llvm.org/svn/llvm-project' unless $TestSVNURL;
my $CVSRootDir = $ENV{'CVSROOT'};
$CVSRootDir = "/home/vadve/shared/PublicCVS" unless $CVSRootDir;
my $BuildDir = $ENV{'BUILDDIR'};
@@ -139,6 +143,7 @@ $SUBMITSCRIPT = "/nightlytest/NightlyTestAccept.php";
$SUBMITAUX="";
$SUBMIT = 1;
$PARALLELJOBS = "2";
+my $TESTFLAGS="";
while (scalar(@ARGV) and ($_ = $ARGV[0], /^[-+]/)) {
shift;
@@ -148,6 +153,7 @@ while (scalar(@ARGV) and ($_ = $ARGV[0], /^[-+]/)) {
if (/^-nocheckout$/) { $NOCHECKOUT = 1; next; }
if (/^-nocvsstats$/) { $NOCVSSTATS = 1; next; }
if (/^-noremove$/) { $NOREMOVE = 1; next; }
+ if (/^-noremoveatend$/) { $NOREMOVEATEND = 1; next; }
if (/^-noremoveresults$/){ $NOREMOVERESULTS = 1; next; }
if (/^-notest$/) { $NOTEST = 1; next; }
if (/^-norunningtests$/) { next; } # Backward compatibility, ignored.
@@ -180,6 +186,8 @@ while (scalar(@ARGV) and ($_ = $ARGV[0], /^[-+]/)) {
shift; next; }
if (/^-with-externals$/) { $CONFIGUREARGS .= " --with-externals=$ARGV[0]";
shift; next; }
+ if (/^-configure-args$/) { $CONFIGUREARGS .= " $ARGV[0]";
+ shift; next; }
if (/^-submit-server/) { $SUBMITSERVER = "$ARGV[0]"; shift; next; }
if (/^-submit-script/) { $SUBMITSCRIPT = "$ARGV[0]"; shift; next; }
if (/^-submit-aux/) { $SUBMITAUX = "$ARGV[0]"; shift; next; }
@@ -200,6 +208,10 @@ while (scalar(@ARGV) and ($_ = $ARGV[0], /^[-+]/)) {
shift; next; }
if (/^-ldflags/) { $MAKEOPTS = "$MAKEOPTS LD.Flags=\'$ARGV[0]\'";
shift; next; }
+ if (/^-test-cflags/) { $TESTFLAGS = "$TESTFLAGS CFLAGS=\'$ARGV[0]\'";
+ shift; next; }
+ if (/^-test-cxxflags/) { $TESTFLAGS = "$TESTFLAGS CXXFLAGS=\'$ARGV[0]\'";
+ shift; next; }
if (/^-compileflags/) { $MAKEOPTS = "$MAKEOPTS $ARGV[0]"; shift; next; }
if (/^-use-gmake/) { $MAKECMD = "gmake"; shift; next; }
if (/^-extraflags/) { $CONFIGUREARGS .=
@@ -921,10 +933,11 @@ sub TestDirectory {
if (!$NOTEST) {
if( $VERBOSE) {
print "$MAKECMD -k $MAKEOPTS $PROGTESTOPTS report.nightly.csv ".
- "TEST=nightly > $ProgramTestLog 2>&1\n";
+ "$TESTFLAGS TEST=nightly > $ProgramTestLog 2>&1\n";
}
RunLoggedCommand("$MAKECMD -k $MAKEOPTS $PROGTESTOPTS report.nightly.csv ".
- "TEST=nightly", $ProgramTestLog, "TEST DIRECTORY $SubDir");
+ "$TESTFLAGS TEST=nightly",
+ $ProgramTestLog, "TEST DIRECTORY $SubDir");
$llcbeta_options=`$MAKECMD print-llcbeta-option`;
}
@@ -1147,6 +1160,6 @@ if ($SUBMIT || !($SUBMITAUX eq "")) {
#
##############################################################
system ( "$NICE rm -rf $BuildDir")
- if (!$NOCHECKOUT and !$NOREMOVE);
+ if (!$NOCHECKOUT and !$NOREMOVE and !$NOREMOVEATEND);
system ( "$NICE rm -rf $WebDir")
if (!$NOCHECKOUT and !$NOREMOVE and !$NOREMOVERESULTS);
diff --git a/utils/PerfectShuffle/PerfectShuffle.cpp b/utils/PerfectShuffle/PerfectShuffle.cpp
index 26c4cf4..b94a7d3 100644
--- a/utils/PerfectShuffle/PerfectShuffle.cpp
+++ b/utils/PerfectShuffle/PerfectShuffle.cpp
@@ -21,11 +21,11 @@
struct Operator;
// Masks are 4-nibble hex numbers. Values 0-7 in any nibble means that it takes
-// an element from that value of the input vectors. A value of 8 means the
+// an element from that value of the input vectors. A value of 8 means the
// entry is undefined.
// Mask manipulation functions.
-static inline unsigned short MakeMask(unsigned V0, unsigned V1,
+static inline unsigned short MakeMask(unsigned V0, unsigned V1,
unsigned V2, unsigned V3) {
return (V0 << (3*4)) | (V1 << (2*4)) | (V2 << (1*4)) | (V3 << (0*4));
}
@@ -70,7 +70,7 @@ static unsigned short getLHSOnlyMask(unsigned short Mask) {
/// getCompressedMask - Turn a 16-bit uncompressed mask (where each elt uses 4
/// bits) into a compressed 13-bit mask, where each elt is multiplied by 9.
static unsigned getCompressedMask(unsigned short Mask) {
- return getMaskElt(Mask, 0)*9*9*9 + getMaskElt(Mask, 1)*9*9 +
+ return getMaskElt(Mask, 0)*9*9*9 + getMaskElt(Mask, 1)*9*9 +
getMaskElt(Mask, 2)*9 + getMaskElt(Mask, 3);
}
@@ -87,7 +87,7 @@ struct ShuffleVal {
unsigned Cost; // Number of instrs used to generate this value.
Operator *Op; // The Operation used to generate this value.
unsigned short Arg0, Arg1; // Input operands for this value.
-
+
ShuffleVal() : Cost(1000000) {}
};
@@ -104,22 +104,25 @@ struct Operator {
unsigned short ShuffleMask;
unsigned short OpNum;
const char *Name;
-
- Operator(unsigned short shufflemask, const char *name, unsigned opnum)
- : ShuffleMask(shufflemask), OpNum(opnum), Name(name) {
+ unsigned Cost;
+
+ Operator(unsigned short shufflemask, const char *name, unsigned opnum,
+ unsigned cost = 1)
+ : ShuffleMask(shufflemask), OpNum(opnum), Name(name), Cost(cost) {
TheOperators.push_back(this);
}
~Operator() {
assert(TheOperators.back() == this);
TheOperators.pop_back();
}
-
+
bool isOnlyLHSOperator() const {
return isOnlyLHSMask(ShuffleMask);
}
-
+
const char *getName() const { return Name; }
-
+ unsigned getCost() const { return Cost; }
+
unsigned short getTransformedMask(unsigned short LHSMask, unsigned RHSMask) {
// Extract the elements from LHSMask and RHSMask, as appropriate.
unsigned Result = 0;
@@ -156,7 +159,7 @@ static void PrintOperation(unsigned ValNo, unsigned short Vals[]) {
std::cerr << "t" << ValNo;
PrintMask(ThisOp, std::cerr);
std::cerr << " = " << ShufTab[ThisOp].Op->getName() << "(";
-
+
if (ShufTab[ShufTab[ThisOp].Arg0].Cost == 0) {
std::cerr << getZeroCostOpName(ShufTab[ThisOp].Arg0);
PrintMask(ShufTab[ThisOp].Arg0, std::cerr);
@@ -168,7 +171,7 @@ static void PrintOperation(unsigned ValNo, unsigned short Vals[]) {
break;
}
}
-
+
if (!ShufTab[Vals[ValNo]].Op->isOnlyLHSOperator()) {
std::cerr << ", ";
if (ShufTab[ShufTab[ThisOp].Arg1].Cost == 0) {
@@ -193,21 +196,21 @@ static unsigned getNumEntered() {
return Count;
}
-static void EvaluateOps(unsigned short Elt, unsigned short Vals[],
+static void EvaluateOps(unsigned short Elt, unsigned short Vals[],
unsigned &NumVals) {
if (ShufTab[Elt].Cost == 0) return;
// If this value has already been evaluated, it is free. FIXME: match undefs.
for (unsigned i = 0, e = NumVals; i != e; ++i)
if (Vals[i] == Elt) return;
-
+
// Otherwise, get the operands of the value, then add it.
unsigned Arg0 = ShufTab[Elt].Arg0, Arg1 = ShufTab[Elt].Arg1;
if (ShufTab[Arg0].Cost)
EvaluateOps(Arg0, Vals, NumVals);
if (Arg0 != Arg1 && ShufTab[Arg1].Cost)
EvaluateOps(Arg1, Vals, NumVals);
-
+
Vals[NumVals++] = Elt;
}
@@ -220,7 +223,7 @@ int main() {
ShufTab[0x4567].Cost = 0;
ShufTab[0x4567].Op = 0;
ShufTab[0x4567].Arg0 = 0x4567;
-
+
// Seed the first-level of shuffles, shuffles whose inputs are the input to
// the vectorshuffle operation.
bool MadeChange = true;
@@ -230,7 +233,7 @@ int main() {
++OpCount;
std::cerr << "Starting iteration #" << OpCount << " with "
<< getNumEntered() << " entries established.\n";
-
+
// Scan the table for two reasons: First, compute the maximum cost of any
// operation left in the table. Second, make sure that values with undefs
// have the cheapest alternative that they match.
@@ -239,7 +242,7 @@ int main() {
if (!isValidMask(i)) continue;
if (ShufTab[i].Cost > MaxCost)
MaxCost = ShufTab[i].Cost;
-
+
// If this value has an undef, make it be computed the cheapest possible
// way of any of the things that it matches.
if (hasUndefElements(i)) {
@@ -266,10 +269,10 @@ int main() {
UndefIdx = 3;
else
abort();
-
+
unsigned MinVal = i;
unsigned MinCost = ShufTab[i].Cost;
-
+
// Scan the 8 entries.
for (unsigned j = 0; j != 8; ++j) {
unsigned NewElt = setMaskElt(i, UndefIdx, j);
@@ -278,15 +281,15 @@ int main() {
MinVal = NewElt;
}
}
-
+
// If we found something cheaper than what was here before, use it.
if (i != MinVal) {
MadeChange = true;
ShufTab[i] = ShufTab[MinVal];
}
- }
+ }
}
-
+
for (unsigned LHS = 0; LHS != 0x8889; ++LHS) {
if (!isValidMask(LHS)) continue;
if (ShufTab[LHS].Cost > 1000) continue;
@@ -295,14 +298,14 @@ int main() {
// we already have, don't consider it.
if (ShufTab[LHS].Cost + 1 >= MaxCost)
continue;
-
+
for (unsigned opnum = 0, e = TheOperators.size(); opnum != e; ++opnum) {
Operator *Op = TheOperators[opnum];
// Evaluate op(LHS,LHS)
unsigned ResultMask = Op->getTransformedMask(LHS, LHS);
- unsigned Cost = ShufTab[LHS].Cost + 1;
+ unsigned Cost = ShufTab[LHS].Cost + Op->getCost();
if (Cost < ShufTab[ResultMask].Cost) {
ShufTab[ResultMask].Cost = Cost;
ShufTab[ResultMask].Op = Op;
@@ -310,20 +313,20 @@ int main() {
ShufTab[ResultMask].Arg1 = LHS;
MadeChange = true;
}
-
+
// If this is a two input instruction, include the op(x,y) cases. If
// this is a one input instruction, skip this.
if (Op->isOnlyLHSOperator()) continue;
-
+
for (unsigned RHS = 0; RHS != 0x8889; ++RHS) {
if (!isValidMask(RHS)) continue;
if (ShufTab[RHS].Cost > 1000) continue;
-
+
// If nothing involving this operand could possibly be cheaper than
// what we already have, don't consider it.
if (ShufTab[RHS].Cost + 1 >= MaxCost)
continue;
-
+
// Evaluate op(LHS,RHS)
unsigned ResultMask = Op->getTransformedMask(LHS, RHS);
@@ -332,7 +335,7 @@ int main() {
ShufTab[ResultMask].Cost <= ShufTab[LHS].Cost ||
ShufTab[ResultMask].Cost <= ShufTab[RHS].Cost)
continue;
-
+
// Figure out the cost to evaluate this, knowing that CSE's only need
// to be evaluated once.
unsigned short Vals[30];
@@ -340,7 +343,7 @@ int main() {
EvaluateOps(LHS, Vals, NumVals);
EvaluateOps(RHS, Vals, NumVals);
- unsigned Cost = NumVals + 1;
+ unsigned Cost = NumVals + Op->getCost();
if (Cost < ShufTab[ResultMask].Cost) {
ShufTab[ResultMask].Cost = Cost;
ShufTab[ResultMask].Op = Op;
@@ -352,10 +355,10 @@ int main() {
}
}
}
-
+
std::cerr << "Finished Table has " << getNumEntered()
<< " entries established.\n";
-
+
unsigned CostArray[10] = { 0 };
// Compute a cost histogram.
@@ -366,33 +369,33 @@ int main() {
else
++CostArray[ShufTab[i].Cost];
}
-
+
for (unsigned i = 0; i != 9; ++i)
if (CostArray[i])
std::cout << "// " << CostArray[i] << " entries have cost " << i << "\n";
if (CostArray[9])
std::cout << "// " << CostArray[9] << " entries have higher cost!\n";
-
-
+
+
// Build up the table to emit.
std::cout << "\n// This table is 6561*4 = 26244 bytes in size.\n";
std::cout << "static const unsigned PerfectShuffleTable[6561+1] = {\n";
-
+
for (unsigned i = 0; i != 0x8889; ++i) {
if (!isValidMask(i)) continue;
-
+
// CostSat - The cost of this operation saturated to two bits.
unsigned CostSat = ShufTab[i].Cost;
if (CostSat > 4) CostSat = 4;
if (CostSat == 0) CostSat = 1;
--CostSat; // Cost is now between 0-3.
-
+
unsigned OpNum = ShufTab[i].Op ? ShufTab[i].Op->OpNum : 0;
assert(OpNum < 16 && "Too few bits to encode operation!");
-
+
unsigned LHS = getCompressedMask(ShufTab[i].Arg0);
unsigned RHS = getCompressedMask(ShufTab[i].Arg1);
-
+
// Encode this as 2 bits of saturated cost, 4 bits of opcodes, 13 bits of
// LHS, and 13 bits of RHS = 32 bits.
unsigned Val = (CostSat << 30) | (OpNum << 26) | (LHS << 13) | RHS;
@@ -417,7 +420,7 @@ int main() {
}
}
std::cout << "\n";
- }
+ }
std::cout << " 0\n};\n";
if (0) {
@@ -427,7 +430,7 @@ int main() {
if (ShufTab[i].Cost < 1000) {
PrintMask(i, std::cerr);
std::cerr << " - Cost " << ShufTab[i].Cost << " - ";
-
+
unsigned short Vals[30];
unsigned NumVals = 0;
EvaluateOps(i, Vals, NumVals);
@@ -441,8 +444,6 @@ int main() {
}
-#define GENERATE_ALTIVEC
-
#ifdef GENERATE_ALTIVEC
///===---------------------------------------------------------------------===//
@@ -495,3 +496,76 @@ vsldoi<2> the_vsldoi2("vsldoi8" , OP_VSLDOI8);
vsldoi<3> the_vsldoi3("vsldoi12", OP_VSLDOI12);
#endif
+
+#define GENERATE_NEON
+
+#ifdef GENERATE_NEON
+enum {
+ OP_COPY = 0, // Copy, used for things like <u,u,u,3> to say it is <0,1,2,3>
+ OP_VREV,
+ OP_VDUP0,
+ OP_VDUP1,
+ OP_VDUP2,
+ OP_VDUP3,
+ OP_VEXT1,
+ OP_VEXT2,
+ OP_VEXT3,
+ OP_VUZPL, // VUZP, left result
+ OP_VUZPR, // VUZP, right result
+ OP_VZIPL, // VZIP, left result
+ OP_VZIPR, // VZIP, right result
+ OP_VTRNL, // VTRN, left result
+ OP_VTRNR // VTRN, right result
+};
+
+struct vrev : public Operator {
+ vrev() : Operator(0x1032, "vrev", OP_VREV) {}
+} the_vrev;
+
+template<unsigned Elt>
+struct vdup : public Operator {
+ vdup(const char *N, unsigned Opc)
+ : Operator(MakeMask(Elt, Elt, Elt, Elt), N, Opc) {}
+};
+
+vdup<0> the_vdup0("vdup0", OP_VDUP0);
+vdup<1> the_vdup1("vdup1", OP_VDUP1);
+vdup<2> the_vdup2("vdup2", OP_VDUP2);
+vdup<3> the_vdup3("vdup3", OP_VDUP3);
+
+template<unsigned N>
+struct vext : public Operator {
+ vext(const char *Name, unsigned Opc)
+ : Operator(MakeMask(N&7, (N+1)&7, (N+2)&7, (N+3)&7), Name, Opc) {
+ }
+};
+
+vext<1> the_vext1("vext1", OP_VEXT1);
+vext<2> the_vext2("vext2", OP_VEXT2);
+vext<3> the_vext3("vext3", OP_VEXT3);
+
+struct vuzpl : public Operator {
+ vuzpl() : Operator(0x0246, "vuzpl", OP_VUZPL, 2) {}
+} the_vuzpl;
+
+struct vuzpr : public Operator {
+ vuzpr() : Operator(0x1357, "vuzpr", OP_VUZPR, 2) {}
+} the_vuzpr;
+
+struct vzipl : public Operator {
+ vzipl() : Operator(0x0415, "vzipl", OP_VZIPL, 2) {}
+} the_vzipl;
+
+struct vzipr : public Operator {
+ vzipr() : Operator(0x2637, "vzipr", OP_VZIPR, 2) {}
+} the_vzipr;
+
+struct vtrnl : public Operator {
+ vtrnl() : Operator(0x0426, "vtrnl", OP_VTRNL, 2) {}
+} the_vtrnl;
+
+struct vtrnr : public Operator {
+ vtrnr() : Operator(0x1537, "vtrnr", OP_VTRNR, 2) {}
+} the_vtrnr;
+
+#endif
diff --git a/utils/TableGen/AsmMatcherEmitter.cpp b/utils/TableGen/AsmMatcherEmitter.cpp
new file mode 100644
index 0000000..3eac9d2
--- /dev/null
+++ b/utils/TableGen/AsmMatcherEmitter.cpp
@@ -0,0 +1,1545 @@
+//===- AsmMatcherEmitter.cpp - Generate an assembly matcher ---------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This tablegen backend emits a target specifier matcher for converting parsed
+// assembly operands in the MCInst structures.
+//
+// The input to the target specific matcher is a list of literal tokens and
+// operands. The target specific parser should generally eliminate any syntax
+// which is not relevant for matching; for example, comma tokens should have
+// already been consumed and eliminated by the parser. Most instructions will
+// end up with a single literal token (the instruction name) and some number of
+// operands.
+//
+// Some example inputs, for X86:
+// 'addl' (immediate ...) (register ...)
+// 'add' (immediate ...) (memory ...)
+// 'call' '*' %epc
+//
+// The assembly matcher is responsible for converting this input into a precise
+// machine instruction (i.e., an instruction with a well defined encoding). This
+// mapping has several properties which complicate matching:
+//
+// - It may be ambiguous; many architectures can legally encode particular
+// variants of an instruction in different ways (for example, using a smaller
+// encoding for small immediates). Such ambiguities should never be
+// arbitrarily resolved by the assembler, the assembler is always responsible
+// for choosing the "best" available instruction.
+//
+// - It may depend on the subtarget or the assembler context. Instructions
+// which are invalid for the current mode, but otherwise unambiguous (e.g.,
+// an SSE instruction in a file being assembled for i486) should be accepted
+// and rejected by the assembler front end. However, if the proper encoding
+// for an instruction is dependent on the assembler context then the matcher
+// is responsible for selecting the correct machine instruction for the
+// current mode.
+//
+// The core matching algorithm attempts to exploit the regularity in most
+// instruction sets to quickly determine the set of possibly matching
+// instructions, and the simplify the generated code. Additionally, this helps
+// to ensure that the ambiguities are intentionally resolved by the user.
+//
+// The matching is divided into two distinct phases:
+//
+// 1. Classification: Each operand is mapped to the unique set which (a)
+// contains it, and (b) is the largest such subset for which a single
+// instruction could match all members.
+//
+// For register classes, we can generate these subgroups automatically. For
+// arbitrary operands, we expect the user to define the classes and their
+// relations to one another (for example, 8-bit signed immediates as a
+// subset of 32-bit immediates).
+//
+// By partitioning the operands in this way, we guarantee that for any
+// tuple of classes, any single instruction must match either all or none
+// of the sets of operands which could classify to that tuple.
+//
+// In addition, the subset relation amongst classes induces a partial order
+// on such tuples, which we use to resolve ambiguities.
+//
+// FIXME: What do we do if a crazy case shows up where this is the wrong
+// resolution?
+//
+// 2. The input can now be treated as a tuple of classes (static tokens are
+// simple singleton sets). Each such tuple should generally map to a single
+// instruction (we currently ignore cases where this isn't true, whee!!!),
+// which we can emit a simple matcher for.
+//
+//===----------------------------------------------------------------------===//
+
+#include "AsmMatcherEmitter.h"
+#include "CodeGenTarget.h"
+#include "Record.h"
+#include "llvm/ADT/OwningPtr.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include <list>
+#include <map>
+#include <set>
+using namespace llvm;
+
+static cl::opt<std::string>
+MatchPrefix("match-prefix", cl::init(""),
+ cl::desc("Only match instructions with the given prefix"));
+
+/// FlattenVariants - Flatten an .td file assembly string by selecting the
+/// variant at index \arg N.
+static std::string FlattenVariants(const std::string &AsmString,
+ unsigned N) {
+ StringRef Cur = AsmString;
+ std::string Res = "";
+
+ for (;;) {
+ // Find the start of the next variant string.
+ size_t VariantsStart = 0;
+ for (size_t e = Cur.size(); VariantsStart != e; ++VariantsStart)
+ if (Cur[VariantsStart] == '{' &&
+ (VariantsStart == 0 || (Cur[VariantsStart-1] != '$' &&
+ Cur[VariantsStart-1] != '\\')))
+ break;
+
+ // Add the prefix to the result.
+ Res += Cur.slice(0, VariantsStart);
+ if (VariantsStart == Cur.size())
+ break;
+
+ ++VariantsStart; // Skip the '{'.
+
+ // Scan to the end of the variants string.
+ size_t VariantsEnd = VariantsStart;
+ unsigned NestedBraces = 1;
+ for (size_t e = Cur.size(); VariantsEnd != e; ++VariantsEnd) {
+ if (Cur[VariantsEnd] == '}' && Cur[VariantsEnd-1] != '\\') {
+ if (--NestedBraces == 0)
+ break;
+ } else if (Cur[VariantsEnd] == '{')
+ ++NestedBraces;
+ }
+
+ // Select the Nth variant (or empty).
+ StringRef Selection = Cur.slice(VariantsStart, VariantsEnd);
+ for (unsigned i = 0; i != N; ++i)
+ Selection = Selection.split('|').second;
+ Res += Selection.split('|').first;
+
+ assert(VariantsEnd != Cur.size() &&
+ "Unterminated variants in assembly string!");
+ Cur = Cur.substr(VariantsEnd + 1);
+ }
+
+ return Res;
+}
+
+/// TokenizeAsmString - Tokenize a simplified assembly string.
+static void TokenizeAsmString(const StringRef &AsmString,
+ SmallVectorImpl<StringRef> &Tokens) {
+ unsigned Prev = 0;
+ bool InTok = true;
+ for (unsigned i = 0, e = AsmString.size(); i != e; ++i) {
+ switch (AsmString[i]) {
+ case '[':
+ case ']':
+ case '*':
+ case '!':
+ case ' ':
+ case '\t':
+ case ',':
+ if (InTok) {
+ Tokens.push_back(AsmString.slice(Prev, i));
+ InTok = false;
+ }
+ if (!isspace(AsmString[i]) && AsmString[i] != ',')
+ Tokens.push_back(AsmString.substr(i, 1));
+ Prev = i + 1;
+ break;
+
+ case '\\':
+ if (InTok) {
+ Tokens.push_back(AsmString.slice(Prev, i));
+ InTok = false;
+ }
+ ++i;
+ assert(i != AsmString.size() && "Invalid quoted character");
+ Tokens.push_back(AsmString.substr(i, 1));
+ Prev = i + 1;
+ break;
+
+ case '$': {
+ // If this isn't "${", treat like a normal token.
+ if (i + 1 == AsmString.size() || AsmString[i + 1] != '{') {
+ if (InTok) {
+ Tokens.push_back(AsmString.slice(Prev, i));
+ InTok = false;
+ }
+ Prev = i;
+ break;
+ }
+
+ if (InTok) {
+ Tokens.push_back(AsmString.slice(Prev, i));
+ InTok = false;
+ }
+
+ StringRef::iterator End =
+ std::find(AsmString.begin() + i, AsmString.end(), '}');
+ assert(End != AsmString.end() && "Missing brace in operand reference!");
+ size_t EndPos = End - AsmString.begin();
+ Tokens.push_back(AsmString.slice(i, EndPos+1));
+ Prev = EndPos + 1;
+ i = EndPos;
+ break;
+ }
+
+ default:
+ InTok = true;
+ }
+ }
+ if (InTok && Prev != AsmString.size())
+ Tokens.push_back(AsmString.substr(Prev));
+}
+
+static bool IsAssemblerInstruction(const StringRef &Name,
+ const CodeGenInstruction &CGI,
+ const SmallVectorImpl<StringRef> &Tokens) {
+ // Ignore "codegen only" instructions.
+ if (CGI.TheDef->getValueAsBit("isCodeGenOnly"))
+ return false;
+
+ // Ignore pseudo ops.
+ //
+ // FIXME: This is a hack; can we convert these instructions to set the
+ // "codegen only" bit instead?
+ if (const RecordVal *Form = CGI.TheDef->getValue("Form"))
+ if (Form->getValue()->getAsString() == "Pseudo")
+ return false;
+
+ // Ignore "Int_*" and "*_Int" instructions, which are internal aliases.
+ //
+ // FIXME: This is a total hack.
+ if (StringRef(Name).startswith("Int_") || StringRef(Name).endswith("_Int"))
+ return false;
+
+ // Ignore instructions with no .s string.
+ //
+ // FIXME: What are these?
+ if (CGI.AsmString.empty())
+ return false;
+
+ // FIXME: Hack; ignore any instructions with a newline in them.
+ if (std::find(CGI.AsmString.begin(),
+ CGI.AsmString.end(), '\n') != CGI.AsmString.end())
+ return false;
+
+ // Ignore instructions with attributes, these are always fake instructions for
+ // simplifying codegen.
+ //
+ // FIXME: Is this true?
+ //
+ // Also, check for instructions which reference the operand multiple times;
+ // this implies a constraint we would not honor.
+ std::set<std::string> OperandNames;
+ for (unsigned i = 1, e = Tokens.size(); i < e; ++i) {
+ if (Tokens[i][0] == '$' &&
+ std::find(Tokens[i].begin(),
+ Tokens[i].end(), ':') != Tokens[i].end()) {
+ DEBUG({
+ errs() << "warning: '" << Name << "': "
+ << "ignoring instruction; operand with attribute '"
+ << Tokens[i] << "'\n";
+ });
+ return false;
+ }
+
+ if (Tokens[i][0] == '$' && !OperandNames.insert(Tokens[i]).second) {
+ std::string Err = "'" + Name.str() + "': " +
+ "invalid assembler instruction; tied operand '" + Tokens[i].str() + "'";
+ throw TGError(CGI.TheDef->getLoc(), Err);
+ }
+ }
+
+ return true;
+}
+
+namespace {
+
+/// ClassInfo - Helper class for storing the information about a particular
+/// class of operands which can be matched.
+struct ClassInfo {
+ enum ClassInfoKind {
+ /// Invalid kind, for use as a sentinel value.
+ Invalid = 0,
+
+ /// The class for a particular token.
+ Token,
+
+ /// The (first) register class, subsequent register classes are
+ /// RegisterClass0+1, and so on.
+ RegisterClass0,
+
+ /// The (first) user defined class, subsequent user defined classes are
+ /// UserClass0+1, and so on.
+ UserClass0 = 1<<16
+ };
+
+ /// Kind - The class kind, which is either a predefined kind, or (UserClass0 +
+ /// N) for the Nth user defined class.
+ unsigned Kind;
+
+ /// SuperClasses - The super classes of this class. Note that for simplicities
+ /// sake user operands only record their immediate super class, while register
+ /// operands include all superclasses.
+ std::vector<ClassInfo*> SuperClasses;
+
+ /// Name - The full class name, suitable for use in an enum.
+ std::string Name;
+
+ /// ClassName - The unadorned generic name for this class (e.g., Token).
+ std::string ClassName;
+
+ /// ValueName - The name of the value this class represents; for a token this
+ /// is the literal token string, for an operand it is the TableGen class (or
+ /// empty if this is a derived class).
+ std::string ValueName;
+
+ /// PredicateMethod - The name of the operand method to test whether the
+ /// operand matches this class; this is not valid for Token or register kinds.
+ std::string PredicateMethod;
+
+ /// RenderMethod - The name of the operand method to add this operand to an
+ /// MCInst; this is not valid for Token or register kinds.
+ std::string RenderMethod;
+
+ /// For register classes, the records for all the registers in this class.
+ std::set<Record*> Registers;
+
+public:
+ /// isRegisterClass() - Check if this is a register class.
+ bool isRegisterClass() const {
+ return Kind >= RegisterClass0 && Kind < UserClass0;
+ }
+
+ /// isUserClass() - Check if this is a user defined class.
+ bool isUserClass() const {
+ return Kind >= UserClass0;
+ }
+
+ /// isRelatedTo - Check whether this class is "related" to \arg RHS. Classes
+ /// are related if they are in the same class hierarchy.
+ bool isRelatedTo(const ClassInfo &RHS) const {
+ // Tokens are only related to tokens.
+ if (Kind == Token || RHS.Kind == Token)
+ return Kind == Token && RHS.Kind == Token;
+
+ // Registers classes are only related to registers classes, and only if
+ // their intersection is non-empty.
+ if (isRegisterClass() || RHS.isRegisterClass()) {
+ if (!isRegisterClass() || !RHS.isRegisterClass())
+ return false;
+
+ std::set<Record*> Tmp;
+ std::insert_iterator< std::set<Record*> > II(Tmp, Tmp.begin());
+ std::set_intersection(Registers.begin(), Registers.end(),
+ RHS.Registers.begin(), RHS.Registers.end(),
+ II);
+
+ return !Tmp.empty();
+ }
+
+ // Otherwise we have two users operands; they are related if they are in the
+ // same class hierarchy.
+ //
+ // FIXME: This is an oversimplification, they should only be related if they
+ // intersect, however we don't have that information.
+ assert(isUserClass() && RHS.isUserClass() && "Unexpected class!");
+ const ClassInfo *Root = this;
+ while (!Root->SuperClasses.empty())
+ Root = Root->SuperClasses.front();
+
+ const ClassInfo *RHSRoot = &RHS;
+ while (!RHSRoot->SuperClasses.empty())
+ RHSRoot = RHSRoot->SuperClasses.front();
+
+ return Root == RHSRoot;
+ }
+
+ /// isSubsetOf - Test whether this class is a subset of \arg RHS;
+ bool isSubsetOf(const ClassInfo &RHS) const {
+ // This is a subset of RHS if it is the same class...
+ if (this == &RHS)
+ return true;
+
+ // ... or if any of its super classes are a subset of RHS.
+ for (std::vector<ClassInfo*>::const_iterator it = SuperClasses.begin(),
+ ie = SuperClasses.end(); it != ie; ++it)
+ if ((*it)->isSubsetOf(RHS))
+ return true;
+
+ return false;
+ }
+
+ /// operator< - Compare two classes.
+ bool operator<(const ClassInfo &RHS) const {
+ // Unrelated classes can be ordered by kind.
+ if (!isRelatedTo(RHS))
+ return Kind < RHS.Kind;
+
+ switch (Kind) {
+ case Invalid:
+ assert(0 && "Invalid kind!");
+ case Token:
+ // Tokens are comparable by value.
+ //
+ // FIXME: Compare by enum value.
+ return ValueName < RHS.ValueName;
+
+ default:
+ // This class preceeds the RHS if it is a proper subset of the RHS.
+ return this != &RHS && isSubsetOf(RHS);
+ }
+ }
+};
+
+/// InstructionInfo - Helper class for storing the necessary information for an
+/// instruction which is capable of being matched.
+struct InstructionInfo {
+ struct Operand {
+ /// The unique class instance this operand should match.
+ ClassInfo *Class;
+
+ /// The original operand this corresponds to, if any.
+ const CodeGenInstruction::OperandInfo *OperandInfo;
+ };
+
+ /// InstrName - The target name for this instruction.
+ std::string InstrName;
+
+ /// Instr - The instruction this matches.
+ const CodeGenInstruction *Instr;
+
+ /// AsmString - The assembly string for this instruction (with variants
+ /// removed).
+ std::string AsmString;
+
+ /// Tokens - The tokenized assembly pattern that this instruction matches.
+ SmallVector<StringRef, 4> Tokens;
+
+ /// Operands - The operands that this instruction matches.
+ SmallVector<Operand, 4> Operands;
+
+ /// ConversionFnKind - The enum value which is passed to the generated
+ /// ConvertToMCInst to convert parsed operands into an MCInst for this
+ /// function.
+ std::string ConversionFnKind;
+
+ /// operator< - Compare two instructions.
+ bool operator<(const InstructionInfo &RHS) const {
+ if (Operands.size() != RHS.Operands.size())
+ return Operands.size() < RHS.Operands.size();
+
+ // Compare lexicographically by operand. The matcher validates that other
+ // orderings wouldn't be ambiguous using \see CouldMatchAmiguouslyWith().
+ for (unsigned i = 0, e = Operands.size(); i != e; ++i) {
+ if (*Operands[i].Class < *RHS.Operands[i].Class)
+ return true;
+ if (*RHS.Operands[i].Class < *Operands[i].Class)
+ return false;
+ }
+
+ return false;
+ }
+
+ /// CouldMatchAmiguouslyWith - Check whether this instruction could
+ /// ambiguously match the same set of operands as \arg RHS (without being a
+ /// strictly superior match).
+ bool CouldMatchAmiguouslyWith(const InstructionInfo &RHS) {
+ // The number of operands is unambiguous.
+ if (Operands.size() != RHS.Operands.size())
+ return false;
+
+ // Tokens and operand kinds are unambiguous (assuming a correct target
+ // specific parser).
+ for (unsigned i = 0, e = Operands.size(); i != e; ++i)
+ if (Operands[i].Class->Kind != RHS.Operands[i].Class->Kind ||
+ Operands[i].Class->Kind == ClassInfo::Token)
+ if (*Operands[i].Class < *RHS.Operands[i].Class ||
+ *RHS.Operands[i].Class < *Operands[i].Class)
+ return false;
+
+ // Otherwise, this operand could commute if all operands are equivalent, or
+ // there is a pair of operands that compare less than and a pair that
+ // compare greater than.
+ bool HasLT = false, HasGT = false;
+ for (unsigned i = 0, e = Operands.size(); i != e; ++i) {
+ if (*Operands[i].Class < *RHS.Operands[i].Class)
+ HasLT = true;
+ if (*RHS.Operands[i].Class < *Operands[i].Class)
+ HasGT = true;
+ }
+
+ return !(HasLT ^ HasGT);
+ }
+
+public:
+ void dump();
+};
+
+class AsmMatcherInfo {
+public:
+ /// The tablegen AsmParser record.
+ Record *AsmParser;
+
+ /// The AsmParser "CommentDelimiter" value.
+ std::string CommentDelimiter;
+
+ /// The AsmParser "RegisterPrefix" value.
+ std::string RegisterPrefix;
+
+ /// The classes which are needed for matching.
+ std::vector<ClassInfo*> Classes;
+
+ /// The information on the instruction to match.
+ std::vector<InstructionInfo*> Instructions;
+
+ /// Map of Register records to their class information.
+ std::map<Record*, ClassInfo*> RegisterClasses;
+
+private:
+ /// Map of token to class information which has already been constructed.
+ std::map<std::string, ClassInfo*> TokenClasses;
+
+ /// Map of RegisterClass records to their class information.
+ std::map<Record*, ClassInfo*> RegisterClassClasses;
+
+ /// Map of AsmOperandClass records to their class information.
+ std::map<Record*, ClassInfo*> AsmOperandClasses;
+
+private:
+ /// getTokenClass - Lookup or create the class for the given token.
+ ClassInfo *getTokenClass(const StringRef &Token);
+
+ /// getOperandClass - Lookup or create the class for the given operand.
+ ClassInfo *getOperandClass(const StringRef &Token,
+ const CodeGenInstruction::OperandInfo &OI);
+
+ /// BuildRegisterClasses - Build the ClassInfo* instances for register
+ /// classes.
+ void BuildRegisterClasses(CodeGenTarget &Target,
+ std::set<std::string> &SingletonRegisterNames);
+
+ /// BuildOperandClasses - Build the ClassInfo* instances for user defined
+ /// operand classes.
+ void BuildOperandClasses(CodeGenTarget &Target);
+
+public:
+ AsmMatcherInfo(Record *_AsmParser);
+
+ /// BuildInfo - Construct the various tables used during matching.
+ void BuildInfo(CodeGenTarget &Target);
+};
+
+}
+
+void InstructionInfo::dump() {
+ errs() << InstrName << " -- " << "flattened:\"" << AsmString << '\"'
+ << ", tokens:[";
+ for (unsigned i = 0, e = Tokens.size(); i != e; ++i) {
+ errs() << Tokens[i];
+ if (i + 1 != e)
+ errs() << ", ";
+ }
+ errs() << "]\n";
+
+ for (unsigned i = 0, e = Operands.size(); i != e; ++i) {
+ Operand &Op = Operands[i];
+ errs() << " op[" << i << "] = " << Op.Class->ClassName << " - ";
+ if (Op.Class->Kind == ClassInfo::Token) {
+ errs() << '\"' << Tokens[i] << "\"\n";
+ continue;
+ }
+
+ if (!Op.OperandInfo) {
+ errs() << "(singleton register)\n";
+ continue;
+ }
+
+ const CodeGenInstruction::OperandInfo &OI = *Op.OperandInfo;
+ errs() << OI.Name << " " << OI.Rec->getName()
+ << " (" << OI.MIOperandNo << ", " << OI.MINumOperands << ")\n";
+ }
+}
+
+static std::string getEnumNameForToken(const StringRef &Str) {
+ std::string Res;
+
+ for (StringRef::iterator it = Str.begin(), ie = Str.end(); it != ie; ++it) {
+ switch (*it) {
+ case '*': Res += "_STAR_"; break;
+ case '%': Res += "_PCT_"; break;
+ case ':': Res += "_COLON_"; break;
+
+ default:
+ if (isalnum(*it)) {
+ Res += *it;
+ } else {
+ Res += "_" + utostr((unsigned) *it) + "_";
+ }
+ }
+ }
+
+ return Res;
+}
+
+/// getRegisterRecord - Get the register record for \arg name, or 0.
+static Record *getRegisterRecord(CodeGenTarget &Target, const StringRef &Name) {
+ for (unsigned i = 0, e = Target.getRegisters().size(); i != e; ++i) {
+ const CodeGenRegister &Reg = Target.getRegisters()[i];
+ if (Name == Reg.TheDef->getValueAsString("AsmName"))
+ return Reg.TheDef;
+ }
+
+ return 0;
+}
+
+ClassInfo *AsmMatcherInfo::getTokenClass(const StringRef &Token) {
+ ClassInfo *&Entry = TokenClasses[Token];
+
+ if (!Entry) {
+ Entry = new ClassInfo();
+ Entry->Kind = ClassInfo::Token;
+ Entry->ClassName = "Token";
+ Entry->Name = "MCK_" + getEnumNameForToken(Token);
+ Entry->ValueName = Token;
+ Entry->PredicateMethod = "<invalid>";
+ Entry->RenderMethod = "<invalid>";
+ Classes.push_back(Entry);
+ }
+
+ return Entry;
+}
+
+ClassInfo *
+AsmMatcherInfo::getOperandClass(const StringRef &Token,
+ const CodeGenInstruction::OperandInfo &OI) {
+ if (OI.Rec->isSubClassOf("RegisterClass")) {
+ ClassInfo *CI = RegisterClassClasses[OI.Rec];
+
+ if (!CI) {
+ PrintError(OI.Rec->getLoc(), "register class has no class info!");
+ throw std::string("ERROR: Missing register class!");
+ }
+
+ return CI;
+ }
+
+ assert(OI.Rec->isSubClassOf("Operand") && "Unexpected operand!");
+ Record *MatchClass = OI.Rec->getValueAsDef("ParserMatchClass");
+ ClassInfo *CI = AsmOperandClasses[MatchClass];
+
+ if (!CI) {
+ PrintError(OI.Rec->getLoc(), "operand has no match class!");
+ throw std::string("ERROR: Missing match class!");
+ }
+
+ return CI;
+}
+
+void AsmMatcherInfo::BuildRegisterClasses(CodeGenTarget &Target,
+ std::set<std::string>
+ &SingletonRegisterNames) {
+ std::vector<CodeGenRegisterClass> RegisterClasses;
+ std::vector<CodeGenRegister> Registers;
+
+ RegisterClasses = Target.getRegisterClasses();
+ Registers = Target.getRegisters();
+
+ // The register sets used for matching.
+ std::set< std::set<Record*> > RegisterSets;
+
+ // Gather the defined sets.
+ for (std::vector<CodeGenRegisterClass>::iterator it = RegisterClasses.begin(),
+ ie = RegisterClasses.end(); it != ie; ++it)
+ RegisterSets.insert(std::set<Record*>(it->Elements.begin(),
+ it->Elements.end()));
+
+ // Add any required singleton sets.
+ for (std::set<std::string>::iterator it = SingletonRegisterNames.begin(),
+ ie = SingletonRegisterNames.end(); it != ie; ++it)
+ if (Record *Rec = getRegisterRecord(Target, *it))
+ RegisterSets.insert(std::set<Record*>(&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;
+ for (std::vector<CodeGenRegister>::iterator it = Registers.begin(),
+ ie = Registers.end(); it != ie; ++it) {
+ CodeGenRegister &CGR = *it;
+ // Compute the intersection of all sets containing this register.
+ std::set<Record*> ContainingSet;
+
+ for (std::set< std::set<Record*> >::iterator it = RegisterSets.begin(),
+ ie = RegisterSets.end(); it != ie; ++it) {
+ if (!it->count(CGR.TheDef))
+ continue;
+
+ if (ContainingSet.empty()) {
+ ContainingSet = *it;
+ } else {
+ std::set<Record*> 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);
+ }
+ }
+
+ if (!ContainingSet.empty()) {
+ RegisterSets.insert(ContainingSet);
+ RegisterMap.insert(std::make_pair(CGR.TheDef, ContainingSet));
+ }
+ }
+
+ // Construct the register classes.
+ std::map<std::set<Record*>, ClassInfo*> RegisterSetClasses;
+ unsigned Index = 0;
+ for (std::set< std::set<Record*> >::iterator it = RegisterSets.begin(),
+ ie = RegisterSets.end(); it != ie; ++it, ++Index) {
+ ClassInfo *CI = new ClassInfo();
+ CI->Kind = ClassInfo::RegisterClass0 + Index;
+ CI->ClassName = "Reg" + utostr(Index);
+ CI->Name = "MCK_Reg" + utostr(Index);
+ CI->ValueName = "";
+ CI->PredicateMethod = ""; // unused
+ CI->RenderMethod = "addRegOperands";
+ CI->Registers = *it;
+ Classes.push_back(CI);
+ RegisterSetClasses.insert(std::make_pair(*it, CI));
+ }
+
+ // 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(),
+ ie = RegisterSets.end(); it != ie; ++it) {
+ ClassInfo *CI = RegisterSetClasses[*it];
+ for (std::set< std::set<Record*> >::iterator it2 = RegisterSets.begin(),
+ ie2 = RegisterSets.end(); it2 != ie2; ++it2)
+ if (*it != *it2 &&
+ std::includes(it2->begin(), it2->end(), it->begin(), it->end()))
+ CI->SuperClasses.push_back(RegisterSetClasses[*it2]);
+ }
+
+ // Name the register classes which correspond to a user defined RegisterClass.
+ for (std::vector<CodeGenRegisterClass>::iterator it = RegisterClasses.begin(),
+ ie = RegisterClasses.end(); it != ie; ++it) {
+ ClassInfo *CI = RegisterSetClasses[std::set<Record*>(it->Elements.begin(),
+ it->Elements.end())];
+ if (CI->ValueName.empty()) {
+ CI->ClassName = it->getName();
+ CI->Name = "MCK_" + it->getName();
+ CI->ValueName = it->getName();
+ } else
+ CI->ValueName = CI->ValueName + "," + it->getName();
+
+ RegisterClassClasses.insert(std::make_pair(it->TheDef, CI));
+ }
+
+ // Populate the map for individual registers.
+ for (std::map<Record*, std::set<Record*> >::iterator it = RegisterMap.begin(),
+ ie = RegisterMap.end(); it != ie; ++it)
+ this->RegisterClasses[it->first] = RegisterSetClasses[it->second];
+
+ // Name the register classes which correspond to singleton registers.
+ for (std::set<std::string>::iterator it = SingletonRegisterNames.begin(),
+ ie = SingletonRegisterNames.end(); it != ie; ++it) {
+ if (Record *Rec = getRegisterRecord(Target, *it)) {
+ ClassInfo *CI = this->RegisterClasses[Rec];
+ assert(CI && "Missing singleton register class info!");
+
+ if (CI->ValueName.empty()) {
+ CI->ClassName = Rec->getName();
+ CI->Name = "MCK_" + Rec->getName();
+ CI->ValueName = Rec->getName();
+ } else
+ CI->ValueName = CI->ValueName + "," + Rec->getName();
+ }
+ }
+}
+
+void AsmMatcherInfo::BuildOperandClasses(CodeGenTarget &Target) {
+ std::vector<Record*> AsmOperands;
+ AsmOperands = Records.getAllDerivedDefinitions("AsmOperandClass");
+ unsigned Index = 0;
+ for (std::vector<Record*>::iterator it = AsmOperands.begin(),
+ ie = AsmOperands.end(); it != ie; ++it, ++Index) {
+ ClassInfo *CI = new ClassInfo();
+ CI->Kind = ClassInfo::UserClass0 + Index;
+
+ Init *Super = (*it)->getValueInit("SuperClass");
+ if (DefInit *DI = dynamic_cast<DefInit*>(Super)) {
+ ClassInfo *SC = AsmOperandClasses[DI->getDef()];
+ if (!SC)
+ PrintError((*it)->getLoc(), "Invalid super class reference!");
+ else
+ CI->SuperClasses.push_back(SC);
+ } else {
+ assert(dynamic_cast<UnsetInit*>(Super) && "Unexpected SuperClass field!");
+ }
+ CI->ClassName = (*it)->getValueAsString("Name");
+ CI->Name = "MCK_" + CI->ClassName;
+ CI->ValueName = (*it)->getName();
+
+ // Get or construct the predicate method name.
+ Init *PMName = (*it)->getValueInit("PredicateMethod");
+ if (StringInit *SI = dynamic_cast<StringInit*>(PMName)) {
+ CI->PredicateMethod = SI->getValue();
+ } else {
+ assert(dynamic_cast<UnsetInit*>(PMName) &&
+ "Unexpected PredicateMethod field!");
+ CI->PredicateMethod = "is" + CI->ClassName;
+ }
+
+ // Get or construct the render method name.
+ Init *RMName = (*it)->getValueInit("RenderMethod");
+ if (StringInit *SI = dynamic_cast<StringInit*>(RMName)) {
+ CI->RenderMethod = SI->getValue();
+ } else {
+ assert(dynamic_cast<UnsetInit*>(RMName) &&
+ "Unexpected RenderMethod field!");
+ CI->RenderMethod = "add" + CI->ClassName + "Operands";
+ }
+
+ AsmOperandClasses[*it] = CI;
+ Classes.push_back(CI);
+ }
+}
+
+AsmMatcherInfo::AsmMatcherInfo(Record *_AsmParser)
+ : AsmParser(_AsmParser),
+ CommentDelimiter(AsmParser->getValueAsString("CommentDelimiter")),
+ RegisterPrefix(AsmParser->getValueAsString("RegisterPrefix"))
+{
+}
+
+void AsmMatcherInfo::BuildInfo(CodeGenTarget &Target) {
+ // Parse the instructions; we need to do this first so that we can gather the
+ // singleton register classes.
+ std::set<std::string> SingletonRegisterNames;
+ for (std::map<std::string, CodeGenInstruction>::const_iterator
+ it = Target.getInstructions().begin(),
+ ie = Target.getInstructions().end();
+ it != ie; ++it) {
+ const CodeGenInstruction &CGI = it->second;
+
+ if (!StringRef(it->first).startswith(MatchPrefix))
+ continue;
+
+ OwningPtr<InstructionInfo> II(new InstructionInfo);
+
+ II->InstrName = it->first;
+ II->Instr = &it->second;
+ II->AsmString = FlattenVariants(CGI.AsmString, 0);
+
+ // Remove comments from the asm string.
+ if (!CommentDelimiter.empty()) {
+ size_t Idx = StringRef(II->AsmString).find(CommentDelimiter);
+ if (Idx != StringRef::npos)
+ II->AsmString = II->AsmString.substr(0, Idx);
+ }
+
+ TokenizeAsmString(II->AsmString, II->Tokens);
+
+ // Ignore instructions which shouldn't be matched.
+ if (!IsAssemblerInstruction(it->first, CGI, II->Tokens))
+ continue;
+
+ // Collect singleton registers, if used.
+ if (!RegisterPrefix.empty()) {
+ for (unsigned i = 0, e = II->Tokens.size(); i != e; ++i) {
+ if (II->Tokens[i].startswith(RegisterPrefix)) {
+ StringRef RegName = II->Tokens[i].substr(RegisterPrefix.size());
+ Record *Rec = getRegisterRecord(Target, RegName);
+
+ if (!Rec) {
+ std::string Err = "unable to find register for '" + RegName.str() +
+ "' (which matches register prefix)";
+ throw TGError(CGI.TheDef->getLoc(), Err);
+ }
+
+ SingletonRegisterNames.insert(RegName);
+ }
+ }
+ }
+
+ Instructions.push_back(II.take());
+ }
+
+ // Build info for the register classes.
+ BuildRegisterClasses(Target, SingletonRegisterNames);
+
+ // Build info for the user defined assembly operand classes.
+ BuildOperandClasses(Target);
+
+ // Build the instruction information.
+ for (std::vector<InstructionInfo*>::iterator it = Instructions.begin(),
+ ie = Instructions.end(); it != ie; ++it) {
+ InstructionInfo *II = *it;
+
+ for (unsigned i = 0, e = II->Tokens.size(); i != e; ++i) {
+ StringRef Token = II->Tokens[i];
+
+ // Check for singleton registers.
+ if (!RegisterPrefix.empty() && Token.startswith(RegisterPrefix)) {
+ StringRef RegName = II->Tokens[i].substr(RegisterPrefix.size());
+ InstructionInfo::Operand Op;
+ Op.Class = RegisterClasses[getRegisterRecord(Target, RegName)];
+ Op.OperandInfo = 0;
+ assert(Op.Class && Op.Class->Registers.size() == 1 &&
+ "Unexpected class for singleton register");
+ II->Operands.push_back(Op);
+ continue;
+ }
+
+ // Check for simple tokens.
+ if (Token[0] != '$') {
+ InstructionInfo::Operand Op;
+ Op.Class = getTokenClass(Token);
+ Op.OperandInfo = 0;
+ II->Operands.push_back(Op);
+ continue;
+ }
+
+ // Otherwise this is an operand reference.
+ StringRef OperandName;
+ if (Token[1] == '{')
+ OperandName = Token.substr(2, Token.size() - 3);
+ else
+ OperandName = Token.substr(1);
+
+ // Map this token to an operand. FIXME: Move elsewhere.
+ unsigned Idx;
+ try {
+ Idx = II->Instr->getOperandNamed(OperandName);
+ } catch(...) {
+ throw std::string("error: unable to find operand: '" +
+ OperandName.str() + "'");
+ }
+
+ const CodeGenInstruction::OperandInfo &OI = II->Instr->OperandList[Idx];
+ InstructionInfo::Operand Op;
+ Op.Class = getOperandClass(Token, OI);
+ Op.OperandInfo = &OI;
+ II->Operands.push_back(Op);
+ }
+ }
+
+ // Reorder classes so that classes preceed super classes.
+ std::sort(Classes.begin(), Classes.end(), less_ptr<ClassInfo>());
+}
+
+static void EmitConvertToMCInst(CodeGenTarget &Target,
+ std::vector<InstructionInfo*> &Infos,
+ raw_ostream &OS) {
+ // Write the convert function to a separate stream, so we can drop it after
+ // the enum.
+ std::string ConvertFnBody;
+ raw_string_ostream CvtOS(ConvertFnBody);
+
+ // Function we have already generated.
+ std::set<std::string> GeneratedFns;
+
+ // Start the unified conversion function.
+
+ CvtOS << "static bool ConvertToMCInst(ConversionKind Kind, MCInst &Inst, "
+ << "unsigned Opcode,\n"
+ << " SmallVectorImpl<"
+ << Target.getName() << "Operand> &Operands) {\n";
+ CvtOS << " Inst.setOpcode(Opcode);\n";
+ CvtOS << " switch (Kind) {\n";
+ CvtOS << " default:\n";
+
+ // Start the enum, which we will generate inline.
+
+ OS << "// Unified function for converting operants to MCInst instances.\n\n";
+ OS << "enum ConversionKind {\n";
+
+ for (std::vector<InstructionInfo*>::const_iterator it = Infos.begin(),
+ ie = Infos.end(); it != ie; ++it) {
+ InstructionInfo &II = **it;
+
+ // Order the (class) operands by the order to convert them into an MCInst.
+ SmallVector<std::pair<unsigned, unsigned>, 4> MIOperandList;
+ for (unsigned i = 0, e = II.Operands.size(); i != e; ++i) {
+ InstructionInfo::Operand &Op = II.Operands[i];
+ if (Op.OperandInfo)
+ MIOperandList.push_back(std::make_pair(Op.OperandInfo->MIOperandNo, i));
+ }
+ std::sort(MIOperandList.begin(), MIOperandList.end());
+
+ // Compute the total number of operands.
+ unsigned NumMIOperands = 0;
+ for (unsigned i = 0, e = II.Instr->OperandList.size(); i != e; ++i) {
+ const CodeGenInstruction::OperandInfo &OI = II.Instr->OperandList[i];
+ NumMIOperands = std::max(NumMIOperands,
+ OI.MIOperandNo + OI.MINumOperands);
+ }
+
+ // Build the conversion function signature.
+ std::string Signature = "Convert";
+ unsigned CurIndex = 0;
+ for (unsigned i = 0, e = MIOperandList.size(); i != e; ++i) {
+ InstructionInfo::Operand &Op = II.Operands[MIOperandList[i].second];
+ assert(CurIndex <= Op.OperandInfo->MIOperandNo &&
+ "Duplicate match for instruction operand!");
+
+ Signature += "_";
+
+ // Skip operands which weren't matched by anything, this occurs when the
+ // .td file encodes "implicit" operands as explicit ones.
+ //
+ // FIXME: This should be removed from the MCInst structure.
+ for (; CurIndex != Op.OperandInfo->MIOperandNo; ++CurIndex)
+ Signature += "Imp";
+
+ // Registers are always converted the same, don't duplicate the conversion
+ // function based on them.
+ //
+ // FIXME: We could generalize this based on the render method, if it
+ // mattered.
+ if (Op.Class->isRegisterClass())
+ Signature += "Reg";
+ else
+ Signature += Op.Class->ClassName;
+ Signature += utostr(Op.OperandInfo->MINumOperands);
+ Signature += "_" + utostr(MIOperandList[i].second);
+
+ CurIndex += Op.OperandInfo->MINumOperands;
+ }
+
+ // Add any trailing implicit operands.
+ for (; CurIndex != NumMIOperands; ++CurIndex)
+ Signature += "Imp";
+
+ II.ConversionFnKind = Signature;
+
+ // Check if we have already generated this signature.
+ if (!GeneratedFns.insert(Signature).second)
+ continue;
+
+ // If not, emit it now.
+
+ // Add to the enum list.
+ OS << " " << Signature << ",\n";
+
+ // And to the convert function.
+ CvtOS << " case " << Signature << ":\n";
+ CurIndex = 0;
+ for (unsigned i = 0, e = MIOperandList.size(); i != e; ++i) {
+ InstructionInfo::Operand &Op = II.Operands[MIOperandList[i].second];
+
+ // Add the implicit operands.
+ for (; CurIndex != Op.OperandInfo->MIOperandNo; ++CurIndex)
+ CvtOS << " Inst.addOperand(MCOperand::CreateReg(0));\n";
+
+ CvtOS << " Operands[" << MIOperandList[i].second
+ << "]." << Op.Class->RenderMethod
+ << "(Inst, " << Op.OperandInfo->MINumOperands << ");\n";
+ CurIndex += Op.OperandInfo->MINumOperands;
+ }
+
+ // And add trailing implicit operands.
+ for (; CurIndex != NumMIOperands; ++CurIndex)
+ CvtOS << " Inst.addOperand(MCOperand::CreateReg(0));\n";
+ CvtOS << " break;\n";
+ }
+
+ // Finish the convert function.
+
+ CvtOS << " }\n";
+ CvtOS << " return false;\n";
+ CvtOS << "}\n\n";
+
+ // Finish the enum, and drop the convert function after it.
+
+ OS << " NumConversionVariants\n";
+ OS << "};\n\n";
+
+ OS << CvtOS.str();
+}
+
+/// EmitMatchClassEnumeration - Emit the enumeration for match class kinds.
+static void EmitMatchClassEnumeration(CodeGenTarget &Target,
+ std::vector<ClassInfo*> &Infos,
+ raw_ostream &OS) {
+ OS << "namespace {\n\n";
+
+ OS << "/// MatchClassKind - The kinds of classes which participate in\n"
+ << "/// instruction matching.\n";
+ OS << "enum MatchClassKind {\n";
+ OS << " InvalidMatchClass = 0,\n";
+ for (std::vector<ClassInfo*>::iterator it = Infos.begin(),
+ ie = Infos.end(); it != ie; ++it) {
+ ClassInfo &CI = **it;
+ OS << " " << CI.Name << ", // ";
+ if (CI.Kind == ClassInfo::Token) {
+ OS << "'" << CI.ValueName << "'\n";
+ } else if (CI.isRegisterClass()) {
+ if (!CI.ValueName.empty())
+ OS << "register class '" << CI.ValueName << "'\n";
+ else
+ OS << "derived register class\n";
+ } else {
+ OS << "user defined class '" << CI.ValueName << "'\n";
+ }
+ }
+ OS << " NumMatchClassKinds\n";
+ OS << "};\n\n";
+
+ OS << "}\n\n";
+}
+
+/// EmitClassifyOperand - Emit the function to classify an operand.
+static void EmitClassifyOperand(CodeGenTarget &Target,
+ AsmMatcherInfo &Info,
+ raw_ostream &OS) {
+ OS << "static MatchClassKind ClassifyOperand("
+ << Target.getName() << "Operand &Operand) {\n";
+
+ // Classify tokens.
+ OS << " if (Operand.isToken())\n";
+ OS << " return MatchTokenString(Operand.getToken());\n\n";
+
+ // Classify registers.
+ //
+ // FIXME: Don't hardcode isReg, getReg.
+ OS << " if (Operand.isReg()) {\n";
+ OS << " switch (Operand.getReg()) {\n";
+ OS << " default: return InvalidMatchClass;\n";
+ for (std::map<Record*, ClassInfo*>::iterator
+ it = Info.RegisterClasses.begin(), ie = Info.RegisterClasses.end();
+ it != ie; ++it)
+ OS << " case " << Target.getName() << "::"
+ << it->first->getName() << ": return " << it->second->Name << ";\n";
+ OS << " }\n";
+ OS << " }\n\n";
+
+ // Classify user defined operands.
+ for (std::vector<ClassInfo*>::iterator it = Info.Classes.begin(),
+ ie = Info.Classes.end(); it != ie; ++it) {
+ ClassInfo &CI = **it;
+
+ if (!CI.isUserClass())
+ continue;
+
+ OS << " // '" << CI.ClassName << "' class";
+ if (!CI.SuperClasses.empty()) {
+ OS << ", subclass of ";
+ for (unsigned i = 0, e = CI.SuperClasses.size(); i != e; ++i) {
+ if (i) OS << ", ";
+ OS << "'" << CI.SuperClasses[i]->ClassName << "'";
+ assert(CI < *CI.SuperClasses[i] && "Invalid class relation!");
+ }
+ }
+ OS << "\n";
+
+ OS << " if (Operand." << CI.PredicateMethod << "()) {\n";
+
+ // Validate subclass relationships.
+ if (!CI.SuperClasses.empty()) {
+ for (unsigned i = 0, e = CI.SuperClasses.size(); i != e; ++i)
+ OS << " assert(Operand." << CI.SuperClasses[i]->PredicateMethod
+ << "() && \"Invalid class relationship!\");\n";
+ }
+
+ OS << " return " << CI.Name << ";\n";
+ OS << " }\n\n";
+ }
+ OS << " return InvalidMatchClass;\n";
+ OS << "}\n\n";
+}
+
+/// EmitIsSubclass - Emit the subclass predicate function.
+static void EmitIsSubclass(CodeGenTarget &Target,
+ std::vector<ClassInfo*> &Infos,
+ raw_ostream &OS) {
+ OS << "/// IsSubclass - Compute whether \\arg A is a subclass of \\arg B.\n";
+ OS << "static bool IsSubclass(MatchClassKind A, MatchClassKind B) {\n";
+ OS << " if (A == B)\n";
+ OS << " return true;\n\n";
+
+ OS << " switch (A) {\n";
+ OS << " default:\n";
+ OS << " return false;\n";
+ for (std::vector<ClassInfo*>::iterator it = Infos.begin(),
+ ie = Infos.end(); it != ie; ++it) {
+ ClassInfo &A = **it;
+
+ if (A.Kind != ClassInfo::Token) {
+ std::vector<StringRef> SuperClasses;
+ for (std::vector<ClassInfo*>::iterator it = Infos.begin(),
+ ie = Infos.end(); it != ie; ++it) {
+ ClassInfo &B = **it;
+
+ if (&A != &B && A.isSubsetOf(B))
+ SuperClasses.push_back(B.Name);
+ }
+
+ if (SuperClasses.empty())
+ continue;
+
+ OS << "\n case " << A.Name << ":\n";
+
+ if (SuperClasses.size() == 1) {
+ OS << " return B == " << SuperClasses.back() << ";\n";
+ continue;
+ }
+
+ OS << " switch (B) {\n";
+ OS << " default: return false;\n";
+ for (unsigned i = 0, e = SuperClasses.size(); i != e; ++i)
+ OS << " case " << SuperClasses[i] << ": return true;\n";
+ OS << " }\n";
+ }
+ }
+ OS << " }\n";
+ OS << "}\n\n";
+}
+
+typedef std::pair<std::string, std::string> StringPair;
+
+/// FindFirstNonCommonLetter - Find the first character in the keys of the
+/// string pairs that is not shared across the whole set of strings. All
+/// strings are assumed to have the same length.
+static unsigned
+FindFirstNonCommonLetter(const std::vector<const StringPair*> &Matches) {
+ assert(!Matches.empty());
+ for (unsigned i = 0, e = Matches[0]->first.size(); i != e; ++i) {
+ // Check to see if letter i is the same across the set.
+ char Letter = Matches[0]->first[i];
+
+ for (unsigned str = 0, e = Matches.size(); str != e; ++str)
+ if (Matches[str]->first[i] != Letter)
+ return i;
+ }
+
+ return Matches[0]->first.size();
+}
+
+/// EmitStringMatcherForChar - Given a set of strings that are known to be the
+/// same length and whose characters leading up to CharNo are the same, emit
+/// code to verify that CharNo and later are the same.
+///
+/// \return - True if control can leave the emitted code fragment.
+static bool EmitStringMatcherForChar(const std::string &StrVariableName,
+ const std::vector<const StringPair*> &Matches,
+ unsigned CharNo, unsigned IndentCount,
+ raw_ostream &OS) {
+ assert(!Matches.empty() && "Must have at least one string to match!");
+ std::string Indent(IndentCount*2+4, ' ');
+
+ // If we have verified that the entire string matches, we're done: output the
+ // matching code.
+ if (CharNo == Matches[0]->first.size()) {
+ assert(Matches.size() == 1 && "Had duplicate keys to match on");
+
+ // FIXME: If Matches[0].first has embeded \n, this will be bad.
+ OS << Indent << Matches[0]->second << "\t // \"" << Matches[0]->first
+ << "\"\n";
+ return false;
+ }
+
+ // Bucket the matches by the character we are comparing.
+ std::map<char, std::vector<const StringPair*> > MatchesByLetter;
+
+ for (unsigned i = 0, e = Matches.size(); i != e; ++i)
+ MatchesByLetter[Matches[i]->first[CharNo]].push_back(Matches[i]);
+
+
+ // If we have exactly one bucket to match, see how many characters are common
+ // across the whole set and match all of them at once.
+ if (MatchesByLetter.size() == 1) {
+ unsigned FirstNonCommonLetter = FindFirstNonCommonLetter(Matches);
+ unsigned NumChars = FirstNonCommonLetter-CharNo;
+
+ // Emit code to break out if the prefix doesn't match.
+ if (NumChars == 1) {
+ // Do the comparison with if (Str[1] != 'f')
+ // FIXME: Need to escape general characters.
+ OS << Indent << "if (" << StrVariableName << "[" << CharNo << "] != '"
+ << Matches[0]->first[CharNo] << "')\n";
+ OS << Indent << " break;\n";
+ } else {
+ // Do the comparison with if (Str.substr(1,3) != "foo").
+ // FIXME: Need to escape general strings.
+ OS << Indent << "if (" << StrVariableName << ".substr(" << CharNo << ","
+ << NumChars << ") != \"";
+ OS << Matches[0]->first.substr(CharNo, NumChars) << "\")\n";
+ OS << Indent << " break;\n";
+ }
+
+ return EmitStringMatcherForChar(StrVariableName, Matches,
+ FirstNonCommonLetter, IndentCount, OS);
+ }
+
+ // Otherwise, we have multiple possible things, emit a switch on the
+ // character.
+ OS << Indent << "switch (" << StrVariableName << "[" << CharNo << "]) {\n";
+ OS << Indent << "default: break;\n";
+
+ for (std::map<char, std::vector<const StringPair*> >::iterator LI =
+ MatchesByLetter.begin(), E = MatchesByLetter.end(); LI != E; ++LI) {
+ // TODO: escape hard stuff (like \n) if we ever care about it.
+ OS << Indent << "case '" << LI->first << "':\t // "
+ << LI->second.size() << " strings to match.\n";
+ if (EmitStringMatcherForChar(StrVariableName, LI->second, CharNo+1,
+ IndentCount+1, OS))
+ OS << Indent << " break;\n";
+ }
+
+ OS << Indent << "}\n";
+ return true;
+}
+
+
+/// EmitStringMatcher - Given a list of strings and code to execute when they
+/// match, output a simple switch tree to classify the input string.
+///
+/// If a match is found, the code in Vals[i].second is executed; control must
+/// not exit this code fragment. If nothing matches, execution falls through.
+///
+/// \param StrVariableName - The name of the variable to test.
+static void EmitStringMatcher(const std::string &StrVariableName,
+ const std::vector<StringPair> &Matches,
+ raw_ostream &OS) {
+ // First level categorization: group strings by length.
+ std::map<unsigned, std::vector<const StringPair*> > MatchesByLength;
+
+ for (unsigned i = 0, e = Matches.size(); i != e; ++i)
+ MatchesByLength[Matches[i].first.size()].push_back(&Matches[i]);
+
+ // Output a switch statement on length and categorize the elements within each
+ // bin.
+ OS << " switch (" << StrVariableName << ".size()) {\n";
+ OS << " default: break;\n";
+
+ for (std::map<unsigned, std::vector<const StringPair*> >::iterator LI =
+ MatchesByLength.begin(), E = MatchesByLength.end(); LI != E; ++LI) {
+ OS << " case " << LI->first << ":\t // " << LI->second.size()
+ << " strings to match.\n";
+ if (EmitStringMatcherForChar(StrVariableName, LI->second, 0, 0, OS))
+ OS << " break;\n";
+ }
+
+ OS << " }\n";
+}
+
+
+/// EmitMatchTokenString - Emit the function to match a token string to the
+/// appropriate match class value.
+static void EmitMatchTokenString(CodeGenTarget &Target,
+ std::vector<ClassInfo*> &Infos,
+ raw_ostream &OS) {
+ // Construct the match list.
+ std::vector<StringPair> Matches;
+ for (std::vector<ClassInfo*>::iterator it = Infos.begin(),
+ ie = Infos.end(); it != ie; ++it) {
+ ClassInfo &CI = **it;
+
+ if (CI.Kind == ClassInfo::Token)
+ Matches.push_back(StringPair(CI.ValueName, "return " + CI.Name + ";"));
+ }
+
+ OS << "static MatchClassKind MatchTokenString(const StringRef &Name) {\n";
+
+ EmitStringMatcher("Name", Matches, OS);
+
+ OS << " return InvalidMatchClass;\n";
+ OS << "}\n\n";
+}
+
+/// EmitMatchRegisterName - Emit the function to match a string to the target
+/// specific register enum.
+static void EmitMatchRegisterName(CodeGenTarget &Target, Record *AsmParser,
+ raw_ostream &OS) {
+ // Construct the match list.
+ std::vector<StringPair> Matches;
+ for (unsigned i = 0, e = Target.getRegisters().size(); i != e; ++i) {
+ const CodeGenRegister &Reg = Target.getRegisters()[i];
+ if (Reg.TheDef->getValueAsString("AsmName").empty())
+ continue;
+
+ Matches.push_back(StringPair(Reg.TheDef->getValueAsString("AsmName"),
+ "return " + utostr(i + 1) + ";"));
+ }
+
+ OS << "unsigned " << Target.getName()
+ << AsmParser->getValueAsString("AsmParserClassName")
+ << "::MatchRegisterName(const StringRef &Name) {\n";
+
+ EmitStringMatcher("Name", Matches, OS);
+
+ OS << " return 0;\n";
+ OS << "}\n\n";
+}
+
+void AsmMatcherEmitter::run(raw_ostream &OS) {
+ CodeGenTarget Target;
+ Record *AsmParser = Target.getAsmParser();
+ std::string ClassName = AsmParser->getValueAsString("AsmParserClassName");
+
+ // Compute the information on the instructions to match.
+ AsmMatcherInfo Info(AsmParser);
+ Info.BuildInfo(Target);
+
+ // Sort the instruction table using the partial order on classes.
+ std::sort(Info.Instructions.begin(), Info.Instructions.end(),
+ less_ptr<InstructionInfo>());
+
+ DEBUG_WITH_TYPE("instruction_info", {
+ for (std::vector<InstructionInfo*>::iterator
+ it = Info.Instructions.begin(), ie = Info.Instructions.end();
+ it != ie; ++it)
+ (*it)->dump();
+ });
+
+ // Check for ambiguous instructions.
+ unsigned NumAmbiguous = 0;
+ for (unsigned i = 0, e = Info.Instructions.size(); i != e; ++i) {
+ for (unsigned j = i + 1; j != e; ++j) {
+ InstructionInfo &A = *Info.Instructions[i];
+ InstructionInfo &B = *Info.Instructions[j];
+
+ if (A.CouldMatchAmiguouslyWith(B)) {
+ DEBUG_WITH_TYPE("ambiguous_instrs", {
+ errs() << "warning: ambiguous instruction match:\n";
+ A.dump();
+ errs() << "\nis incomparable with:\n";
+ B.dump();
+ errs() << "\n\n";
+ });
+ ++NumAmbiguous;
+ }
+ }
+ }
+ if (NumAmbiguous)
+ DEBUG_WITH_TYPE("ambiguous_instrs", {
+ errs() << "warning: " << NumAmbiguous
+ << " ambiguous instructions!\n";
+ });
+
+ // Write the output.
+
+ EmitSourceFileHeader("Assembly Matcher Source Fragment", OS);
+
+ // Emit the function to match a register name to number.
+ EmitMatchRegisterName(Target, AsmParser, OS);
+
+ // Generate the unified function to convert operands into an MCInst.
+ EmitConvertToMCInst(Target, Info.Instructions, OS);
+
+ // Emit the enumeration for classes which participate in matching.
+ EmitMatchClassEnumeration(Target, Info.Classes, OS);
+
+ // Emit the routine to match token strings to their match class.
+ EmitMatchTokenString(Target, Info.Classes, OS);
+
+ // Emit the routine to classify an operand.
+ EmitClassifyOperand(Target, Info, OS);
+
+ // Emit the subclass predicate routine.
+ EmitIsSubclass(Target, Info.Classes, OS);
+
+ // Finally, build the match function.
+
+ size_t MaxNumOperands = 0;
+ for (std::vector<InstructionInfo*>::const_iterator it =
+ Info.Instructions.begin(), ie = Info.Instructions.end();
+ it != ie; ++it)
+ MaxNumOperands = std::max(MaxNumOperands, (*it)->Operands.size());
+
+ OS << "bool " << Target.getName() << ClassName
+ << "::MatchInstruction("
+ << "SmallVectorImpl<" << Target.getName() << "Operand> &Operands, "
+ << "MCInst &Inst) {\n";
+
+ // Emit the static match table; unused classes get initalized to 0 which is
+ // guaranteed to be InvalidMatchClass.
+ //
+ // FIXME: We can reduce the size of this table very easily. First, we change
+ // it so that store the kinds in separate bit-fields for each index, which
+ // only needs to be the max width used for classes at that index (we also need
+ // to reject based on this during classification). If we then make sure to
+ // order the match kinds appropriately (putting mnemonics last), then we
+ // should only end up using a few bits for each class, especially the ones
+ // following the mnemonic.
+ OS << " static const struct MatchEntry {\n";
+ OS << " unsigned Opcode;\n";
+ OS << " ConversionKind ConvertFn;\n";
+ OS << " MatchClassKind Classes[" << MaxNumOperands << "];\n";
+ OS << " } MatchTable[" << Info.Instructions.size() << "] = {\n";
+
+ for (std::vector<InstructionInfo*>::const_iterator it =
+ Info.Instructions.begin(), ie = Info.Instructions.end();
+ it != ie; ++it) {
+ InstructionInfo &II = **it;
+
+ OS << " { " << Target.getName() << "::" << II.InstrName
+ << ", " << II.ConversionFnKind << ", { ";
+ for (unsigned i = 0, e = II.Operands.size(); i != e; ++i) {
+ InstructionInfo::Operand &Op = II.Operands[i];
+
+ if (i) OS << ", ";
+ OS << Op.Class->Name;
+ }
+ OS << " } },\n";
+ }
+
+ OS << " };\n\n";
+
+ // Emit code to compute the class list for this operand vector.
+ OS << " // Eliminate obvious mismatches.\n";
+ OS << " if (Operands.size() > " << MaxNumOperands << ")\n";
+ OS << " return true;\n\n";
+
+ OS << " // Compute the class list for this operand vector.\n";
+ OS << " MatchClassKind Classes[" << MaxNumOperands << "];\n";
+ OS << " for (unsigned i = 0, e = Operands.size(); i != e; ++i) {\n";
+ OS << " Classes[i] = ClassifyOperand(Operands[i]);\n\n";
+
+ OS << " // Check for invalid operands before matching.\n";
+ OS << " if (Classes[i] == InvalidMatchClass)\n";
+ OS << " return true;\n";
+ OS << " }\n\n";
+
+ OS << " // Mark unused classes.\n";
+ OS << " for (unsigned i = Operands.size(), e = " << MaxNumOperands << "; "
+ << "i != e; ++i)\n";
+ OS << " Classes[i] = InvalidMatchClass;\n\n";
+
+ // Emit code to search the table.
+ OS << " // Search the table.\n";
+ OS << " for (const MatchEntry *it = MatchTable, "
+ << "*ie = MatchTable + " << Info.Instructions.size()
+ << "; it != ie; ++it) {\n";
+ for (unsigned i = 0; i != MaxNumOperands; ++i) {
+ OS << " if (!IsSubclass(Classes["
+ << i << "], it->Classes[" << i << "]))\n";
+ OS << " continue;\n";
+ }
+ OS << "\n";
+ OS << " return ConvertToMCInst(it->ConvertFn, Inst, "
+ << "it->Opcode, Operands);\n";
+ OS << " }\n\n";
+
+ OS << " return true;\n";
+ OS << "}\n\n";
+}
diff --git a/utils/TableGen/AsmMatcherEmitter.h b/utils/TableGen/AsmMatcherEmitter.h
new file mode 100644
index 0000000..729c938
--- /dev/null
+++ b/utils/TableGen/AsmMatcherEmitter.h
@@ -0,0 +1,33 @@
+//===- AsmMatcherEmitter.h - Generate an assembly matcher -------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This tablegen backend emits a target specifier matcher for converting parsed
+// assembly operands in the MCInst structures.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef ASMMATCHER_EMITTER_H
+#define ASMMATCHER_EMITTER_H
+
+#include "TableGenBackend.h"
+#include <map>
+#include <vector>
+#include <cassert>
+
+namespace llvm {
+ class AsmMatcherEmitter : public TableGenBackend {
+ RecordKeeper &Records;
+ public:
+ AsmMatcherEmitter(RecordKeeper &R) : Records(R) {}
+
+ // run - Output the matcher, returning true on failure.
+ void run(raw_ostream &o);
+ };
+}
+#endif
diff --git a/utils/TableGen/AsmWriterEmitter.cpp b/utils/TableGen/AsmWriterEmitter.cpp
index f34feef..84a647b 100644
--- a/utils/TableGen/AsmWriterEmitter.cpp
+++ b/utils/TableGen/AsmWriterEmitter.cpp
@@ -15,13 +15,14 @@
#include "AsmWriterEmitter.h"
#include "CodeGenTarget.h"
#include "Record.h"
+#include "StringToOffsetTable.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/MathExtras.h"
#include <algorithm>
-#include <iostream>
using namespace llvm;
+
static bool isIdentChar(char C) {
return (C >= 'a' && C <= 'z') ||
(C >= 'A' && C <= 'Z') ||
@@ -32,10 +33,20 @@ static bool isIdentChar(char C) {
// This should be an anon namespace, this works around a GCC warning.
namespace llvm {
struct AsmWriterOperand {
- enum { isLiteralTextOperand, isMachineInstrOperand } OperandType;
+ enum OpType {
+ // Output this text surrounded by quotes to the asm.
+ isLiteralTextOperand,
+ // This is the name of a routine to call to print the operand.
+ isMachineInstrOperand,
+ // Output this text verbatim to the asm writer. It is code that
+ // will output some text to the asm.
+ isLiteralStatementOperand
+ } OperandType;
/// Str - For isLiteralTextOperand, this IS the literal text. For
- /// isMachineInstrOperand, this is the PrinterMethodName for the operand.
+ /// isMachineInstrOperand, this is the PrinterMethodName for the operand..
+ /// For isLiteralStatementOperand, this is the code to insert verbatim
+ /// into the asm writer.
std::string Str;
/// MiOpNo - For isMachineInstrOperand, this is the operand number of the
@@ -47,14 +58,16 @@ namespace llvm {
std::string MiModifier;
// To make VS STL happy
- AsmWriterOperand():OperandType(isLiteralTextOperand) {}
+ AsmWriterOperand(OpType op = isLiteralTextOperand):OperandType(op) {}
- explicit AsmWriterOperand(const std::string &LitStr)
- : OperandType(isLiteralTextOperand), Str(LitStr) {}
+ AsmWriterOperand(const std::string &LitStr,
+ OpType op = isLiteralTextOperand)
+ : OperandType(op), Str(LitStr) {}
AsmWriterOperand(const std::string &Printer, unsigned OpNo,
- const std::string &Modifier)
- : OperandType(isMachineInstrOperand), Str(Printer), MIOpNo(OpNo),
+ const std::string &Modifier,
+ OpType op = isMachineInstrOperand)
+ : OperandType(op), Str(Printer), MIOpNo(OpNo),
MiModifier(Modifier) {}
bool operator!=(const AsmWriterOperand &Other) const {
@@ -78,7 +91,7 @@ namespace llvm {
std::vector<AsmWriterOperand> Operands;
const CodeGenInstruction *CGI;
- AsmWriterInst(const CodeGenInstruction &CGI, unsigned Variant);
+ AsmWriterInst(const CodeGenInstruction &CGI, Record *AsmWriter);
/// MatchesAllButOneOp - If this instruction is exactly identical to the
/// specified instruction except for one differing operand, return the
@@ -100,8 +113,14 @@ namespace llvm {
std::string AsmWriterOperand::getCode() const {
- if (OperandType == isLiteralTextOperand)
+ if (OperandType == isLiteralTextOperand) {
+ if (Str.size() == 1)
+ return "O << '" + Str + "'; ";
return "O << \"" + Str + "\"; ";
+ }
+
+ if (OperandType == isLiteralStatementOperand)
+ return Str;
std::string Result = Str + "(MI";
if (MIOpNo != ~0U)
@@ -115,10 +134,19 @@ std::string AsmWriterOperand::getCode() const {
/// ParseAsmString - Parse the specified Instruction's AsmString into this
/// AsmWriterInst.
///
-AsmWriterInst::AsmWriterInst(const CodeGenInstruction &CGI, unsigned Variant) {
+AsmWriterInst::AsmWriterInst(const CodeGenInstruction &CGI, Record *AsmWriter) {
this->CGI = &CGI;
+
+ unsigned Variant = AsmWriter->getValueAsInt("Variant");
+ int FirstOperandColumn = AsmWriter->getValueAsInt("FirstOperandColumn");
+ int OperandSpacing = AsmWriter->getValueAsInt("OperandSpacing");
+
unsigned CurVariant = ~0U; // ~0 if we are outside a {.|.|.} region, other #.
+ // This is the number of tabs we've seen if we're doing columnar layout.
+ unsigned CurColumn = 0;
+
+
// NOTE: Any extensions to this code need to be mirrored in the
// AsmPrinter::printInlineAsm code that executes as compile time (assuming
// that inline asm strings should also get the new feature)!
@@ -130,14 +158,35 @@ AsmWriterInst::AsmWriterInst(const CodeGenInstruction &CGI, unsigned Variant) {
if (DollarPos == std::string::npos) DollarPos = AsmString.size();
// Emit a constant string fragment.
+
if (DollarPos != LastEmitted) {
if (CurVariant == Variant || CurVariant == ~0U) {
for (; LastEmitted != DollarPos; ++LastEmitted)
switch (AsmString[LastEmitted]) {
- case '\n': AddLiteralString("\\n"); break;
- case '\t': AddLiteralString("\\t"); break;
- case '"': AddLiteralString("\\\""); break;
- case '\\': AddLiteralString("\\\\"); break;
+ case '\n':
+ AddLiteralString("\\n");
+ break;
+ case '\t':
+ // If the asm writer is not using a columnar layout, \t is not
+ // magic.
+ if (FirstOperandColumn == -1 || OperandSpacing == -1) {
+ AddLiteralString("\\t");
+ } else {
+ // We recognize a tab as an operand delimeter.
+ unsigned DestColumn = FirstOperandColumn +
+ CurColumn++ * OperandSpacing;
+ Operands.push_back(
+ AsmWriterOperand("O.PadToColumn(" +
+ utostr(DestColumn) + ");\n",
+ AsmWriterOperand::isLiteralStatementOperand));
+ }
+ break;
+ case '"':
+ AddLiteralString("\\\"");
+ break;
+ case '\\':
+ AddLiteralString("\\\\");
+ break;
default:
AddLiteralString(std::string(1, AsmString[LastEmitted]));
break;
@@ -151,7 +200,20 @@ AsmWriterInst::AsmWriterInst(const CodeGenInstruction &CGI, unsigned Variant) {
if (AsmString[DollarPos+1] == 'n') {
AddLiteralString("\\n");
} else if (AsmString[DollarPos+1] == 't') {
- AddLiteralString("\\t");
+ // If the asm writer is not using a columnar layout, \t is not
+ // magic.
+ if (FirstOperandColumn == -1 || OperandSpacing == -1) {
+ AddLiteralString("\\t");
+ break;
+ }
+
+ // We recognize a tab as an operand delimeter.
+ unsigned DestColumn = FirstOperandColumn +
+ CurColumn++ * OperandSpacing;
+ Operands.push_back(
+ AsmWriterOperand("O.PadToColumn(" + utostr(DestColumn) + ");\n",
+ AsmWriterOperand::isLiteralStatementOperand));
+ break;
} else if (std::string("${|}\\").find(AsmString[DollarPos+1])
!= std::string::npos) {
AddLiteralString(std::string(1, AsmString[DollarPos+1]));
@@ -182,13 +244,14 @@ AsmWriterInst::AsmWriterInst(const CodeGenInstruction &CGI, unsigned Variant) {
CurVariant = ~0U;
} else if (DollarPos+1 != AsmString.size() &&
AsmString[DollarPos+1] == '$') {
- if (CurVariant == Variant || CurVariant == ~0U)
+ if (CurVariant == Variant || CurVariant == ~0U) {
AddLiteralString("$"); // "$$" -> $
+ }
LastEmitted = DollarPos+2;
} else {
// Get the name of the variable.
std::string::size_type VarEnd = DollarPos+1;
-
+
// handle ${foo}bar as $foo by detecting whether the character following
// the dollar sign is a curly brace. If so, advance VarEnd and DollarPos
// so the variable name does not contain the leading curly brace.
@@ -259,8 +322,9 @@ AsmWriterInst::AsmWriterInst(const CodeGenInstruction &CGI, unsigned Variant) {
LastEmitted = VarEnd;
}
}
-
- AddLiteralString("\\n");
+
+ Operands.push_back(AsmWriterOperand("return;",
+ AsmWriterOperand::isLiteralStatementOperand));
}
/// MatchesAllButOneOp - If this instruction is exactly identical to the
@@ -357,7 +421,6 @@ static void EmitInstructions(std::vector<AsmWriterInst> &Insts,
}
O << "\n";
}
-
O << " break;\n";
}
@@ -384,10 +447,6 @@ FindUniqueOperandCommands(std::vector<std::string> &UniqueOperandCommands,
Command = " " + Inst->Operands[0].getCode() + "\n";
- // If this is the last operand, emit a return.
- if (Inst->Operands.size() == 1)
- Command += " return true;\n";
-
// Check to see if we already have 'Command' in UniqueOperandCommands.
// If not, add it.
bool FoundIt = false;
@@ -431,7 +490,10 @@ FindUniqueOperandCommands(std::vector<std::string> &UniqueOperandCommands,
// Otherwise, scan to see if all of the other instructions in this command
// set share the operand.
bool AllSame = true;
-
+ // Keep track of the maximum, number of operands or any
+ // instruction we see in the group.
+ size_t MaxSize = FirstInst->Operands.size();
+
for (NIT = std::find(NIT+1, InstIdxs.end(), CommandIdx);
NIT != InstIdxs.end();
NIT = std::find(NIT+1, InstIdxs.end(), CommandIdx)) {
@@ -439,6 +501,11 @@ FindUniqueOperandCommands(std::vector<std::string> &UniqueOperandCommands,
// matches, we're ok, otherwise bail out.
const AsmWriterInst *OtherInst =
getAsmWriterInstByID(NIT-InstIdxs.begin());
+
+ if (OtherInst &&
+ OtherInst->Operands.size() > FirstInst->Operands.size())
+ MaxSize = std::max(MaxSize, OtherInst->Operands.size());
+
if (!OtherInst || OtherInst->Operands.size() == Op ||
OtherInst->Operands[Op] != FirstInst->Operands[Op]) {
AllSame = false;
@@ -451,10 +518,6 @@ FindUniqueOperandCommands(std::vector<std::string> &UniqueOperandCommands,
// to UniqueOperandCommands and remember that it was consumed.
std::string Command = " " + FirstInst->Operands[Op].getCode() + "\n";
- // If this is the last operand, emit a return after the code.
- if (FirstInst->Operands.size() == Op+1)
- Command += " return true;\n";
-
UniqueOperandCommands[CommandIdx] += Command;
InstOpsUsed[CommandIdx]++;
}
@@ -475,29 +538,26 @@ FindUniqueOperandCommands(std::vector<std::string> &UniqueOperandCommands,
}
-
-void AsmWriterEmitter::run(raw_ostream &O) {
- EmitSourceFileHeader("Assembly Writer Source Fragment", O);
-
+/// EmitPrintInstruction - Generate the code for the "printInstruction" method
+/// implementation.
+void AsmWriterEmitter::EmitPrintInstruction(raw_ostream &O) {
CodeGenTarget Target;
Record *AsmWriter = Target.getAsmWriter();
std::string ClassName = AsmWriter->getValueAsString("AsmWriterClassName");
- unsigned Variant = AsmWriter->getValueAsInt("Variant");
-
+
O <<
"/// printInstruction - This method is automatically generated by tablegen\n"
- "/// from the instruction set description. This method returns true if the\n"
- "/// machine instruction was sufficiently described to print it, otherwise\n"
- "/// it returns false.\n"
- "bool " << Target.getName() << ClassName
+ "/// from the instruction set description.\n"
+ "void " << Target.getName() << ClassName
<< "::printInstruction(const MachineInstr *MI) {\n";
std::vector<AsmWriterInst> Instructions;
for (CodeGenTarget::inst_iterator I = Target.inst_begin(),
E = Target.inst_end(); I != E; ++I)
- if (!I->second.AsmString.empty())
- Instructions.push_back(AsmWriterInst(I->second, Variant));
+ if (!I->second.AsmString.empty() &&
+ I->second.TheDef->getName() != "PHI")
+ Instructions.push_back(AsmWriterInst(I->second, AsmWriter));
// Get the instruction numbering.
Target.getInstructionsByEnumValue(NumberedInstructions);
@@ -509,10 +569,7 @@ void AsmWriterEmitter::run(raw_ostream &O) {
CGIAWIMap.insert(std::make_pair(Instructions[i].CGI, &Instructions[i]));
// Build an aggregate string, and build a table of offsets into it.
- std::map<std::string, unsigned> StringOffset;
- std::string AggregateString;
- AggregateString.push_back(0); // "\0"
- AggregateString.push_back(0); // "\0"
+ StringToOffsetTable StringTable;
/// OpcodeInfo - This encodes the index of the string to use for the first
/// chunk of the output as well as indices used for operand printing.
@@ -524,32 +581,28 @@ void AsmWriterEmitter::run(raw_ostream &O) {
unsigned Idx;
if (AWI == 0) {
// Something not handled by the asmwriter printer.
- Idx = 0;
+ Idx = ~0U;
} else if (AWI->Operands[0].OperandType !=
AsmWriterOperand::isLiteralTextOperand ||
AWI->Operands[0].Str.empty()) {
// Something handled by the asmwriter printer, but with no leading string.
- Idx = 1;
+ Idx = StringTable.GetOrAddStringOffset("");
} else {
- unsigned &Entry = StringOffset[AWI->Operands[0].Str];
- if (Entry == 0) {
- // Add the string to the aggregate if this is the first time found.
- MaxStringIdx = Entry = AggregateString.size();
- std::string Str = AWI->Operands[0].Str;
- UnescapeString(Str);
- AggregateString += Str;
- AggregateString += '\0';
- }
- Idx = Entry;
-
+ std::string Str = AWI->Operands[0].Str;
+ UnescapeString(Str);
+ Idx = StringTable.GetOrAddStringOffset(Str);
+ MaxStringIdx = std::max(MaxStringIdx, Idx);
+
// Nuke the string from the operand list. It is now handled!
AWI->Operands.erase(AWI->Operands.begin());
}
- OpcodeInfo.push_back(Idx);
+
+ // Bias offset by one since we want 0 as a sentinel.
+ OpcodeInfo.push_back(Idx+1);
}
// Figure out how many bits we used for the string index.
- unsigned AsmStrBits = Log2_32_Ceil(MaxStringIdx+1);
+ unsigned AsmStrBits = Log2_32_Ceil(MaxStringIdx+2);
// To reduce code size, we compactify common instructions into a few bits
// in the opcode-indexed table.
@@ -557,17 +610,8 @@ void AsmWriterEmitter::run(raw_ostream &O) {
std::vector<std::vector<std::string> > TableDrivenOperandPrinters;
- bool isFirst = true;
while (1) {
std::vector<std::string> UniqueOperandCommands;
-
- // For the first operand check, add a default value for instructions with
- // just opcode strings to use.
- if (isFirst) {
- UniqueOperandCommands.push_back(" return true;\n");
- isFirst = false;
- }
-
std::vector<unsigned> InstIdxs;
std::vector<unsigned> NumInstOpsHandled;
FindUniqueOperandCommands(UniqueOperandCommands, InstIdxs,
@@ -582,8 +626,8 @@ void AsmWriterEmitter::run(raw_ostream &O) {
// If we don't have enough bits for this operand, don't include it.
if (NumBits > BitsLeft) {
- DOUT << "Not enough bits to densely encode " << NumBits
- << " more bits\n";
+ DEBUG(errs() << "Not enough bits to densely encode " << NumBits
+ << " more bits\n");
break;
}
@@ -621,52 +665,24 @@ void AsmWriterEmitter::run(raw_ostream &O) {
O << " };\n\n";
// Emit the string itself.
- O << " const char *AsmStrs = \n \"";
- unsigned CharsPrinted = 0;
- EscapeString(AggregateString);
- 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] == '\\') {
- 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 << "\";\n\n";
-
- O << " processDebugLoc(MI->getDebugLoc());\n\n";
+ O << " const char *AsmStrs = \n";
+ StringTable.EmitString(O);
+ O << ";\n\n";
O << "\n#ifndef NO_ASM_WRITER_BOILERPLATE\n";
O << " if (MI->getOpcode() == TargetInstrInfo::INLINEASM) {\n"
<< " O << \"\\t\";\n"
<< " printInlineAsm(MI);\n"
- << " return true;\n"
+ << " return;\n"
<< " } else if (MI->isLabel()) {\n"
<< " printLabel(MI);\n"
- << " return true;\n"
- << " } else if (MI->getOpcode() == TargetInstrInfo::DECLARE) {\n"
- << " printDeclare(MI);\n"
- << " return true;\n"
+ << " return;\n"
<< " } else if (MI->getOpcode() == TargetInstrInfo::IMPLICIT_DEF) {\n"
<< " printImplicitDef(MI);\n"
- << " return true;\n"
+ << " return;\n"
+ << " } else if (MI->getOpcode() == TargetInstrInfo::KILL) {\n"
+ << " return;\n"
<< " }\n\n";
O << "\n#endif\n";
@@ -675,8 +691,8 @@ void AsmWriterEmitter::run(raw_ostream &O) {
O << " // Emit the opcode for the instruction.\n"
<< " unsigned Bits = OpInfo[MI->getOpcode()];\n"
- << " if (Bits == 0) return false;\n"
- << " O << AsmStrs+(Bits & " << (1 << AsmStrBits)-1 << ");\n\n";
+ << " assert(Bits != 0 && \"Cannot print this instruction.\");\n"
+ << " O << AsmStrs+(Bits & " << (1 << AsmStrBits)-1 << ")-1;\n\n";
// Output the table driven operand information.
BitsLeft = 32-AsmStrBits;
@@ -732,6 +748,11 @@ void AsmWriterEmitter::run(raw_ostream &O) {
// elements in the vector.
std::reverse(Instructions.begin(), Instructions.end());
+
+ // Now that we've emitted all of the operand info that fit into 32 bits, emit
+ // information for those instructions that are left. This is a less dense
+ // encoding, but we expect the main 32-bit table to handle the majority of
+ // instructions.
if (!Instructions.empty()) {
// Find the opcode # of inline asm.
O << " switch (MI->getOpcode()) {\n";
@@ -739,8 +760,61 @@ void AsmWriterEmitter::run(raw_ostream &O) {
EmitInstructions(Instructions, O);
O << " }\n";
- O << " return true;\n";
+ O << " return;\n";
}
-
+
+ O << " return;\n";
O << "}\n";
}
+
+
+void AsmWriterEmitter::EmitGetRegisterName(raw_ostream &O) {
+ CodeGenTarget Target;
+ Record *AsmWriter = Target.getAsmWriter();
+ std::string ClassName = AsmWriter->getValueAsString("AsmWriterClassName");
+ const std::vector<CodeGenRegister> &Registers = Target.getRegisters();
+
+ StringToOffsetTable StringTable;
+ O <<
+ "\n\n/// getRegisterName - This method is automatically generated by tblgen\n"
+ "/// from the register set description. This returns the assembler name\n"
+ "/// for the specified register.\n"
+ "const char *" << Target.getName() << ClassName
+ << "::getRegisterName(unsigned RegNo) {\n"
+ << " assert(RegNo && RegNo < " << (Registers.size()+1)
+ << " && \"Invalid register number!\");\n"
+ << "\n"
+ << " static const unsigned RegAsmOffset[] = {";
+ for (unsigned i = 0, e = Registers.size(); i != e; ++i) {
+ const CodeGenRegister &Reg = Registers[i];
+
+ std::string AsmName = Reg.TheDef->getValueAsString("AsmName");
+ if (AsmName.empty())
+ AsmName = Reg.getName();
+
+
+ if ((i % 14) == 0)
+ O << "\n ";
+
+ O << StringTable.GetOrAddStringOffset(AsmName) << ", ";
+ }
+ O << "0\n"
+ << " };\n"
+ << "\n";
+
+ O << " const char *AsmStrs =\n";
+ StringTable.EmitString(O);
+ O << ";\n";
+
+ O << " return AsmStrs+RegAsmOffset[RegNo-1];\n"
+ << "}\n";
+}
+
+
+void AsmWriterEmitter::run(raw_ostream &O) {
+ EmitSourceFileHeader("Assembly Writer Source Fragment", O);
+
+ EmitPrintInstruction(O);
+ EmitGetRegisterName(O);
+}
+
diff --git a/utils/TableGen/AsmWriterEmitter.h b/utils/TableGen/AsmWriterEmitter.h
index 75e6996..7862caa 100644
--- a/utils/TableGen/AsmWriterEmitter.h
+++ b/utils/TableGen/AsmWriterEmitter.h
@@ -35,6 +35,9 @@ namespace llvm {
void run(raw_ostream &o);
private:
+ void EmitPrintInstruction(raw_ostream &o);
+ void EmitGetRegisterName(raw_ostream &o);
+
AsmWriterInst *getAsmWriterInstByID(unsigned ID) const {
assert(ID < NumberedInstructions.size());
std::map<const CodeGenInstruction*, AsmWriterInst*>::const_iterator I =
diff --git a/utils/TableGen/CMakeLists.txt b/utils/TableGen/CMakeLists.txt
index 6ec1d99..e568c62 100644
--- a/utils/TableGen/CMakeLists.txt
+++ b/utils/TableGen/CMakeLists.txt
@@ -1,4 +1,5 @@
add_executable(tblgen
+ AsmMatcherEmitter.cpp
AsmWriterEmitter.cpp
CallingConvEmitter.cpp
ClangDiagnosticsEmitter.cpp
diff --git a/utils/TableGen/CallingConvEmitter.cpp b/utils/TableGen/CallingConvEmitter.cpp
index a14be0b..28ba2ed 100644
--- a/utils/TableGen/CallingConvEmitter.cpp
+++ b/utils/TableGen/CallingConvEmitter.cpp
@@ -26,9 +26,9 @@ void CallingConvEmitter::run(raw_ostream &O) {
// other.
for (unsigned i = 0, e = CCs.size(); i != e; ++i) {
O << "static bool " << CCs[i]->getName()
- << "(unsigned ValNo, MVT ValVT,\n"
+ << "(unsigned ValNo, EVT ValVT,\n"
<< std::string(CCs[i]->getName().size()+13, ' ')
- << "MVT LocVT, CCValAssign::LocInfo LocInfo,\n"
+ << "EVT LocVT, CCValAssign::LocInfo LocInfo,\n"
<< std::string(CCs[i]->getName().size()+13, ' ')
<< "ISD::ArgFlagsTy ArgFlags, CCState &State);\n";
}
@@ -44,9 +44,9 @@ void CallingConvEmitter::EmitCallingConv(Record *CC, raw_ostream &O) {
Counter = 0;
O << "\n\nstatic bool " << CC->getName()
- << "(unsigned ValNo, MVT ValVT,\n"
+ << "(unsigned ValNo, EVT ValVT,\n"
<< std::string(CC->getName().size()+13, ' ')
- << "MVT LocVT, CCValAssign::LocInfo LocInfo,\n"
+ << "EVT LocVT, CCValAssign::LocInfo LocInfo,\n"
<< std::string(CC->getName().size()+13, ' ')
<< "ISD::ArgFlagsTy ArgFlags, CCState &State) {\n";
// Emit all of the actions, in order.
@@ -163,12 +163,12 @@ void CallingConvEmitter::EmitAction(Record *Action,
O << Size << ", ";
else
O << "\n" << IndentStr << " State.getTarget().getTargetData()"
- "->getTypeAllocSize(LocVT.getTypeForMVT()), ";
+ "->getTypeAllocSize(LocVT.getTypeForEVT(State.getContext())), ";
if (Align)
O << Align;
else
O << "\n" << IndentStr << " State.getTarget().getTargetData()"
- "->getABITypeAlignment(LocVT.getTypeForMVT())";
+ "->getABITypeAlignment(LocVT.getTypeForEVT(State.getContext()))";
O << ");\n" << IndentStr
<< "State.addLoc(CCValAssign::getMem(ValNo, ValVT, Offset"
<< Counter << ", LocVT, LocInfo));\n";
@@ -186,6 +186,10 @@ void CallingConvEmitter::EmitAction(Record *Action,
Record *DestTy = Action->getValueAsDef("DestTy");
O << IndentStr << "LocVT = " << getEnumName(getValueType(DestTy)) <<";\n";
O << IndentStr << "LocInfo = CCValAssign::BCvt;\n";
+ } else if (Action->isSubClassOf("CCPassIndirect")) {
+ Record *DestTy = Action->getValueAsDef("DestTy");
+ O << IndentStr << "LocVT = " << getEnumName(getValueType(DestTy)) <<";\n";
+ O << IndentStr << "LocInfo = CCValAssign::Indirect;\n";
} else if (Action->isSubClassOf("CCPassByVal")) {
int Size = Action->getValueAsInt("Size");
int Align = Action->getValueAsInt("Align");
diff --git a/utils/TableGen/CodeEmitterGen.cpp b/utils/TableGen/CodeEmitterGen.cpp
index 6466025..7e6c769 100644
--- a/utils/TableGen/CodeEmitterGen.cpp
+++ b/utils/TableGen/CodeEmitterGen.cpp
@@ -29,7 +29,7 @@ void CodeEmitterGen::reverseBits(std::vector<Record*> &Insts) {
R->getName() == "DBG_LABEL" ||
R->getName() == "EH_LABEL" ||
R->getName() == "GC_LABEL" ||
- R->getName() == "DECLARE" ||
+ R->getName() == "KILL" ||
R->getName() == "EXTRACT_SUBREG" ||
R->getName() == "INSERT_SUBREG" ||
R->getName() == "IMPLICIT_DEF" ||
@@ -106,7 +106,7 @@ void CodeEmitterGen::run(raw_ostream &o) {
R->getName() == "DBG_LABEL" ||
R->getName() == "EH_LABEL" ||
R->getName() == "GC_LABEL" ||
- R->getName() == "DECLARE" ||
+ R->getName() == "KILL" ||
R->getName() == "EXTRACT_SUBREG" ||
R->getName() == "INSERT_SUBREG" ||
R->getName() == "IMPLICIT_DEF" ||
@@ -144,7 +144,7 @@ void CodeEmitterGen::run(raw_ostream &o) {
InstName == "DBG_LABEL"||
InstName == "EH_LABEL"||
InstName == "GC_LABEL"||
- InstName == "DECLARE"||
+ InstName == "KILL"||
InstName == "EXTRACT_SUBREG" ||
InstName == "INSERT_SUBREG" ||
InstName == "IMPLICIT_DEF" ||
@@ -243,8 +243,10 @@ void CodeEmitterGen::run(raw_ostream &o) {
// Default case: unhandled opcode
o << " default:\n"
- << " cerr << \"Not supported instr: \" << MI << \"\\n\";\n"
- << " abort();\n"
+ << " std::string msg;\n"
+ << " raw_string_ostream Msg(msg);\n"
+ << " Msg << \"Not supported instr: \" << MI;\n"
+ << " llvm_report_error(Msg.str());\n"
<< " }\n"
<< " return Value;\n"
<< "}\n\n";
diff --git a/utils/TableGen/CodeGenDAGPatterns.cpp b/utils/TableGen/CodeGenDAGPatterns.cpp
index 2289ae7..6b8ceae 100644
--- a/utils/TableGen/CodeGenDAGPatterns.cpp
+++ b/utils/TableGen/CodeGenDAGPatterns.cpp
@@ -55,15 +55,15 @@ ConvertVTs(const std::vector<MVT::SimpleValueType> &InVTs) {
}
static inline bool isInteger(MVT::SimpleValueType VT) {
- return MVT(VT).isInteger();
+ return EVT(VT).isInteger();
}
static inline bool isFloatingPoint(MVT::SimpleValueType VT) {
- return MVT(VT).isFloatingPoint();
+ return EVT(VT).isFloatingPoint();
}
static inline bool isVector(MVT::SimpleValueType VT) {
- return MVT(VT).isVector();
+ return EVT(VT).isVector();
}
static bool LHSIsSubsetOfRHS(const std::vector<unsigned char> &LHS,
@@ -76,23 +76,33 @@ static bool LHSIsSubsetOfRHS(const std::vector<unsigned char> &LHS,
}
namespace llvm {
-namespace EMVT {
+namespace EEVT {
/// isExtIntegerInVTs - Return true if the specified extended value type vector
-/// contains isInt or an integer value type.
+/// contains iAny or an integer value type.
bool isExtIntegerInVTs(const std::vector<unsigned char> &EVTs) {
assert(!EVTs.empty() && "Cannot check for integer in empty ExtVT list!");
- return EVTs[0] == isInt || !(FilterEVTs(EVTs, isInteger).empty());
+ return EVTs[0] == MVT::iAny || !(FilterEVTs(EVTs, isInteger).empty());
}
/// isExtFloatingPointInVTs - Return true if the specified extended value type
-/// vector contains isFP or a FP value type.
+/// vector contains fAny or a FP value type.
bool isExtFloatingPointInVTs(const std::vector<unsigned char> &EVTs) {
- assert(!EVTs.empty() && "Cannot check for integer in empty ExtVT list!");
- return EVTs[0] == isFP || !(FilterEVTs(EVTs, isFloatingPoint).empty());
+ assert(!EVTs.empty() && "Cannot check for FP in empty ExtVT list!");
+ return EVTs[0] == MVT::fAny || !(FilterEVTs(EVTs, isFloatingPoint).empty());
+}
+
+/// isExtVectorInVTs - Return true if the specified extended value type
+/// vector contains vAny or a vector value type.
+bool isExtVectorInVTs(const std::vector<unsigned char> &EVTs) {
+ assert(!EVTs.empty() && "Cannot check for vector in empty ExtVT list!");
+ return EVTs[0] == MVT::vAny || !(FilterEVTs(EVTs, isVector).empty());
}
-} // end namespace EMVT.
+} // end namespace EEVT.
} // end namespace llvm.
+bool RecordPtrCmp::operator()(const Record *LHS, const Record *RHS) const {
+ return LHS->getID() < RHS->getID();
+}
/// Dependent variable map for CodeGenDAGPattern variant generation
typedef std::map<std::string, int> DepVarMap;
@@ -128,14 +138,14 @@ void FindDepVars(TreePatternNode *N, MultipleUseVarSet &DepVars) {
//! Dump the dependent variable set:
void DumpDepVars(MultipleUseVarSet &DepVars) {
if (DepVars.empty()) {
- DOUT << "<empty set>";
+ DEBUG(errs() << "<empty set>");
} else {
- DOUT << "[ ";
+ DEBUG(errs() << "[ ");
for (MultipleUseVarSet::const_iterator i = DepVars.begin(), e = DepVars.end();
i != e; ++i) {
- DOUT << (*i) << " ";
+ DEBUG(errs() << (*i) << " ");
}
- DOUT << "]";
+ DEBUG(errs() << "]");
}
}
}
@@ -183,6 +193,8 @@ SDTypeConstraint::SDTypeConstraint(Record *R) {
ConstraintType = SDTCisInt;
} else if (R->isSubClassOf("SDTCisFP")) {
ConstraintType = SDTCisFP;
+ } else if (R->isSubClassOf("SDTCisVec")) {
+ ConstraintType = SDTCisVec;
} else if (R->isSubClassOf("SDTCisSameAs")) {
ConstraintType = SDTCisSameAs;
x.SDTCisSameAs_Info.OtherOperandNum = R->getValueAsInt("OtherOperandNum");
@@ -264,7 +276,7 @@ bool SDTypeConstraint::ApplyTypeConstraint(TreePatternNode *N,
// If we found exactly one supported integer type, apply it.
if (IntVTs.size() == 1)
return NodeToApply->UpdateNodeType(IntVTs[0], TP);
- return NodeToApply->UpdateNodeType(EMVT::isInt, TP);
+ return NodeToApply->UpdateNodeType(MVT::iAny, TP);
}
case SDTCisFP: {
// If there is only one FP type supported, this must be it.
@@ -274,7 +286,17 @@ bool SDTypeConstraint::ApplyTypeConstraint(TreePatternNode *N,
// If we found exactly one supported FP type, apply it.
if (FPVTs.size() == 1)
return NodeToApply->UpdateNodeType(FPVTs[0], TP);
- return NodeToApply->UpdateNodeType(EMVT::isFP, TP);
+ return NodeToApply->UpdateNodeType(MVT::fAny, TP);
+ }
+ case SDTCisVec: {
+ // If there is only one vector type supported, this must be it.
+ std::vector<MVT::SimpleValueType> VecVTs =
+ FilterVTs(CGT.getLegalValueTypes(), isVector);
+
+ // If we found exactly one supported vector type, apply it.
+ if (VecVTs.size() == 1)
+ return NodeToApply->UpdateNodeType(VecVTs[0], TP);
+ return NodeToApply->UpdateNodeType(MVT::vAny, TP);
}
case SDTCisSameAs: {
TreePatternNode *OtherNode =
@@ -300,7 +322,7 @@ bool SDTypeConstraint::ApplyTypeConstraint(TreePatternNode *N,
// It must be integer.
bool MadeChange = false;
- MadeChange |= OtherNode->UpdateNodeType(EMVT::isInt, TP);
+ MadeChange |= OtherNode->UpdateNodeType(MVT::iAny, TP);
// This code only handles nodes that have one type set. Assert here so
// that we can change this if we ever need to deal with multiple value
@@ -320,25 +342,25 @@ bool SDTypeConstraint::ApplyTypeConstraint(TreePatternNode *N,
// This code does not currently handle nodes which have multiple types,
// where some types are integer, and some are fp. Assert that this is not
// the case.
- assert(!(EMVT::isExtIntegerInVTs(NodeToApply->getExtTypes()) &&
- EMVT::isExtFloatingPointInVTs(NodeToApply->getExtTypes())) &&
- !(EMVT::isExtIntegerInVTs(BigOperand->getExtTypes()) &&
- EMVT::isExtFloatingPointInVTs(BigOperand->getExtTypes())) &&
+ assert(!(EEVT::isExtIntegerInVTs(NodeToApply->getExtTypes()) &&
+ EEVT::isExtFloatingPointInVTs(NodeToApply->getExtTypes())) &&
+ !(EEVT::isExtIntegerInVTs(BigOperand->getExtTypes()) &&
+ EEVT::isExtFloatingPointInVTs(BigOperand->getExtTypes())) &&
"SDTCisOpSmallerThanOp does not handle mixed int/fp types!");
- if (EMVT::isExtIntegerInVTs(NodeToApply->getExtTypes()))
- MadeChange |= BigOperand->UpdateNodeType(EMVT::isInt, TP);
- else if (EMVT::isExtFloatingPointInVTs(NodeToApply->getExtTypes()))
- MadeChange |= BigOperand->UpdateNodeType(EMVT::isFP, TP);
- if (EMVT::isExtIntegerInVTs(BigOperand->getExtTypes()))
- MadeChange |= NodeToApply->UpdateNodeType(EMVT::isInt, TP);
- else if (EMVT::isExtFloatingPointInVTs(BigOperand->getExtTypes()))
- MadeChange |= NodeToApply->UpdateNodeType(EMVT::isFP, TP);
+ if (EEVT::isExtIntegerInVTs(NodeToApply->getExtTypes()))
+ MadeChange |= BigOperand->UpdateNodeType(MVT::iAny, TP);
+ else if (EEVT::isExtFloatingPointInVTs(NodeToApply->getExtTypes()))
+ MadeChange |= BigOperand->UpdateNodeType(MVT::fAny, TP);
+ if (EEVT::isExtIntegerInVTs(BigOperand->getExtTypes()))
+ MadeChange |= NodeToApply->UpdateNodeType(MVT::iAny, TP);
+ else if (EEVT::isExtFloatingPointInVTs(BigOperand->getExtTypes()))
+ MadeChange |= NodeToApply->UpdateNodeType(MVT::fAny, TP);
std::vector<MVT::SimpleValueType> VTs = CGT.getLegalValueTypes();
- if (EMVT::isExtIntegerInVTs(NodeToApply->getExtTypes())) {
+ if (EEVT::isExtIntegerInVTs(NodeToApply->getExtTypes())) {
VTs = FilterVTs(VTs, isInteger);
- } else if (EMVT::isExtFloatingPointInVTs(NodeToApply->getExtTypes())) {
+ } else if (EEVT::isExtFloatingPointInVTs(NodeToApply->getExtTypes())) {
VTs = FilterVTs(VTs, isFloatingPoint);
} else {
VTs.clear();
@@ -368,9 +390,9 @@ bool SDTypeConstraint::ApplyTypeConstraint(TreePatternNode *N,
if (OtherOperand->hasTypeSet()) {
if (!isVector(OtherOperand->getTypeNum(0)))
TP.error(N->getOperator()->getName() + " VT operand must be a vector!");
- MVT IVT = OtherOperand->getTypeNum(0);
+ EVT IVT = OtherOperand->getTypeNum(0);
IVT = IVT.getVectorElementType();
- return NodeToApply->UpdateNodeType(IVT.getSimpleVT(), TP);
+ return NodeToApply->UpdateNodeType(IVT.getSimpleVT().SimpleTy, TP);
}
return false;
}
@@ -445,7 +467,7 @@ bool TreePatternNode::UpdateNodeType(const std::vector<unsigned char> &ExtVTs,
TreePattern &TP) {
assert(!ExtVTs.empty() && "Cannot update node type with empty type vector!");
- if (ExtVTs[0] == EMVT::isUnknown || LHSIsSubsetOfRHS(getExtTypes(), ExtVTs))
+ if (ExtVTs[0] == EEVT::isUnknown || LHSIsSubsetOfRHS(getExtTypes(), ExtVTs))
return false;
if (isTypeCompletelyUnknown() || LHSIsSubsetOfRHS(ExtVTs, getExtTypes())) {
setTypes(ExtVTs);
@@ -454,9 +476,9 @@ bool TreePatternNode::UpdateNodeType(const std::vector<unsigned char> &ExtVTs,
if (getExtTypeNum(0) == MVT::iPTR || getExtTypeNum(0) == MVT::iPTRAny) {
if (ExtVTs[0] == MVT::iPTR || ExtVTs[0] == MVT::iPTRAny ||
- ExtVTs[0] == EMVT::isInt)
+ ExtVTs[0] == MVT::iAny)
return false;
- if (EMVT::isExtIntegerInVTs(ExtVTs)) {
+ if (EEVT::isExtIntegerInVTs(ExtVTs)) {
std::vector<unsigned char> FVTs = FilterEVTs(ExtVTs, isInteger);
if (FVTs.size()) {
setTypes(ExtVTs);
@@ -465,8 +487,19 @@ bool TreePatternNode::UpdateNodeType(const std::vector<unsigned char> &ExtVTs,
}
}
- if ((ExtVTs[0] == EMVT::isInt || ExtVTs[0] == MVT::iAny) &&
- EMVT::isExtIntegerInVTs(getExtTypes())) {
+ // Merge vAny with iAny/fAny. The latter include vector types so keep them
+ // as the more specific information.
+ if (ExtVTs[0] == MVT::vAny &&
+ (getExtTypeNum(0) == MVT::iAny || getExtTypeNum(0) == MVT::fAny))
+ return false;
+ if (getExtTypeNum(0) == MVT::vAny &&
+ (ExtVTs[0] == MVT::iAny || ExtVTs[0] == MVT::fAny)) {
+ setTypes(ExtVTs);
+ return true;
+ }
+
+ if (ExtVTs[0] == MVT::iAny &&
+ EEVT::isExtIntegerInVTs(getExtTypes())) {
assert(hasTypeSet() && "should be handled above!");
std::vector<unsigned char> FVTs = FilterEVTs(getExtTypes(), isInteger);
if (getExtTypes() == FVTs)
@@ -475,7 +508,7 @@ bool TreePatternNode::UpdateNodeType(const std::vector<unsigned char> &ExtVTs,
return true;
}
if ((ExtVTs[0] == MVT::iPTR || ExtVTs[0] == MVT::iPTRAny) &&
- EMVT::isExtIntegerInVTs(getExtTypes())) {
+ EEVT::isExtIntegerInVTs(getExtTypes())) {
//assert(hasTypeSet() && "should be handled above!");
std::vector<unsigned char> FVTs = FilterEVTs(getExtTypes(), isInteger);
if (getExtTypes() == FVTs)
@@ -485,8 +518,8 @@ bool TreePatternNode::UpdateNodeType(const std::vector<unsigned char> &ExtVTs,
return true;
}
}
- if ((ExtVTs[0] == EMVT::isFP || ExtVTs[0] == MVT::fAny) &&
- EMVT::isExtFloatingPointInVTs(getExtTypes())) {
+ if (ExtVTs[0] == MVT::fAny &&
+ EEVT::isExtFloatingPointInVTs(getExtTypes())) {
assert(hasTypeSet() && "should be handled above!");
std::vector<unsigned char> FVTs =
FilterEVTs(getExtTypes(), isFloatingPoint);
@@ -495,20 +528,31 @@ bool TreePatternNode::UpdateNodeType(const std::vector<unsigned char> &ExtVTs,
setTypes(FVTs);
return true;
}
-
- // If we know this is an int or fp type, and we are told it is a specific one,
- // take the advice.
+ if (ExtVTs[0] == MVT::vAny &&
+ EEVT::isExtVectorInVTs(getExtTypes())) {
+ assert(hasTypeSet() && "should be handled above!");
+ std::vector<unsigned char> FVTs = FilterEVTs(getExtTypes(), isVector);
+ if (getExtTypes() == FVTs)
+ return false;
+ setTypes(FVTs);
+ return true;
+ }
+
+ // If we know this is an int, FP, or vector type, and we are told it is a
+ // specific one, take the advice.
//
// Similarly, we should probably set the type here to the intersection of
- // {isInt|isFP} and ExtVTs
- if (((getExtTypeNum(0) == EMVT::isInt || getExtTypeNum(0) == MVT::iAny) &&
- EMVT::isExtIntegerInVTs(ExtVTs)) ||
- ((getExtTypeNum(0) == EMVT::isFP || getExtTypeNum(0) == MVT::fAny) &&
- EMVT::isExtFloatingPointInVTs(ExtVTs))) {
+ // {iAny|fAny|vAny} and ExtVTs
+ if ((getExtTypeNum(0) == MVT::iAny &&
+ EEVT::isExtIntegerInVTs(ExtVTs)) ||
+ (getExtTypeNum(0) == MVT::fAny &&
+ EEVT::isExtFloatingPointInVTs(ExtVTs)) ||
+ (getExtTypeNum(0) == MVT::vAny &&
+ EEVT::isExtVectorInVTs(ExtVTs))) {
setTypes(ExtVTs);
return true;
}
- if (getExtTypeNum(0) == EMVT::isInt &&
+ if (getExtTypeNum(0) == MVT::iAny &&
(ExtVTs[0] == MVT::iPTR || ExtVTs[0] == MVT::iPTRAny)) {
setTypes(ExtVTs);
return true;
@@ -537,14 +581,15 @@ void TreePatternNode::print(raw_ostream &OS) const {
// nodes that are multiply typed.
switch (getExtTypeNum(0)) {
case MVT::Other: OS << ":Other"; break;
- case EMVT::isInt: OS << ":isInt"; break;
- case EMVT::isFP : OS << ":isFP"; break;
- case EMVT::isUnknown: ; /*OS << ":?";*/ break;
+ case MVT::iAny: OS << ":iAny"; break;
+ case MVT::fAny : OS << ":fAny"; break;
+ case MVT::vAny: OS << ":vAny"; break;
+ case EEVT::isUnknown: ; /*OS << ":?";*/ break;
case MVT::iPTR: OS << ":iPTR"; break;
case MVT::iPTRAny: OS << ":iPTRAny"; break;
default: {
std::string VTName = llvm::getName(getTypeNum(0));
- // Strip off MVT:: prefix if present.
+ // Strip off EVT:: prefix if present.
if (VTName.substr(0,5) == "MVT::")
VTName = VTName.substr(5);
OS << ":" << VTName;
@@ -726,7 +771,7 @@ TreePatternNode *TreePatternNode::InlinePatternFragments(TreePattern &TP) {
static std::vector<unsigned char> getImplicitType(Record *R, bool NotRegisters,
TreePattern &TP) {
// Some common return values
- std::vector<unsigned char> Unknown(1, EMVT::isUnknown);
+ std::vector<unsigned char> Unknown(1, EEVT::isUnknown);
std::vector<unsigned char> Other(1, MVT::Other);
// Check to see if this is a register or a register class...
@@ -753,7 +798,7 @@ static std::vector<unsigned char> getImplicitType(Record *R, bool NotRegisters,
std::vector<unsigned char>
ComplexPat(1, TP.getDAGPatterns().getComplexPattern(R).getValueType());
return ComplexPat;
- } else if (R->getName() == "ptr_rc") {
+ } else if (R->isSubClassOf("PointerLikeRegClass")) {
Other[0] = MVT::iPTR;
return Other;
} else if (R->getName() == "node" || R->getName() == "srcvalue" ||
@@ -803,7 +848,7 @@ bool TreePatternNode::ApplyTypeConstraints(TreePattern &TP, bool NotRegisters) {
return UpdateNodeType(getImplicitType(DI->getDef(), NotRegisters, TP),TP);
} else if (IntInit *II = dynamic_cast<IntInit*>(getLeafValue())) {
// Int inits are always integers. :)
- bool MadeChange = UpdateNodeType(EMVT::isInt, TP);
+ bool MadeChange = UpdateNodeType(MVT::iAny, TP);
if (hasTypeSet()) {
// At some point, it may make sense for this tree pattern to have
@@ -816,7 +861,7 @@ bool TreePatternNode::ApplyTypeConstraints(TreePattern &TP, bool NotRegisters) {
VT = getTypeNum(0);
if (VT != MVT::iPTR && VT != MVT::iPTRAny) {
- unsigned Size = MVT(VT).getSizeInBits();
+ unsigned Size = EVT(VT).getSizeInBits();
// Make sure that the value is representable for this type.
if (Size < 32) {
int Val = (II->getValue() << (32-Size)) >> (32-Size);
@@ -924,13 +969,13 @@ bool TreePatternNode::ApplyTypeConstraints(TreePattern &TP, bool NotRegisters) {
} else {
Record *ResultNode = Inst.getResult(0);
- if (ResultNode->getName() == "ptr_rc") {
+ if (ResultNode->isSubClassOf("PointerLikeRegClass")) {
std::vector<unsigned char> VT;
VT.push_back(MVT::iPTR);
MadeChange = UpdateNodeType(VT, TP);
} else if (ResultNode->getName() == "unknown") {
std::vector<unsigned char> VT;
- VT.push_back(EMVT::isUnknown);
+ VT.push_back(EEVT::isUnknown);
MadeChange = UpdateNodeType(VT, TP);
} else {
assert(ResultNode->isSubClassOf("RegisterClass") &&
@@ -968,10 +1013,10 @@ bool TreePatternNode::ApplyTypeConstraints(TreePattern &TP, bool NotRegisters) {
} else if (OperandNode->isSubClassOf("Operand")) {
VT = getValueType(OperandNode->getValueAsDef("Type"));
MadeChange |= Child->UpdateNodeType(VT, TP);
- } else if (OperandNode->getName() == "ptr_rc") {
+ } else if (OperandNode->isSubClassOf("PointerLikeRegClass")) {
MadeChange |= Child->UpdateNodeType(MVT::iPTR, TP);
} else if (OperandNode->getName() == "unknown") {
- MadeChange |= Child->UpdateNodeType(EMVT::isUnknown, TP);
+ MadeChange |= Child->UpdateNodeType(EEVT::isUnknown, TP);
} else {
assert(0 && "Unknown operand type!");
abort();
@@ -1297,7 +1342,7 @@ CodeGenDAGPatterns::CodeGenDAGPatterns(RecordKeeper &R) : Records(R) {
}
CodeGenDAGPatterns::~CodeGenDAGPatterns() {
- for (std::map<Record*, TreePattern*>::iterator I = PatternFragments.begin(),
+ for (pf_iterator I = PatternFragments.begin(),
E = PatternFragments.end(); I != E; ++I)
delete I->second;
}
@@ -1602,7 +1647,7 @@ FindPatternInputsAndOutputs(TreePattern *I, TreePatternNode *Pat,
I->error("set destination should be a register!");
if (Val->getDef()->isSubClassOf("RegisterClass") ||
- Val->getDef()->getName() == "ptr_rc") {
+ Val->getDef()->isSubClassOf("PointerLikeRegClass")) {
if (Dest->getName().empty())
I->error("set destination must have a name!");
if (InstResults.count(Dest->getName()))
@@ -1949,7 +1994,8 @@ void CodeGenDAGPatterns::ParseInstructions() {
}
// If we can, convert the instructions to be patterns that are matched!
- for (std::map<Record*, DAGInstruction>::iterator II = Instructions.begin(),
+ for (std::map<Record*, DAGInstruction, RecordPtrCmp>::iterator II =
+ Instructions.begin(),
E = Instructions.end(); II != E; ++II) {
DAGInstruction &TheInst = II->second;
const TreePattern *I = TheInst.getPattern();
@@ -2350,7 +2396,7 @@ static void GenerateVariantsOf(TreePatternNode *N,
// GenerateVariants - Generate variants. For example, commutative patterns can
// match multiple ways. Add them to PatternsToMatch as well.
void CodeGenDAGPatterns::GenerateVariants() {
- DOUT << "Generating instruction variants.\n";
+ DEBUG(errs() << "Generating instruction variants.\n");
// Loop over all of the patterns we've collected, checking to see if we can
// generate variants of the instruction, through the exploitation of
@@ -2365,9 +2411,9 @@ void CodeGenDAGPatterns::GenerateVariants() {
MultipleUseVarSet DepVars;
std::vector<TreePatternNode*> Variants;
FindDepVars(PatternsToMatch[i].getSrcPattern(), DepVars);
- DOUT << "Dependent/multiply used variables: ";
+ DEBUG(errs() << "Dependent/multiply used variables: ");
DEBUG(DumpDepVars(DepVars));
- DOUT << "\n";
+ DEBUG(errs() << "\n");
GenerateVariantsOf(PatternsToMatch[i].getSrcPattern(), Variants, *this, DepVars);
assert(!Variants.empty() && "Must create at least original variant!");
@@ -2376,16 +2422,16 @@ void CodeGenDAGPatterns::GenerateVariants() {
if (Variants.empty()) // No variants for this pattern.
continue;
- DOUT << "FOUND VARIANTS OF: ";
- DEBUG(PatternsToMatch[i].getSrcPattern()->dump());
- DOUT << "\n";
+ DEBUG(errs() << "FOUND VARIANTS OF: ";
+ PatternsToMatch[i].getSrcPattern()->dump();
+ errs() << "\n");
for (unsigned v = 0, e = Variants.size(); v != e; ++v) {
TreePatternNode *Variant = Variants[v];
- DOUT << " VAR#" << v << ": ";
- DEBUG(Variant->dump());
- DOUT << "\n";
+ DEBUG(errs() << " VAR#" << v << ": ";
+ Variant->dump();
+ errs() << "\n");
// Scan to see if an instruction or explicit pattern already matches this.
bool AlreadyExists = false;
@@ -2396,7 +2442,7 @@ void CodeGenDAGPatterns::GenerateVariants() {
continue;
// Check to see if this variant already exists.
if (Variant->isIsomorphicTo(PatternsToMatch[p].getSrcPattern(), DepVars)) {
- DOUT << " *** ALREADY EXISTS, ignoring variant.\n";
+ DEBUG(errs() << " *** ALREADY EXISTS, ignoring variant.\n");
AlreadyExists = true;
break;
}
@@ -2412,7 +2458,7 @@ void CodeGenDAGPatterns::GenerateVariants() {
PatternsToMatch[i].getAddedComplexity()));
}
- DOUT << "\n";
+ DEBUG(errs() << "\n");
}
}
diff --git a/utils/TableGen/CodeGenDAGPatterns.h b/utils/TableGen/CodeGenDAGPatterns.h
index d398006..9b53ecc5 100644
--- a/utils/TableGen/CodeGenDAGPatterns.h
+++ b/utils/TableGen/CodeGenDAGPatterns.h
@@ -33,22 +33,27 @@ namespace llvm {
class CodeGenDAGPatterns;
class ComplexPattern;
-/// EMVT::DAGISelGenValueType - These are some extended forms of
+/// EEVT::DAGISelGenValueType - These are some extended forms of
/// MVT::SimpleValueType that we use as lattice values during type inference.
-namespace EMVT {
+/// The existing MVT iAny, fAny and vAny types suffice to represent
+/// arbitrary integer, floating-point, and vector types, so only an unknown
+/// value is needed.
+namespace EEVT {
enum DAGISelGenValueType {
- isFP = MVT::LAST_VALUETYPE,
- isInt,
- isUnknown
+ isUnknown = MVT::LAST_VALUETYPE
};
- /// isExtIntegerVT - Return true if the specified extended value type vector
- /// contains isInt or an integer value type.
+ /// isExtIntegerInVTs - Return true if the specified extended value type
+ /// vector contains iAny or an integer value type.
bool isExtIntegerInVTs(const std::vector<unsigned char> &EVTs);
- /// isExtFloatingPointVT - Return true if the specified extended value type
- /// vector contains isFP or a FP value type.
+ /// isExtFloatingPointInVTs - Return true if the specified extended value
+ /// type vector contains fAny or a FP value type.
bool isExtFloatingPointInVTs(const std::vector<unsigned char> &EVTs);
+
+ /// isExtVectorinVTs - Return true if the specified extended value type
+ /// vector contains vAny or a vector value type.
+ bool isExtVectorInVTs(const std::vector<unsigned char> &EVTs);
}
/// Set type used to track multiply used variables in patterns
@@ -61,7 +66,7 @@ struct SDTypeConstraint {
unsigned OperandNo; // The operand # this constraint applies to.
enum {
- SDTCisVT, SDTCisPtrTy, SDTCisInt, SDTCisFP, SDTCisSameAs,
+ SDTCisVT, SDTCisPtrTy, SDTCisInt, SDTCisFP, SDTCisVec, SDTCisSameAs,
SDTCisVTSmallerThanOp, SDTCisOpSmallerThanOp, SDTCisEltOfVec
} ConstraintType;
@@ -140,7 +145,7 @@ public:
/// patterns), and as such should be ref counted. We currently just leak all
/// TreePatternNode objects!
class TreePatternNode {
- /// The inferred type for this node, or EMVT::isUnknown if it hasn't
+ /// The inferred type for this node, or EEVT::isUnknown if it hasn't
/// been determined yet. This is a std::vector because during inference
/// there may be multiple possible types.
std::vector<unsigned char> Types;
@@ -169,10 +174,10 @@ class TreePatternNode {
public:
TreePatternNode(Record *Op, const std::vector<TreePatternNode*> &Ch)
: Types(), Operator(Op), Val(0), TransformFn(0),
- Children(Ch) { Types.push_back(EMVT::isUnknown); }
+ Children(Ch) { Types.push_back(EEVT::isUnknown); }
TreePatternNode(Init *val) // leaf ctor
: Types(), Operator(0), Val(val), TransformFn(0) {
- Types.push_back(EMVT::isUnknown);
+ Types.push_back(EEVT::isUnknown);
}
~TreePatternNode();
@@ -185,7 +190,7 @@ public:
(Types[0] == MVT::iPTRAny);
}
bool isTypeCompletelyUnknown() const {
- return Types[0] == EMVT::isUnknown;
+ return Types[0] == EEVT::isUnknown;
}
bool isTypeDynamicallyResolved() const {
return (Types[0] == MVT::iPTR) || (Types[0] == MVT::iPTRAny);
@@ -201,7 +206,7 @@ public:
}
const std::vector<unsigned char> &getExtTypes() const { return Types; }
void setTypes(const std::vector<unsigned char> &T) { Types = T; }
- void removeTypes() { Types = std::vector<unsigned char>(1, EMVT::isUnknown); }
+ void removeTypes() { Types = std::vector<unsigned char>(1, EEVT::isUnknown); }
Init *getLeafValue() const { assert(isLeaf()); return Val; }
Record *getOperator() const { assert(!isLeaf()); return Operator; }
@@ -457,6 +462,10 @@ struct PatternToMatch {
std::string getPredicateCheck() const;
};
+// Deterministic comparison of Record*.
+struct RecordPtrCmp {
+ bool operator()(const Record *LHS, const Record *RHS) const;
+};
class CodeGenDAGPatterns {
RecordKeeper &Records;
@@ -464,12 +473,12 @@ class CodeGenDAGPatterns {
std::vector<CodeGenIntrinsic> Intrinsics;
std::vector<CodeGenIntrinsic> TgtIntrinsics;
- std::map<Record*, SDNodeInfo> SDNodes;
- std::map<Record*, std::pair<Record*, std::string> > SDNodeXForms;
- std::map<Record*, ComplexPattern> ComplexPatterns;
- std::map<Record*, TreePattern*> PatternFragments;
- std::map<Record*, DAGDefaultOperand> DefaultOperands;
- std::map<Record*, DAGInstruction> Instructions;
+ std::map<Record*, SDNodeInfo, RecordPtrCmp> SDNodes;
+ std::map<Record*, std::pair<Record*, std::string>, RecordPtrCmp> SDNodeXForms;
+ std::map<Record*, ComplexPattern, RecordPtrCmp> ComplexPatterns;
+ std::map<Record*, TreePattern*, RecordPtrCmp> PatternFragments;
+ std::map<Record*, DAGDefaultOperand, RecordPtrCmp> DefaultOperands;
+ std::map<Record*, DAGInstruction, RecordPtrCmp> Instructions;
// Specific SDNode definitions:
Record *intrinsic_void_sdnode;
@@ -500,7 +509,8 @@ public:
return SDNodeXForms.find(R)->second;
}
- typedef std::map<Record*, NodeXForm>::const_iterator nx_iterator;
+ typedef std::map<Record*, NodeXForm, RecordPtrCmp>::const_iterator
+ nx_iterator;
nx_iterator nx_begin() const { return SDNodeXForms.begin(); }
nx_iterator nx_end() const { return SDNodeXForms.end(); }
@@ -547,7 +557,8 @@ public:
assert(PatternFragments.count(R) && "Invalid pattern fragment request!");
return PatternFragments.find(R)->second;
}
- typedef std::map<Record*, TreePattern*>::const_iterator pf_iterator;
+ typedef std::map<Record*, TreePattern*, RecordPtrCmp>::const_iterator
+ pf_iterator;
pf_iterator pf_begin() const { return PatternFragments.begin(); }
pf_iterator pf_end() const { return PatternFragments.end(); }
diff --git a/utils/TableGen/CodeGenInstruction.cpp b/utils/TableGen/CodeGenInstruction.cpp
index 4650b88..d421fd0 100644
--- a/utils/TableGen/CodeGenInstruction.cpp
+++ b/utils/TableGen/CodeGenInstruction.cpp
@@ -22,7 +22,7 @@ static void ParseConstraint(const std::string &CStr, CodeGenInstruction *I) {
std::string::size_type pos = CStr.find_first_of('=');
assert(pos != std::string::npos && "Unrecognized constraint");
std::string::size_type start = CStr.find_first_not_of(" \t");
- std::string Name = CStr.substr(start, pos);
+ std::string Name = CStr.substr(start, pos - start);
// TIED_TO: $src1 = $dst
std::string::size_type wpos = Name.find_first_of(" \t");
@@ -70,7 +70,7 @@ static void ParseConstraints(const std::string &CStr, CodeGenInstruction *I) {
if (eidx == std::string::npos)
eidx = CStr.length();
- ParseConstraint(CStr.substr(bidx, eidx), I);
+ ParseConstraint(CStr.substr(bidx, eidx - bidx), I);
bidx = CStr.find_first_not_of(delims, eidx);
}
}
@@ -101,6 +101,8 @@ CodeGenInstruction::CodeGenInstruction(Record *R, const std::string &AsmStr)
mayHaveSideEffects = R->getValueAsBit("mayHaveSideEffects");
neverHasSideEffects = R->getValueAsBit("neverHasSideEffects");
isAsCheapAsAMove = R->getValueAsBit("isAsCheapAsAMove");
+ hasExtraSrcRegAllocReq = R->getValueAsBit("hasExtraSrcRegAllocReq");
+ hasExtraDefRegAllocReq = R->getValueAsBit("hasExtraDefRegAllocReq");
hasOptionalDef = false;
isVariadic = false;
diff --git a/utils/TableGen/CodeGenInstruction.h b/utils/TableGen/CodeGenInstruction.h
index f4afd5e..04506e9 100644
--- a/utils/TableGen/CodeGenInstruction.h
+++ b/utils/TableGen/CodeGenInstruction.h
@@ -106,6 +106,8 @@ namespace llvm {
bool mayHaveSideEffects;
bool neverHasSideEffects;
bool isAsCheapAsAMove;
+ bool hasExtraSrcRegAllocReq;
+ bool hasExtraDefRegAllocReq;
/// ParseOperandName - Parse an operand name like "$foo" or "$foo.bar",
/// where $foo is a whole operand and $foo.bar refers to a suboperand.
diff --git a/utils/TableGen/CodeGenTarget.cpp b/utils/TableGen/CodeGenTarget.cpp
index c17cd0e..0edca73 100644
--- a/utils/TableGen/CodeGenTarget.cpp
+++ b/utils/TableGen/CodeGenTarget.cpp
@@ -23,6 +23,10 @@
using namespace llvm;
static cl::opt<unsigned>
+AsmParserNum("asmparsernum", cl::init(0),
+ cl::desc("Make -gen-asm-parser emit assembly parser #N"));
+
+static cl::opt<unsigned>
AsmWriterNum("asmwriternum", cl::init(0),
cl::desc("Make -gen-asm-writer emit assembly writer #N"));
@@ -34,47 +38,10 @@ MVT::SimpleValueType llvm::getValueType(Record *Rec) {
std::string llvm::getName(MVT::SimpleValueType T) {
switch (T) {
- case MVT::Other: return "UNKNOWN";
- case MVT::i1: return "MVT::i1";
- case MVT::i8: return "MVT::i8";
- case MVT::i16: return "MVT::i16";
- case MVT::i32: return "MVT::i32";
- case MVT::i64: return "MVT::i64";
- case MVT::i128: return "MVT::i128";
- case MVT::iAny: return "MVT::iAny";
- case MVT::fAny: return "MVT::fAny";
- case MVT::f32: return "MVT::f32";
- case MVT::f64: return "MVT::f64";
- case MVT::f80: return "MVT::f80";
- case MVT::f128: return "MVT::f128";
- case MVT::ppcf128: return "MVT::ppcf128";
- case MVT::Flag: return "MVT::Flag";
- case MVT::isVoid:return "MVT::isVoid";
- case MVT::v2i8: return "MVT::v2i8";
- case MVT::v4i8: return "MVT::v4i8";
- case MVT::v8i8: return "MVT::v8i8";
- case MVT::v16i8: return "MVT::v16i8";
- case MVT::v32i8: return "MVT::v32i8";
- case MVT::v2i16: return "MVT::v2i16";
- case MVT::v4i16: return "MVT::v4i16";
- case MVT::v8i16: return "MVT::v8i16";
- case MVT::v16i16: return "MVT::v16i16";
- case MVT::v2i32: return "MVT::v2i32";
- case MVT::v4i32: return "MVT::v4i32";
- case MVT::v8i32: return "MVT::v8i32";
- case MVT::v1i64: return "MVT::v1i64";
- case MVT::v2i64: return "MVT::v2i64";
- case MVT::v4i64: return "MVT::v4i64";
- case MVT::v2f32: return "MVT::v2f32";
- case MVT::v4f32: return "MVT::v4f32";
- case MVT::v8f32: return "MVT::v8f32";
- case MVT::v2f64: return "MVT::v2f64";
- case MVT::v4f64: return "MVT::v4f64";
- case MVT::v3i32: return "MVT::v3i32";
- case MVT::v3f32: return "MVT::v3f32";
- case MVT::iPTR: return "TLI.getPointerTy()";
- case MVT::iPTRAny: return "TLI.getPointerTy()";
- default: assert(0 && "ILLEGAL VALUE TYPE!"); return "";
+ case MVT::Other: return "UNKNOWN";
+ case MVT::iPTR: return "TLI.getPointerTy()";
+ case MVT::iPTRAny: return "TLI.getPointerTy()";
+ default: return getEnumName(T);
}
}
@@ -89,6 +56,7 @@ std::string llvm::getEnumName(MVT::SimpleValueType T) {
case MVT::i128: return "MVT::i128";
case MVT::iAny: return "MVT::iAny";
case MVT::fAny: return "MVT::fAny";
+ case MVT::vAny: return "MVT::vAny";
case MVT::f32: return "MVT::f32";
case MVT::f64: return "MVT::f64";
case MVT::f80: return "MVT::f80";
@@ -116,8 +84,7 @@ std::string llvm::getEnumName(MVT::SimpleValueType T) {
case MVT::v8f32: return "MVT::v8f32";
case MVT::v2f64: return "MVT::v2f64";
case MVT::v4f64: return "MVT::v4f64";
- case MVT::v3i32: return "MVT::v3i32";
- case MVT::v3f32: return "MVT::v3f32";
+ case MVT::Metadata: return "MVT::Metadata";
case MVT::iPTR: return "MVT::iPTR";
case MVT::iPTRAny: return "MVT::iPTRAny";
default: assert(0 && "ILLEGAL VALUE TYPE!"); return "";
@@ -171,6 +138,15 @@ Record *CodeGenTarget::getInstructionSet() const {
return TargetRec->getValueAsDef("InstructionSet");
}
+/// getAsmParser - Return the AssemblyParser definition for this target.
+///
+Record *CodeGenTarget::getAsmParser() const {
+ std::vector<Record*> LI = TargetRec->getValueAsListOfDefs("AssemblyParsers");
+ if (AsmParserNum >= LI.size())
+ throw "Target does not have an AsmParser #" + utostr(AsmParserNum) + "!";
+ return LI[AsmParserNum];
+}
+
/// getAsmWriter - Return the AssemblyWriter definition for this target.
///
Record *CodeGenTarget::getAsmWriter() const {
@@ -265,7 +241,7 @@ CodeGenRegisterClass::CodeGenRegisterClass(Record *R) : TheDef(R) {
unsigned Size = R->getValueAsInt("Size");
Namespace = R->getValueAsString("Namespace");
- SpillSize = Size ? Size : MVT(VTs[0]).getSizeInBits();
+ SpillSize = Size ? Size : EVT(VTs[0]).getSizeInBits();
SpillAlignment = R->getValueAsInt("Alignment");
CopyCost = R->getValueAsInt("CopyCost");
MethodBodies = R->getValueAsCode("MethodBodies");
@@ -332,9 +308,9 @@ getInstructionsByEnumValue(std::vector<const CodeGenInstruction*>
if (I == Instructions.end()) throw "Could not find 'GC_LABEL' instruction!";
const CodeGenInstruction *GC_LABEL = &I->second;
- I = getInstructions().find("DECLARE");
- if (I == Instructions.end()) throw "Could not find 'DECLARE' instruction!";
- const CodeGenInstruction *DECLARE = &I->second;
+ I = getInstructions().find("KILL");
+ if (I == Instructions.end()) throw "Could not find 'KILL' instruction!";
+ const CodeGenInstruction *KILL = &I->second;
I = getInstructions().find("EXTRACT_SUBREG");
if (I == Instructions.end())
@@ -367,7 +343,7 @@ getInstructionsByEnumValue(std::vector<const CodeGenInstruction*>
NumberedInstructions.push_back(DBG_LABEL);
NumberedInstructions.push_back(EH_LABEL);
NumberedInstructions.push_back(GC_LABEL);
- NumberedInstructions.push_back(DECLARE);
+ NumberedInstructions.push_back(KILL);
NumberedInstructions.push_back(EXTRACT_SUBREG);
NumberedInstructions.push_back(INSERT_SUBREG);
NumberedInstructions.push_back(IMPLICIT_DEF);
@@ -379,7 +355,7 @@ getInstructionsByEnumValue(std::vector<const CodeGenInstruction*>
&II->second != DBG_LABEL &&
&II->second != EH_LABEL &&
&II->second != GC_LABEL &&
- &II->second != DECLARE &&
+ &II->second != KILL &&
&II->second != EXTRACT_SUBREG &&
&II->second != INSERT_SUBREG &&
&II->second != IMPLICIT_DEF &&
@@ -517,11 +493,12 @@ CodeGenIntrinsic::CodeGenIntrinsic(Record *R) {
// overloaded, all the types can be specified directly.
assert(((!TyEl->isSubClassOf("LLVMExtendedElementVectorType") &&
!TyEl->isSubClassOf("LLVMTruncatedElementVectorType")) ||
- VT == MVT::iAny) && "Expected iAny type");
+ VT == MVT::iAny || VT == MVT::vAny) &&
+ "Expected iAny or vAny type");
} else {
VT = getValueType(TyEl->getValueAsDef("VT"));
}
- if (VT == MVT::iAny || VT == MVT::fAny || VT == MVT::iPTRAny) {
+ if (EVT(VT).isOverloaded()) {
OverloadedVTs.push_back(VT);
isOverloaded |= true;
}
@@ -548,10 +525,11 @@ CodeGenIntrinsic::CodeGenIntrinsic(Record *R) {
// overloaded, all the types can be specified directly.
assert(((!TyEl->isSubClassOf("LLVMExtendedElementVectorType") &&
!TyEl->isSubClassOf("LLVMTruncatedElementVectorType")) ||
- VT == MVT::iAny) && "Expected iAny type");
+ VT == MVT::iAny || VT == MVT::vAny) &&
+ "Expected iAny or vAny type");
} else
VT = getValueType(TyEl->getValueAsDef("VT"));
- if (VT == MVT::iAny || VT == MVT::fAny || VT == MVT::iPTRAny) {
+ if (EVT(VT).isOverloaded()) {
OverloadedVTs.push_back(VT);
isOverloaded |= true;
}
diff --git a/utils/TableGen/CodeGenTarget.h b/utils/TableGen/CodeGenTarget.h
index 0ec9955..e763795 100644
--- a/utils/TableGen/CodeGenTarget.h
+++ b/utils/TableGen/CodeGenTarget.h
@@ -87,6 +87,10 @@ public:
///
Record *getInstructionSet() const;
+ /// getAsmParser - Return the AssemblyParser definition for this target.
+ ///
+ Record *getAsmParser() const;
+
/// getAsmWriter - Return the AssemblyWriter definition for this target.
///
Record *getAsmWriter() const;
diff --git a/utils/TableGen/DAGISelEmitter.cpp b/utils/TableGen/DAGISelEmitter.cpp
index bb26a1d..dcf64e4 100644
--- a/utils/TableGen/DAGISelEmitter.cpp
+++ b/utils/TableGen/DAGISelEmitter.cpp
@@ -23,11 +23,8 @@
#include <iostream>
using namespace llvm;
-namespace {
- cl::opt<bool>
- GenDebug("gen-debug", cl::desc("Generate debug code"),
- cl::init(false));
-}
+static cl::opt<bool>
+GenDebug("gen-debug", cl::desc("Generate debug code"), cl::init(false));
//===----------------------------------------------------------------------===//
// DAGISelEmitter Helper methods
@@ -60,8 +57,8 @@ static const ComplexPattern *NodeGetComplexPattern(TreePatternNode *N,
/// patterns before small ones. This is used to determine the size of a
/// pattern.
static unsigned getPatternSize(TreePatternNode *P, CodeGenDAGPatterns &CGP) {
- assert((EMVT::isExtIntegerInVTs(P->getExtTypes()) ||
- EMVT::isExtFloatingPointInVTs(P->getExtTypes()) ||
+ assert((EEVT::isExtIntegerInVTs(P->getExtTypes()) ||
+ EEVT::isExtFloatingPointInVTs(P->getExtTypes()) ||
P->getExtTypeNum(0) == MVT::isVoid ||
P->getExtTypeNum(0) == MVT::Flag ||
P->getExtTypeNum(0) == MVT::iPTR ||
@@ -698,7 +695,7 @@ public:
if (DefInit *DI = dynamic_cast<DefInit*>(Child->getLeafValue())) {
Record *LeafRec = DI->getDef();
if (LeafRec->isSubClassOf("RegisterClass") ||
- LeafRec->getName() == "ptr_rc") {
+ LeafRec->isSubClassOf("PointerLikeRegClass")) {
// Handle register references. Nothing to do here.
} else if (LeafRec->isSubClassOf("Register")) {
// Handle register references.
@@ -787,7 +784,7 @@ public:
EmitResultCode(TreePatternNode *N, std::vector<Record*> DstRegs,
bool InFlagDecled, bool ResNodeDecled,
bool LikeLeaf = false, bool isRoot = false) {
- // List of arguments of getTargetNode() or SelectNodeTo().
+ // List of arguments of getMachineNode() or SelectNodeTo().
std::vector<std::string> NodeOps;
// This is something selected from the pattern we matched.
if (!N->getName().empty()) {
@@ -932,7 +929,8 @@ public:
unsigned ResNo = TmpNo++;
assert(N->getExtTypes().size() == 1 && "Multiple types not handled!");
emitCode("SDValue Tmp" + utostr(ResNo) +
- " = CurDAG->getTargetConstant(0x" + itohexstr(II->getValue()) +
+ " = CurDAG->getTargetConstant(0x" +
+ utohexstr((uint64_t) II->getValue()) +
"ULL, " + getEnumName(N->getTypeNum(0)) + ");");
NodeOps.push_back("Tmp" + utostr(ResNo));
return NodeOps;
@@ -1091,7 +1089,7 @@ public:
std::string Code = "Opc" + utostr(OpcNo);
if (!isRoot || (InputHasChain && !NodeHasChain))
- // For call to "getTargetNode()".
+ // For call to "getMachineNode()".
Code += ", N.getDebugLoc()";
emitOpcode(II.Namespace + "::" + II.TheDef->getName());
@@ -1137,24 +1135,18 @@ public:
emitCode("}");
}
- // Generate MemOperandSDNodes nodes for each memory accesses covered by
+ // Populate MemRefs with entries for each memory accesses covered by
// this pattern.
- if (II.mayLoad | II.mayStore) {
- std::vector<std::string>::const_iterator mi, mie;
- for (mi = LSI.begin(), mie = LSI.end(); mi != mie; ++mi) {
- std::string LSIName = "LSI_" + *mi;
- emitCode("SDValue " + LSIName + " = "
- "CurDAG->getMemOperand(cast<MemSDNode>(" +
- *mi + ")->getMemOperand());");
- if (GenDebug) {
- emitCode("CurDAG->setSubgraphColor(" + LSIName +".getNode(), \"yellow\");");
- emitCode("CurDAG->setSubgraphColor(" + LSIName +".getNode(), \"black\");");
- }
- if (IsVariadic)
- emitCode("Ops" + utostr(OpsNo) + ".push_back(" + LSIName + ");");
- else
- AllOps.push_back(LSIName);
- }
+ if (isRoot && !LSI.empty()) {
+ std::string MemRefs = "MemRefs" + utostr(OpsNo);
+ emitCode("MachineSDNode::mmo_iterator " + MemRefs + " = "
+ "MF->allocateMemRefsArray(" + utostr(LSI.size()) + ");");
+ for (unsigned i = 0, e = LSI.size(); i != e; ++i)
+ emitCode(MemRefs + "[" + utostr(i) + "] = "
+ "cast<MemSDNode>(" + LSI[i] + ")->getMemOperand();");
+ After.push_back("cast<MachineSDNode>(ResNode)->setMemRefs(" +
+ MemRefs + ", " + MemRefs + " + " + utostr(LSI.size()) +
+ ");");
}
if (NodeHasChain) {
@@ -1305,7 +1297,7 @@ public:
// would leave users of the chain dangling.
//
if (!isRoot || (InputHasChain && !NodeHasChain)) {
- Code = "CurDAG->getTargetNode(" + Code;
+ Code = "CurDAG->getMachineNode(" + Code;
} else {
Code = "CurDAG->SelectNodeTo(N.getNode(), " + Code;
}
@@ -1778,7 +1770,7 @@ void DAGISelEmitter::EmitInstructionSelector(raw_ostream &OS) {
CallerCode += ", " + TargetOpcodes[j];
}
for (unsigned j = 0, e = TargetVTs.size(); j != e; ++j) {
- CalleeCode += ", MVT VT" + utostr(j);
+ CalleeCode += ", MVT::SimpleValueType VT" + utostr(j);
CallerCode += ", " + TargetVTs[j];
}
for (std::set<std::string>::iterator
@@ -1930,7 +1922,7 @@ void DAGISelEmitter::EmitInstructionSelector(raw_ostream &OS) {
<< " std::vector<SDValue> Ops(N.getNode()->op_begin(), N.getNode()->op_end());\n"
<< " SelectInlineAsmMemoryOperands(Ops);\n\n"
- << " std::vector<MVT> VTs;\n"
+ << " std::vector<EVT> VTs;\n"
<< " VTs.push_back(MVT::Other);\n"
<< " VTs.push_back(MVT::Flag);\n"
<< " SDValue New = CurDAG->getNode(ISD::INLINEASM, N.getDebugLoc(), "
@@ -1959,32 +1951,14 @@ void DAGISelEmitter::EmitInstructionSelector(raw_ostream &OS) {
<< " MVT::Other, Tmp, Chain);\n"
<< "}\n\n";
- OS << "SDNode *Select_DECLARE(const SDValue &N) {\n"
- << " SDValue Chain = N.getOperand(0);\n"
- << " SDValue N1 = N.getOperand(1);\n"
- << " SDValue N2 = N.getOperand(2);\n"
- << " if (!isa<FrameIndexSDNode>(N1) || !isa<GlobalAddressSDNode>(N2)) {\n"
- << " CannotYetSelect(N);\n"
- << " }\n"
- << " int FI = cast<FrameIndexSDNode>(N1)->getIndex();\n"
- << " GlobalValue *GV = cast<GlobalAddressSDNode>(N2)->getGlobal();\n"
- << " SDValue Tmp1 = "
- << "CurDAG->getTargetFrameIndex(FI, TLI.getPointerTy());\n"
- << " SDValue Tmp2 = "
- << "CurDAG->getTargetGlobalAddress(GV, TLI.getPointerTy());\n"
- << " return CurDAG->SelectNodeTo(N.getNode(), TargetInstrInfo::DECLARE,\n"
- << " MVT::Other, Tmp1, Tmp2, Chain);\n"
- << "}\n\n";
-
OS << "// The main instruction selector code.\n"
<< "SDNode *SelectCode(SDValue N) {\n"
- << " MVT::SimpleValueType NVT = N.getNode()->getValueType(0).getSimpleVT();\n"
+ << " MVT::SimpleValueType NVT = N.getNode()->getValueType(0).getSimpleVT().SimpleTy;\n"
<< " switch (N.getOpcode()) {\n"
<< " default:\n"
<< " assert(!N.isMachineOpcode() && \"Node already selected!\");\n"
<< " break;\n"
<< " case ISD::EntryToken: // These nodes remain the same.\n"
- << " case ISD::MEMOPERAND:\n"
<< " case ISD::BasicBlock:\n"
<< " case ISD::Register:\n"
<< " case ISD::HANDLENODE:\n"
@@ -2009,7 +1983,6 @@ void DAGISelEmitter::EmitInstructionSelector(raw_ostream &OS) {
<< " case ISD::INLINEASM: return Select_INLINEASM(N);\n"
<< " case ISD::DBG_LABEL: return Select_DBG_LABEL(N);\n"
<< " case ISD::EH_LABEL: return Select_EH_LABEL(N);\n"
- << " case ISD::DECLARE: return Select_DECLARE(N);\n"
<< " case ISD::UNDEF: return Select_UNDEF(N);\n";
// Loop over all of the case statements, emiting a call to each method we
@@ -2083,20 +2056,19 @@ void DAGISelEmitter::EmitInstructionSelector(raw_ostream &OS) {
<< "}\n\n";
OS << "void CannotYetSelect(SDValue N) DISABLE_INLINE {\n"
- << " cerr << \"Cannot yet select: \";\n"
- << " N.getNode()->dump(CurDAG);\n"
- << " cerr << '\\n';\n"
- << " abort();\n"
+ << " std::string msg;\n"
+ << " raw_string_ostream Msg(msg);\n"
+ << " Msg << \"Cannot yet select: \";\n"
+ << " N.getNode()->print(Msg, CurDAG);\n"
+ << " llvm_report_error(Msg.str());\n"
<< "}\n\n";
OS << "void CannotYetSelectIntrinsic(SDValue N) DISABLE_INLINE {\n"
- << " cerr << \"Cannot yet select: \";\n"
+ << " errs() << \"Cannot yet select: \";\n"
<< " unsigned iid = cast<ConstantSDNode>(N.getOperand("
<< "N.getOperand(0).getValueType() == MVT::Other))->getZExtValue();\n"
- << " cerr << \"intrinsic %\"<< "
- << "Intrinsic::getName((Intrinsic::ID)iid);\n"
- << " cerr << '\\n';\n"
- << " abort();\n"
+ << " llvm_report_error(\"Cannot yet select: intrinsic %\" +\n"
+ << "Intrinsic::getName((Intrinsic::ID)iid));\n"
<< "}\n\n";
}
@@ -2115,12 +2087,12 @@ void DAGISelEmitter::run(raw_ostream &OS) {
EmitNodeTransforms(OS);
EmitPredicateFunctions(OS);
- DOUT << "\n\nALL PATTERNS TO MATCH:\n\n";
+ DEBUG(errs() << "\n\nALL PATTERNS TO MATCH:\n\n");
for (CodeGenDAGPatterns::ptm_iterator I = CGP.ptm_begin(), E = CGP.ptm_end();
I != E; ++I) {
- DOUT << "PATTERN: "; DEBUG(I->getSrcPattern()->dump());
- DOUT << "\nRESULT: "; DEBUG(I->getDstPattern()->dump());
- DOUT << "\n";
+ DEBUG(errs() << "PATTERN: "; I->getSrcPattern()->dump());
+ DEBUG(errs() << "\nRESULT: "; I->getDstPattern()->dump());
+ DEBUG(errs() << "\n");
}
// At this point, we have full information about the 'Patterns' we need to
diff --git a/utils/TableGen/FastISelEmitter.cpp b/utils/TableGen/FastISelEmitter.cpp
index 0a43f02..277640d 100644
--- a/utils/TableGen/FastISelEmitter.cpp
+++ b/utils/TableGen/FastISelEmitter.cpp
@@ -216,7 +216,6 @@ public:
explicit FastISelMap(std::string InstNS);
void CollectPatterns(CodeGenDAGPatterns &CGP);
- void PrintClass(raw_ostream &OS);
void PrintFunctionDefinitions(raw_ostream &OS);
};
@@ -461,11 +460,11 @@ void FastISelMap::PrintFunctionDefinitions(raw_ostream &OS) {
<< getLegalCName(Opcode) << "_"
<< getLegalCName(getName(VT)) << "_";
Operands.PrintManglingSuffix(OS);
- OS << "(MVT::SimpleValueType RetVT";
+ OS << "(MVT RetVT";
if (!Operands.empty())
OS << ", ";
Operands.PrintParameters(OS);
- OS << ") {\nswitch (RetVT) {\n";
+ OS << ") {\nswitch (RetVT.SimpleTy) {\n";
for (RetPredMap::const_iterator RI = RM.begin(), RE = RM.end();
RI != RE; ++RI) {
MVT::SimpleValueType RetVT = RI->first;
@@ -485,13 +484,13 @@ void FastISelMap::PrintFunctionDefinitions(raw_ostream &OS) {
<< getLegalCName(Opcode) << "_"
<< getLegalCName(getName(VT)) << "_";
Operands.PrintManglingSuffix(OS);
- OS << "(MVT::SimpleValueType RetVT";
+ OS << "(MVT RetVT";
if (!Operands.empty())
OS << ", ";
Operands.PrintParameters(OS);
OS << ") {\n";
- OS << " if (RetVT != " << getName(RM.begin()->first)
+ OS << " if (RetVT.SimpleTy != " << getName(RM.begin()->first)
<< ")\n return 0;\n";
const PredMap &PM = RM.begin()->second;
@@ -555,12 +554,12 @@ void FastISelMap::PrintFunctionDefinitions(raw_ostream &OS) {
OS << "unsigned FastEmit_"
<< getLegalCName(Opcode) << "_";
Operands.PrintManglingSuffix(OS);
- OS << "(MVT::SimpleValueType VT, MVT::SimpleValueType RetVT";
+ OS << "(MVT VT, MVT RetVT";
if (!Operands.empty())
OS << ", ";
Operands.PrintParameters(OS);
OS << ") {\n";
- OS << " switch (VT) {\n";
+ OS << " switch (VT.SimpleTy) {\n";
for (TypeRetPredMap::const_iterator TI = TM.begin(), TE = TM.end();
TI != TE; ++TI) {
MVT::SimpleValueType VT = TI->first;
@@ -587,7 +586,7 @@ void FastISelMap::PrintFunctionDefinitions(raw_ostream &OS) {
// on opcode and type.
OS << "unsigned FastEmit_";
Operands.PrintManglingSuffix(OS);
- OS << "(MVT::SimpleValueType VT, MVT::SimpleValueType RetVT, ISD::NodeType Opcode";
+ OS << "(MVT VT, MVT RetVT, ISD::NodeType Opcode";
if (!Operands.empty())
OS << ", ";
Operands.PrintParameters(OS);
diff --git a/utils/TableGen/InstrInfoEmitter.cpp b/utils/TableGen/InstrInfoEmitter.cpp
index 4502da1..3a104ea 100644
--- a/utils/TableGen/InstrInfoEmitter.cpp
+++ b/utils/TableGen/InstrInfoEmitter.cpp
@@ -15,6 +15,7 @@
#include "InstrInfoEmitter.h"
#include "CodeGenTarget.h"
#include "Record.h"
+#include "llvm/ADT/StringExtras.h"
#include <algorithm>
using namespace llvm;
@@ -94,13 +95,16 @@ InstrInfoEmitter::GetOperandInfo(const CodeGenInstruction &Inst) {
if (OpR->isSubClassOf("RegisterClass"))
Res += getQualifiedName(OpR) + "RegClassID, ";
+ else if (OpR->isSubClassOf("PointerLikeRegClass"))
+ Res += utostr(OpR->getValueAsInt("RegClassKind")) + ", ";
else
Res += "0, ";
+
// Fill in applicable flags.
Res += "0";
// Ptr value whose register class is resolved via callback.
- if (OpR->getName() == "ptr_rc")
+ if (OpR->isSubClassOf("PointerLikeRegClass"))
Res += "|(1<<TOI::LookupPtrRegClass)";
// Predicate operands. Check to see if the original unexpanded operand
@@ -276,6 +280,8 @@ void InstrInfoEmitter::emitRecord(const CodeGenInstruction &Inst, unsigned Num,
if (Inst.isVariadic) OS << "|(1<<TID::Variadic)";
if (Inst.hasSideEffects) OS << "|(1<<TID::UnmodeledSideEffects)";
if (Inst.isAsCheapAsAMove) OS << "|(1<<TID::CheapAsAMove)";
+ if (Inst.hasExtraSrcRegAllocReq) OS << "|(1<<TID::ExtraSrcRegAllocReq)";
+ if (Inst.hasExtraDefRegAllocReq) OS << "|(1<<TID::ExtraDefRegAllocReq)";
OS << ", 0";
// Emit all of the target-specific flags...
@@ -335,7 +341,7 @@ void InstrInfoEmitter::emitShiftedValue(Record *R, StringInit *Val,
R->getName() != "DBG_LABEL" &&
R->getName() != "EH_LABEL" &&
R->getName() != "GC_LABEL" &&
- R->getName() != "DECLARE" &&
+ R->getName() != "KILL" &&
R->getName() != "EXTRACT_SUBREG" &&
R->getName() != "INSERT_SUBREG" &&
R->getName() != "IMPLICIT_DEF" &&
diff --git a/utils/TableGen/IntrinsicEmitter.cpp b/utils/TableGen/IntrinsicEmitter.cpp
index 3676831..23919d9 100644
--- a/utils/TableGen/IntrinsicEmitter.cpp
+++ b/utils/TableGen/IntrinsicEmitter.cpp
@@ -141,24 +141,26 @@ EmitIntrinsicToOverloadTable(const std::vector<CodeGenIntrinsic> &Ints,
}
static void EmitTypeForValueType(raw_ostream &OS, MVT::SimpleValueType VT) {
- if (MVT(VT).isInteger()) {
- unsigned BitWidth = MVT(VT).getSizeInBits();
- OS << "IntegerType::get(" << BitWidth << ")";
+ if (EVT(VT).isInteger()) {
+ unsigned BitWidth = EVT(VT).getSizeInBits();
+ OS << "IntegerType::get(Context, " << BitWidth << ")";
} else if (VT == MVT::Other) {
// MVT::OtherVT is used to mean the empty struct type here.
- OS << "StructType::get()";
+ OS << "StructType::get(Context)";
} else if (VT == MVT::f32) {
- OS << "Type::FloatTy";
+ OS << "Type::getFloatTy(Context)";
} else if (VT == MVT::f64) {
- OS << "Type::DoubleTy";
+ OS << "Type::getDoubleTy(Context)";
} else if (VT == MVT::f80) {
- OS << "Type::X86_FP80Ty";
+ OS << "Type::getX86_FP80Ty(Context)";
} else if (VT == MVT::f128) {
- OS << "Type::FP128Ty";
+ OS << "Type::getFP128Ty(Context)";
} else if (VT == MVT::ppcf128) {
- OS << "Type::PPC_FP128Ty";
+ OS << "Type::getPPC_FP128Ty(Context)";
} else if (VT == MVT::isVoid) {
- OS << "Type::VoidTy";
+ OS << "Type::getVoidTy(Context)";
+ } else if (VT == MVT::Metadata) {
+ OS << "Type::getMetadataTy(Context)";
} else {
assert(false && "Unsupported ValueType!");
}
@@ -175,7 +177,7 @@ static void EmitTypeGenerate(raw_ostream &OS,
return;
}
- OS << "StructType::get(";
+ OS << "StructType::get(Context, ";
for (std::vector<Record*>::const_iterator
I = ArgTypes.begin(), E = ArgTypes.end(); I != E; ++I) {
@@ -201,17 +203,17 @@ static void EmitTypeGenerate(raw_ostream &OS, const Record *ArgType,
<< "(dyn_cast<VectorType>(Tys[" << Number << "]))";
else
OS << "Tys[" << Number << "]";
- } else if (VT == MVT::iAny || VT == MVT::fAny) {
+ } else if (VT == MVT::iAny || VT == MVT::fAny || VT == MVT::vAny) {
// NOTE: The ArgNo variable here is not the absolute argument number, it is
// the index of the "arbitrary" type in the Tys array passed to the
// Intrinsic::getDeclaration function. Consequently, we only want to
// increment it when we actually hit an overloaded type. Getting this wrong
// leads to very subtle bugs!
OS << "Tys[" << ArgNo++ << "]";
- } else if (MVT(VT).isVector()) {
- MVT VVT = VT;
+ } else if (EVT(VT).isVector()) {
+ EVT VVT = VT;
OS << "VectorType::get(";
- EmitTypeForValueType(OS, VVT.getVectorElementType().getSimpleVT());
+ EmitTypeForValueType(OS, VVT.getVectorElementType().getSimpleVT().SimpleTy);
OS << ", " << VVT.getVectorNumElements() << ")";
} else if (VT == MVT::iPTR) {
OS << "PointerType::getUnqual(";
@@ -227,7 +229,7 @@ static void EmitTypeGenerate(raw_ostream &OS, const Record *ArgType,
++ArgNo;
} else if (VT == MVT::isVoid) {
if (ArgNo == 0)
- OS << "Type::VoidTy";
+ OS << "Type::getVoidTy(Context)";
else
// MVT::isVoid is used to mean varargs here.
OS << "...";
@@ -302,6 +304,7 @@ void IntrinsicEmitter::EmitVerifier(const std::vector<CodeGenIntrinsic> &Ints,
const RecPair &ArgTypes = I->first;
const std::vector<Record*> &RetTys = ArgTypes.first;
const std::vector<Record*> &ParamTys = ArgTypes.second;
+ std::vector<unsigned> OverloadedTypeIndices;
OS << " VerifyIntrinsicPrototype(ID, IF, " << RetTys.size() << ", "
<< ParamTys.size();
@@ -313,6 +316,9 @@ void IntrinsicEmitter::EmitVerifier(const std::vector<CodeGenIntrinsic> &Ints,
if (ArgType->isSubClassOf("LLVMMatchType")) {
unsigned Number = ArgType->getValueAsInt("Number");
+ assert(Number < OverloadedTypeIndices.size() &&
+ "Invalid matching number!");
+ Number = OverloadedTypeIndices[Number];
if (ArgType->isSubClassOf("LLVMExtendedElementVectorType"))
OS << "~(ExtendedElementVectorType | " << Number << ")";
else if (ArgType->isSubClassOf("LLVMTruncatedElementVectorType"))
@@ -323,6 +329,9 @@ void IntrinsicEmitter::EmitVerifier(const std::vector<CodeGenIntrinsic> &Ints,
MVT::SimpleValueType VT = getValueType(ArgType->getValueAsDef("VT"));
OS << getEnumName(VT);
+ if (EVT(VT).isOverloaded())
+ OverloadedTypeIndices.push_back(j);
+
if (VT == MVT::isVoid && j != 0 && j != je - 1)
throw "Var arg type not last argument";
}
@@ -335,6 +344,9 @@ void IntrinsicEmitter::EmitVerifier(const std::vector<CodeGenIntrinsic> &Ints,
if (ArgType->isSubClassOf("LLVMMatchType")) {
unsigned Number = ArgType->getValueAsInt("Number");
+ assert(Number < OverloadedTypeIndices.size() &&
+ "Invalid matching number!");
+ Number = OverloadedTypeIndices[Number];
if (ArgType->isSubClassOf("LLVMExtendedElementVectorType"))
OS << "~(ExtendedElementVectorType | " << Number << ")";
else if (ArgType->isSubClassOf("LLVMTruncatedElementVectorType"))
@@ -345,6 +357,9 @@ void IntrinsicEmitter::EmitVerifier(const std::vector<CodeGenIntrinsic> &Ints,
MVT::SimpleValueType VT = getValueType(ArgType->getValueAsDef("VT"));
OS << getEnumName(VT);
+ if (EVT(VT).isOverloaded())
+ OverloadedTypeIndices.push_back(j + RetTys.size());
+
if (VT == MVT::isVoid && j != 0 && j != je - 1)
throw "Var arg type not last argument";
}
diff --git a/utils/TableGen/LLVMCConfigurationEmitter.cpp b/utils/TableGen/LLVMCConfigurationEmitter.cpp
index fc182ce..06afaf7 100644
--- a/utils/TableGen/LLVMCConfigurationEmitter.cpp
+++ b/utils/TableGen/LLVMCConfigurationEmitter.cpp
@@ -39,10 +39,11 @@ typedef std::vector<std::string> StrVector;
//===----------------------------------------------------------------------===//
/// Constants
-// Indentation strings.
-const char * Indent1 = " ";
-const char * Indent2 = " ";
-const char * Indent3 = " ";
+// Indentation.
+unsigned TabWidth = 4;
+unsigned Indent1 = TabWidth*1;
+unsigned Indent2 = TabWidth*2;
+unsigned Indent3 = TabWidth*3;
// Default help string.
const char * DefaultHelpString = "NO HELP MESSAGE PROVIDED";
@@ -81,16 +82,15 @@ const DagInit& InitPtrToDag(const Init* ptr) {
}
// checkNumberOfArguments - Ensure that the number of args in d is
-// less than or equal to min_arguments, otherwise throw an exception.
+// greater than or equal to min_arguments, otherwise throw an exception.
void checkNumberOfArguments (const DagInit* d, unsigned min_arguments) {
if (!d || d->getNumArgs() < min_arguments)
- throw d->getOperator()->getAsString()
- + ": too few arguments!";
+ throw d->getOperator()->getAsString() + ": too few arguments!";
}
// isDagEmpty - is this DAG marked with an empty marker?
bool isDagEmpty (const DagInit* d) {
- return d->getOperator()->getAsString() == "empty";
+ return d->getOperator()->getAsString() == "empty_dag_marker";
}
// EscapeVariableName - Escape commas and other symbols not allowed
@@ -139,20 +139,21 @@ void checkedIncrement(I& P, I E, S ErrorString) {
/// OptionType - One of six different option types. See the
/// documentation for detailed description of differences.
namespace OptionType {
+
enum OptionType { Alias, Switch, Parameter, ParameterList,
Prefix, PrefixList};
-bool IsList (OptionType t) {
- return (t == ParameterList || t == PrefixList);
-}
+ bool IsList (OptionType t) {
+ return (t == ParameterList || t == PrefixList);
+ }
-bool IsSwitch (OptionType t) {
- return (t == Switch);
-}
+ bool IsSwitch (OptionType t) {
+ return (t == Switch);
+ }
-bool IsParameter (OptionType t) {
- return (t == Parameter || t == Prefix);
-}
+ bool IsParameter (OptionType t) {
+ return (t == Parameter || t == Prefix);
+ }
}
@@ -187,11 +188,12 @@ struct OptionDescription {
unsigned Flags;
std::string Help;
unsigned MultiVal;
+ Init* InitVal;
OptionDescription(OptionType::OptionType t = OptionType::Switch,
const std::string& n = "",
const std::string& h = DefaultHelpString)
- : Type(t), Name(n), Flags(0x0), Help(h), MultiVal(1)
+ : Type(t), Name(n), Flags(0x0), Help(h), MultiVal(1), InitVal(0)
{}
/// GenTypeDeclaration - Returns the C++ variable type of this
@@ -229,6 +231,15 @@ struct OptionDescription {
bool isReallyHidden() const;
void setReallyHidden();
+ bool isParameter() const
+ { return OptionType::IsParameter(this->Type); }
+
+ bool isSwitch() const
+ { return OptionType::IsSwitch(this->Type); }
+
+ bool isList() const
+ { return OptionType::IsList(this->Type); }
+
};
void OptionDescription::Merge (const OptionDescription& other)
@@ -438,6 +449,7 @@ public:
AddHandler("extern", &CollectOptionProperties::onExtern);
AddHandler("help", &CollectOptionProperties::onHelp);
AddHandler("hidden", &CollectOptionProperties::onHidden);
+ AddHandler("init", &CollectOptionProperties::onInit);
AddHandler("multi_val", &CollectOptionProperties::onMultiVal);
AddHandler("one_or_more", &CollectOptionProperties::onOneOrMore);
AddHandler("really_hidden", &CollectOptionProperties::onReallyHidden);
@@ -481,6 +493,20 @@ private:
optDesc_.setRequired();
}
+ void onInit (const DagInit* d) {
+ checkNumberOfArguments(d, 1);
+ Init* i = d->getArg(0);
+ const std::string& str = i->getAsString();
+
+ bool correct = optDesc_.isParameter() && dynamic_cast<StringInit*>(i);
+ correct |= (optDesc_.isSwitch() && (str == "true" || str == "false"));
+
+ if (!correct)
+ throw std::string("Incorrect usage of the 'init' option property!");
+
+ optDesc_.InitVal = i;
+ }
+
void onOneOrMore (const DagInit* d) {
checkNumberOfArguments(d, 0);
if (optDesc_.isRequired() || optDesc_.isZeroOrOne())
@@ -950,8 +976,22 @@ void CheckForSuperfluousOptions (const RecordVector& Edges,
}
}
-/// EmitCaseTest1Arg - Helper function used by
-/// EmitCaseConstructHandler.
+/// EmitCaseTest0Args - Helper function used by EmitCaseConstructHandler().
+bool EmitCaseTest0Args(const std::string& TestName, raw_ostream& O) {
+ if (TestName == "single_input_file") {
+ O << "InputFilenames.size() == 1";
+ return true;
+ }
+ else if (TestName == "multiple_input_files") {
+ O << "InputFilenames.size() > 1";
+ return true;
+ }
+
+ return false;
+}
+
+
+/// EmitCaseTest1Arg - Helper function used by EmitCaseConstructHandler().
bool EmitCaseTest1Arg(const std::string& TestName,
const DagInit& d,
const OptionDescriptions& OptDescs,
@@ -961,7 +1001,7 @@ bool EmitCaseTest1Arg(const std::string& TestName,
if (TestName == "switch_on") {
const OptionDescription& OptDesc = OptDescs.FindOption(OptName);
- if (!OptionType::IsSwitch(OptDesc.Type))
+ if (!OptDesc.isSwitch())
throw OptName + ": incorrect option type - should be a switch!";
O << OptDesc.GenVariableName();
return true;
@@ -984,7 +1024,7 @@ bool EmitCaseTest1Arg(const std::string& TestName,
}
else {
const OptionDescription& OptDesc = OptDescs.FindOption(OptName);
- if (OptionType::IsSwitch(OptDesc.Type))
+ if (OptDesc.isSwitch())
throw OptName
+ ": incorrect option type - should be a list or parameter!";
O << Test << OptDesc.GenVariableName() << ".empty()";
@@ -995,11 +1035,10 @@ bool EmitCaseTest1Arg(const std::string& TestName,
return false;
}
-/// EmitCaseTest2Args - Helper function used by
-/// EmitCaseConstructHandler.
+/// EmitCaseTest2Args - Helper function used by EmitCaseConstructHandler().
bool EmitCaseTest2Args(const std::string& TestName,
const DagInit& d,
- const char* IndentLevel,
+ unsigned IndentLevel,
const OptionDescriptions& OptDescs,
raw_ostream& O) {
checkNumberOfArguments(&d, 2);
@@ -1008,17 +1047,18 @@ bool EmitCaseTest2Args(const std::string& TestName,
const OptionDescription& OptDesc = OptDescs.FindOption(OptName);
if (TestName == "parameter_equals") {
- if (!OptionType::IsParameter(OptDesc.Type))
+ if (!OptDesc.isParameter())
throw OptName + ": incorrect option type - should be a parameter!";
O << OptDesc.GenVariableName() << " == \"" << OptArg << "\"";
return true;
}
else if (TestName == "element_in_list") {
- if (!OptionType::IsList(OptDesc.Type))
+ if (!OptDesc.isList())
throw OptName + ": incorrect option type - should be a list!";
const std::string& VarName = OptDesc.GenVariableName();
- O << "std::find(" << VarName << ".begin(),\n"
- << IndentLevel << Indent1 << VarName << ".end(), \""
+ O << "std::find(" << VarName << ".begin(),\n";
+ O.indent(IndentLevel + Indent1)
+ << VarName << ".end(), \""
<< OptArg << "\") != " << VarName << ".end()";
return true;
}
@@ -1028,29 +1068,42 @@ bool EmitCaseTest2Args(const std::string& TestName,
// Forward declaration.
// EmitLogicalOperationTest and EmitCaseTest are mutually recursive.
-void EmitCaseTest(const DagInit& d, const char* IndentLevel,
+void EmitCaseTest(const DagInit& d, unsigned IndentLevel,
const OptionDescriptions& OptDescs,
raw_ostream& O);
/// EmitLogicalOperationTest - Helper function used by
/// EmitCaseConstructHandler.
void EmitLogicalOperationTest(const DagInit& d, const char* LogicOp,
- const char* IndentLevel,
+ unsigned IndentLevel,
const OptionDescriptions& OptDescs,
raw_ostream& O) {
O << '(';
for (unsigned j = 0, NumArgs = d.getNumArgs(); j < NumArgs; ++j) {
const DagInit& InnerTest = InitPtrToDag(d.getArg(j));
EmitCaseTest(InnerTest, IndentLevel, OptDescs, O);
- if (j != NumArgs - 1)
- O << ")\n" << IndentLevel << Indent1 << ' ' << LogicOp << " (";
- else
+ if (j != NumArgs - 1) {
+ O << ")\n";
+ O.indent(IndentLevel + Indent1) << ' ' << LogicOp << " (";
+ }
+ else {
O << ')';
+ }
}
}
+void EmitLogicalNot(const DagInit& d, unsigned IndentLevel,
+ const OptionDescriptions& OptDescs, raw_ostream& O)
+{
+ checkNumberOfArguments(&d, 1);
+ const DagInit& InnerTest = InitPtrToDag(d.getArg(0));
+ O << "! (";
+ EmitCaseTest(InnerTest, IndentLevel, OptDescs, O);
+ O << ")";
+}
+
/// EmitCaseTest - Helper function used by EmitCaseConstructHandler.
-void EmitCaseTest(const DagInit& d, const char* IndentLevel,
+void EmitCaseTest(const DagInit& d, unsigned IndentLevel,
const OptionDescriptions& OptDescs,
raw_ostream& O) {
const std::string& TestName = d.getOperator()->getAsString();
@@ -1059,6 +1112,10 @@ void EmitCaseTest(const DagInit& d, const char* IndentLevel,
EmitLogicalOperationTest(d, "&&", IndentLevel, OptDescs, O);
else if (TestName == "or")
EmitLogicalOperationTest(d, "||", IndentLevel, OptDescs, O);
+ else if (TestName == "not")
+ EmitLogicalNot(d, IndentLevel, OptDescs, O);
+ else if (EmitCaseTest0Args(TestName, O))
+ return;
else if (EmitCaseTest1Arg(TestName, d, OptDescs, O))
return;
else if (EmitCaseTest2Args(TestName, d, IndentLevel, OptDescs, O))
@@ -1070,9 +1127,9 @@ void EmitCaseTest(const DagInit& d, const char* IndentLevel,
// Emit code that handles the 'case' construct.
// Takes a function object that should emit code for every case clause.
// Callback's type is
-// void F(Init* Statement, const char* IndentLevel, raw_ostream& O).
+// void F(Init* Statement, unsigned IndentLevel, raw_ostream& O).
template <typename F>
-void EmitCaseConstructHandler(const Init* Dag, const char* IndentLevel,
+void EmitCaseConstructHandler(const Init* Dag, unsigned IndentLevel,
F Callback, bool EmitElseIf,
const OptionDescriptions& OptDescs,
raw_ostream& O) {
@@ -1094,10 +1151,10 @@ void EmitCaseConstructHandler(const Init* Dag, const char* IndentLevel,
if (i+2 != numArgs)
throw std::string("The 'default' clause should be the last in the"
"'case' construct!");
- O << IndentLevel << "else {\n";
+ O.indent(IndentLevel) << "else {\n";
}
else {
- O << IndentLevel << ((i != 0 && EmitElseIf) ? "else if (" : "if (");
+ O.indent(IndentLevel) << ((i != 0 && EmitElseIf) ? "else if (" : "if (");
EmitCaseTest(Test, IndentLevel, OptDescs, O);
O << ") {\n";
}
@@ -1112,13 +1169,13 @@ void EmitCaseConstructHandler(const Init* Dag, const char* IndentLevel,
const DagInit* nd = dynamic_cast<DagInit*>(arg);
if (nd && (nd->getOperator()->getAsString() == "case")) {
// Handle the nested 'case'.
- EmitCaseConstructHandler(nd, (std::string(IndentLevel) + Indent1).c_str(),
+ EmitCaseConstructHandler(nd, (IndentLevel + Indent1),
Callback, EmitElseIf, OptDescs, O);
}
else {
- Callback(arg, (std::string(IndentLevel) + Indent1).c_str(), O);
+ Callback(arg, (IndentLevel + Indent1), O);
}
- O << IndentLevel << "}\n";
+ O.indent(IndentLevel) << "}\n";
}
}
@@ -1272,7 +1329,7 @@ StrVector::const_iterator SubstituteSpecialCommands
/// EmitCmdLineVecFill - Emit code that fills in the command line
/// vector. Helper function used by EmitGenerateActionMethod().
void EmitCmdLineVecFill(const Init* CmdLine, const std::string& ToolName,
- bool IsJoin, const char* IndentLevel,
+ bool IsJoin, unsigned IndentLevel,
raw_ostream& O) {
StrVector StrVec;
TokenizeCmdline(InitPtrToString(CmdLine), StrVec);
@@ -1295,22 +1352,28 @@ void EmitCmdLineVecFill(const Init* CmdLine, const std::string& ToolName,
++I;
}
+ bool hasINFILE = false;
+
for (; I != E; ++I) {
const std::string& cmd = *I;
assert(!cmd.empty());
- O << IndentLevel;
+ O.indent(IndentLevel);
if (cmd.at(0) == '$') {
if (cmd == "$INFILE") {
- if (IsJoin)
+ hasINFILE = true;
+ if (IsJoin) {
O << "for (PathVector::const_iterator B = inFiles.begin()"
- << ", E = inFiles.end();\n"
- << IndentLevel << "B != E; ++B)\n"
- << IndentLevel << Indent1 << "vec.push_back(B->toString());\n";
- else
- O << "vec.push_back(inFile.toString());\n";
+ << ", E = inFiles.end();\n";
+ O.indent(IndentLevel) << "B != E; ++B)\n";
+ O.indent(IndentLevel + Indent1) << "vec.push_back(B->str());\n";
+ }
+ else {
+ O << "vec.push_back(inFile.str());\n";
+ }
}
else if (cmd == "$OUTFILE") {
- O << "vec.push_back(out_file);\n";
+ O << "vec.push_back(\"\");\n";
+ O.indent(IndentLevel) << "out_file_index = vec.size()-1;\n";
}
else {
O << "vec.push_back(";
@@ -1322,8 +1385,10 @@ void EmitCmdLineVecFill(const Init* CmdLine, const std::string& ToolName,
O << "vec.push_back(\"" << cmd << "\");\n";
}
}
- O << IndentLevel << "cmd = ";
+ if (!hasINFILE)
+ throw "Tool '" + ToolName + "' doesn't take any input!";
+ O.indent(IndentLevel) << "cmd = ";
if (StrVec[0][0] == '$')
SubstituteSpecialCommands(StrVec.begin(), StrVec.end(), O);
else
@@ -1341,11 +1406,10 @@ class EmitCmdLineVecFillCallback {
EmitCmdLineVecFillCallback(bool J, const std::string& TN)
: IsJoin(J), ToolName(TN) {}
- void operator()(const Init* Statement, const char* IndentLevel,
+ void operator()(const Init* Statement, unsigned IndentLevel,
raw_ostream& O) const
{
- EmitCmdLineVecFill(Statement, ToolName, IsJoin,
- IndentLevel, O);
+ EmitCmdLineVecFill(Statement, ToolName, IsJoin, IndentLevel, O);
}
};
@@ -1353,53 +1417,56 @@ class EmitCmdLineVecFillCallback {
/// implement EmitActionHandler. Emits code for
/// handling the (forward) and (forward_as) option properties.
void EmitForwardOptionPropertyHandlingCode (const OptionDescription& D,
- const char* Indent,
+ unsigned IndentLevel,
const std::string& NewName,
raw_ostream& O) {
const std::string& Name = NewName.empty()
? ("-" + D.Name)
: NewName;
+ unsigned IndentLevel1 = IndentLevel + Indent1;
switch (D.Type) {
case OptionType::Switch:
- O << Indent << "vec.push_back(\"" << Name << "\");\n";
+ O.indent(IndentLevel) << "vec.push_back(\"" << Name << "\");\n";
break;
case OptionType::Parameter:
- O << Indent << "vec.push_back(\"" << Name << "\");\n";
- O << Indent << "vec.push_back(" << D.GenVariableName() << ");\n";
+ O.indent(IndentLevel) << "vec.push_back(\"" << Name << "\");\n";
+ O.indent(IndentLevel) << "vec.push_back(" << D.GenVariableName() << ");\n";
break;
case OptionType::Prefix:
- O << Indent << "vec.push_back(\"" << Name << "\" + "
- << D.GenVariableName() << ");\n";
+ O.indent(IndentLevel) << "vec.push_back(\"" << Name << "\" + "
+ << D.GenVariableName() << ");\n";
break;
case OptionType::PrefixList:
- O << Indent << "for (" << D.GenTypeDeclaration()
- << "::iterator B = " << D.GenVariableName() << ".begin(),\n"
- << Indent << "E = " << D.GenVariableName() << ".end(); B != E;) {\n"
- << Indent << Indent1 << "vec.push_back(\"" << Name << "\" + "
- << "*B);\n"
- << Indent << Indent1 << "++B;\n";
+ O.indent(IndentLevel)
+ << "for (" << D.GenTypeDeclaration()
+ << "::iterator B = " << D.GenVariableName() << ".begin(),\n";
+ O.indent(IndentLevel)
+ << "E = " << D.GenVariableName() << ".end(); B != E;) {\n";
+ O.indent(IndentLevel1) << "vec.push_back(\"" << Name << "\" + " << "*B);\n";
+ O.indent(IndentLevel1) << "++B;\n";
for (int i = 1, j = D.MultiVal; i < j; ++i) {
- O << Indent << Indent1 << "vec.push_back(*B);\n"
- << Indent << Indent1 << "++B;\n";
+ O.indent(IndentLevel1) << "vec.push_back(*B);\n";
+ O.indent(IndentLevel1) << "++B;\n";
}
- O << Indent << "}\n";
+ O.indent(IndentLevel) << "}\n";
break;
case OptionType::ParameterList:
- O << Indent << "for (" << D.GenTypeDeclaration()
- << "::iterator B = " << D.GenVariableName() << ".begin(),\n"
- << Indent << "E = " << D.GenVariableName()
- << ".end() ; B != E;) {\n"
- << Indent << Indent1 << "vec.push_back(\"" << Name << "\");\n";
+ O.indent(IndentLevel)
+ << "for (" << D.GenTypeDeclaration() << "::iterator B = "
+ << D.GenVariableName() << ".begin(),\n";
+ O.indent(IndentLevel) << "E = " << D.GenVariableName()
+ << ".end() ; B != E;) {\n";
+ O.indent(IndentLevel1) << "vec.push_back(\"" << Name << "\");\n";
for (int i = 0, j = D.MultiVal; i < j; ++i) {
- O << Indent << Indent1 << "vec.push_back(*B);\n"
- << Indent << Indent1 << "++B;\n";
+ O.indent(IndentLevel1) << "vec.push_back(*B);\n";
+ O.indent(IndentLevel1) << "++B;\n";
}
- O << Indent << "}\n";
+ O.indent(IndentLevel) << "}\n";
break;
case OptionType::Alias:
default:
@@ -1413,7 +1480,7 @@ void EmitForwardOptionPropertyHandlingCode (const OptionDescription& D,
class EmitActionHandler {
const OptionDescriptions& OptDescs;
- void processActionDag(const Init* Statement, const char* IndentLevel,
+ void processActionDag(const Init* Statement, unsigned IndentLevel,
raw_ostream& O) const
{
const DagInit& Dag = InitPtrToDag(Statement);
@@ -1427,10 +1494,10 @@ class EmitActionHandler {
for (StrVector::const_iterator B = Out.begin(), E = Out.end();
B != E; ++B)
- O << IndentLevel << "vec.push_back(\"" << *B << "\");\n";
+ O.indent(IndentLevel) << "vec.push_back(\"" << *B << "\");\n";
}
else if (ActionName == "error") {
- O << IndentLevel << "throw std::runtime_error(\"" <<
+ O.indent(IndentLevel) << "throw std::runtime_error(\"" <<
(Dag.getNumArgs() >= 1 ? InitPtrToString(Dag.getArg(0))
: "Unknown error!")
<< "\");\n";
@@ -1451,10 +1518,10 @@ class EmitActionHandler {
else if (ActionName == "output_suffix") {
checkNumberOfArguments(&Dag, 1);
const std::string& OutSuf = InitPtrToString(Dag.getArg(0));
- O << IndentLevel << "output_suffix = \"" << OutSuf << "\";\n";
+ O.indent(IndentLevel) << "output_suffix = \"" << OutSuf << "\";\n";
}
else if (ActionName == "stop_compilation") {
- O << IndentLevel << "stop_compilation = true;\n";
+ O.indent(IndentLevel) << "stop_compilation = true;\n";
}
else if (ActionName == "unpack_values") {
checkNumberOfArguments(&Dag, 1);
@@ -1464,16 +1531,18 @@ class EmitActionHandler {
if (D.isMultiVal())
throw std::string("Can't use unpack_values with multi-valued options!");
- if (OptionType::IsList(D.Type)) {
- O << IndentLevel << "for (" << D.GenTypeDeclaration()
- << "::iterator B = " << D.GenVariableName() << ".begin(),\n"
- << IndentLevel << "E = " << D.GenVariableName()
- << ".end(); B != E; ++B)\n"
- << IndentLevel << Indent1 << "llvm::SplitString(*B, vec, \",\");\n";
+ if (D.isList()) {
+ O.indent(IndentLevel)
+ << "for (" << D.GenTypeDeclaration()
+ << "::iterator B = " << D.GenVariableName() << ".begin(),\n";
+ O.indent(IndentLevel)
+ << "E = " << D.GenVariableName() << ".end(); B != E; ++B)\n";
+ O.indent(IndentLevel + Indent1)
+ << "llvm::SplitString(*B, vec, \",\");\n";
}
- else if (OptionType::IsParameter(D.Type)){
- O << Indent3 << "llvm::SplitString("
- << D.GenVariableName() << ", vec, \",\");\n";
+ else if (D.isParameter()){
+ O.indent(IndentLevel) << "llvm::SplitString("
+ << D.GenVariableName() << ", vec, \",\");\n";
}
else {
throw "Option '" + D.Name +
@@ -1488,7 +1557,7 @@ class EmitActionHandler {
EmitActionHandler(const OptionDescriptions& OD)
: OptDescs(OD) {}
- void operator()(const Init* Statement, const char* IndentLevel,
+ void operator()(const Init* Statement, unsigned IndentLevel,
raw_ostream& O) const
{
if (typeid(*Statement) == typeid(ListInit)) {
@@ -1503,56 +1572,111 @@ class EmitActionHandler {
}
};
-// EmitGenerateActionMethod - Emit one of two versions of the
+bool IsOutFileIndexCheckRequiredStr (const Init* CmdLine) {
+ StrVector StrVec;
+ TokenizeCmdline(InitPtrToString(CmdLine), StrVec);
+
+ for (StrVector::const_iterator I = StrVec.begin(), E = StrVec.end();
+ I != E; ++I) {
+ if (*I == "$OUTFILE")
+ return false;
+ }
+
+ return true;
+}
+
+class IsOutFileIndexCheckRequiredStrCallback {
+ bool* ret_;
+
+public:
+ IsOutFileIndexCheckRequiredStrCallback(bool* ret) : ret_(ret)
+ {}
+
+ void operator()(const Init* CmdLine) {
+ if (IsOutFileIndexCheckRequiredStr(CmdLine))
+ *ret_ = true;
+ }
+};
+
+bool IsOutFileIndexCheckRequiredCase (Init* CmdLine) {
+ bool ret = false;
+ WalkCase(CmdLine, Id(), IsOutFileIndexCheckRequiredStrCallback(&ret));
+ return ret;
+}
+
+/// IsOutFileIndexCheckRequired - Should we emit an "out_file_index != -1" check
+/// in EmitGenerateActionMethod() ?
+bool IsOutFileIndexCheckRequired (Init* CmdLine) {
+ if (typeid(*CmdLine) == typeid(StringInit))
+ return IsOutFileIndexCheckRequiredStr(CmdLine);
+ else
+ return IsOutFileIndexCheckRequiredCase(CmdLine);
+}
+
+// EmitGenerateActionMethod - Emit either a normal or a "join" version of the
// Tool::GenerateAction() method.
void EmitGenerateActionMethod (const ToolDescription& D,
const OptionDescriptions& OptDescs,
bool IsJoin, raw_ostream& O) {
if (IsJoin)
- O << Indent1 << "Action GenerateAction(const PathVector& inFiles,\n";
+ O.indent(Indent1) << "Action GenerateAction(const PathVector& inFiles,\n";
else
- O << Indent1 << "Action GenerateAction(const sys::Path& inFile,\n";
-
- O << Indent2 << "bool HasChildren,\n"
- << Indent2 << "const llvm::sys::Path& TempDir,\n"
- << Indent2 << "const InputLanguagesSet& InLangs,\n"
- << Indent2 << "const LanguageMap& LangMap) const\n"
- << Indent1 << "{\n"
- << Indent2 << "std::string cmd;\n"
- << Indent2 << "std::vector<std::string> vec;\n"
- << Indent2 << "bool stop_compilation = !HasChildren;\n"
- << Indent2 << "const char* output_suffix = \"" << D.OutputSuffix << "\";\n"
- << Indent2 << "std::string out_file;\n\n";
-
- // For every understood option, emit handling code.
- if (D.Actions)
- EmitCaseConstructHandler(D.Actions, Indent2, EmitActionHandler(OptDescs),
- false, OptDescs, O);
-
- O << '\n' << Indent2
- << "out_file = OutFilename(" << (IsJoin ? "sys::Path(),\n" : "inFile,\n")
- << Indent3 << "TempDir, stop_compilation, output_suffix).toString();\n\n";
+ O.indent(Indent1) << "Action GenerateAction(const sys::Path& inFile,\n";
+
+ O.indent(Indent2) << "bool HasChildren,\n";
+ O.indent(Indent2) << "const llvm::sys::Path& TempDir,\n";
+ O.indent(Indent2) << "const InputLanguagesSet& InLangs,\n";
+ O.indent(Indent2) << "const LanguageMap& LangMap) const\n";
+ O.indent(Indent1) << "{\n";
+ O.indent(Indent2) << "std::string cmd;\n";
+ O.indent(Indent2) << "std::vector<std::string> vec;\n";
+ O.indent(Indent2) << "bool stop_compilation = !HasChildren;\n";
+ O.indent(Indent2) << "const char* output_suffix = \""
+ << D.OutputSuffix << "\";\n";
- // cmd_line is either a string or a 'case' construct.
if (!D.CmdLine)
throw "Tool " + D.Name + " has no cmd_line property!";
- else if (typeid(*D.CmdLine) == typeid(StringInit))
+
+ bool IndexCheckRequired = IsOutFileIndexCheckRequired(D.CmdLine);
+ O.indent(Indent2) << "int out_file_index"
+ << (IndexCheckRequired ? " = -1" : "")
+ << ";\n\n";
+
+ // Process the cmd_line property.
+ if (typeid(*D.CmdLine) == typeid(StringInit))
EmitCmdLineVecFill(D.CmdLine, D.Name, IsJoin, Indent2, O);
else
EmitCaseConstructHandler(D.CmdLine, Indent2,
EmitCmdLineVecFillCallback(IsJoin, D.Name),
true, OptDescs, O);
+ // For every understood option, emit handling code.
+ if (D.Actions)
+ EmitCaseConstructHandler(D.Actions, Indent2, EmitActionHandler(OptDescs),
+ false, OptDescs, O);
+
+ O << '\n';
+ O.indent(Indent2)
+ << "std::string out_file = OutFilename("
+ << (IsJoin ? "sys::Path(),\n" : "inFile,\n");
+ O.indent(Indent3) << "TempDir, stop_compilation, output_suffix).str();\n\n";
+
+ if (IndexCheckRequired)
+ O.indent(Indent2) << "if (out_file_index != -1)\n";
+ O.indent(IndexCheckRequired ? Indent3 : Indent2)
+ << "vec[out_file_index] = out_file;\n";
+
// Handle the Sink property.
if (D.isSink()) {
- O << Indent2 << "if (!" << SinkOptionName << ".empty()) {\n"
- << Indent3 << "vec.insert(vec.end(), "
- << SinkOptionName << ".begin(), " << SinkOptionName << ".end());\n"
- << Indent2 << "}\n";
+ O.indent(Indent2) << "if (!" << SinkOptionName << ".empty()) {\n";
+ O.indent(Indent3) << "vec.insert(vec.end(), "
+ << SinkOptionName << ".begin(), " << SinkOptionName
+ << ".end());\n";
+ O.indent(Indent2) << "}\n";
}
- O << Indent2 << "return Action(cmd, vec, stop_compilation, out_file);\n"
- << Indent1 << "}\n\n";
+ O.indent(Indent2) << "return Action(cmd, vec, stop_compilation, out_file);\n";
+ O.indent(Indent1) << "}\n\n";
}
/// EmitGenerateActionMethods - Emit two GenerateAction() methods for
@@ -1560,18 +1684,20 @@ void EmitGenerateActionMethod (const ToolDescription& D,
void EmitGenerateActionMethods (const ToolDescription& ToolDesc,
const OptionDescriptions& OptDescs,
raw_ostream& O) {
- if (!ToolDesc.isJoin())
- O << Indent1 << "Action GenerateAction(const PathVector& inFiles,\n"
- << Indent2 << "bool HasChildren,\n"
- << Indent2 << "const llvm::sys::Path& TempDir,\n"
- << Indent2 << "const InputLanguagesSet& InLangs,\n"
- << Indent2 << "const LanguageMap& LangMap) const\n"
- << Indent1 << "{\n"
- << Indent2 << "throw std::runtime_error(\"" << ToolDesc.Name
- << " is not a Join tool!\");\n"
- << Indent1 << "}\n\n";
- else
+ if (!ToolDesc.isJoin()) {
+ O.indent(Indent1) << "Action GenerateAction(const PathVector& inFiles,\n";
+ O.indent(Indent2) << "bool HasChildren,\n";
+ O.indent(Indent2) << "const llvm::sys::Path& TempDir,\n";
+ O.indent(Indent2) << "const InputLanguagesSet& InLangs,\n";
+ O.indent(Indent2) << "const LanguageMap& LangMap) const\n";
+ O.indent(Indent1) << "{\n";
+ O.indent(Indent2) << "throw std::runtime_error(\"" << ToolDesc.Name
+ << " is not a Join tool!\");\n";
+ O.indent(Indent1) << "}\n\n";
+ }
+ else {
EmitGenerateActionMethod(ToolDesc, OptDescs, true, O);
+ }
EmitGenerateActionMethod(ToolDesc, OptDescs, false, O);
}
@@ -1579,34 +1705,34 @@ void EmitGenerateActionMethods (const ToolDescription& ToolDesc,
/// EmitInOutLanguageMethods - Emit the [Input,Output]Language()
/// methods for a given Tool class.
void EmitInOutLanguageMethods (const ToolDescription& D, raw_ostream& O) {
- O << Indent1 << "const char** InputLanguages() const {\n"
- << Indent2 << "return InputLanguages_;\n"
- << Indent1 << "}\n\n";
+ O.indent(Indent1) << "const char** InputLanguages() const {\n";
+ O.indent(Indent2) << "return InputLanguages_;\n";
+ O.indent(Indent1) << "}\n\n";
if (D.OutLanguage.empty())
throw "Tool " + D.Name + " has no 'out_language' property!";
- O << Indent1 << "const char* OutputLanguage() const {\n"
- << Indent2 << "return \"" << D.OutLanguage << "\";\n"
- << Indent1 << "}\n\n";
+ O.indent(Indent1) << "const char* OutputLanguage() const {\n";
+ O.indent(Indent2) << "return \"" << D.OutLanguage << "\";\n";
+ O.indent(Indent1) << "}\n\n";
}
/// EmitNameMethod - Emit the Name() method for a given Tool class.
void EmitNameMethod (const ToolDescription& D, raw_ostream& O) {
- O << Indent1 << "const char* Name() const {\n"
- << Indent2 << "return \"" << D.Name << "\";\n"
- << Indent1 << "}\n\n";
+ O.indent(Indent1) << "const char* Name() const {\n";
+ O.indent(Indent2) << "return \"" << D.Name << "\";\n";
+ O.indent(Indent1) << "}\n\n";
}
/// EmitIsJoinMethod - Emit the IsJoin() method for a given Tool
/// class.
void EmitIsJoinMethod (const ToolDescription& D, raw_ostream& O) {
- O << Indent1 << "bool IsJoin() const {\n";
+ O.indent(Indent1) << "bool IsJoin() const {\n";
if (D.isJoin())
- O << Indent2 << "return true;\n";
+ O.indent(Indent2) << "return true;\n";
else
- O << Indent2 << "return false;\n";
- O << Indent1 << "}\n\n";
+ O.indent(Indent2) << "return false;\n";
+ O.indent(Indent1) << "}\n\n";
}
/// EmitStaticMemberDefinitions - Emit static member definitions for a
@@ -1636,8 +1762,8 @@ void EmitToolClassDefinition (const ToolDescription& D,
else
O << "Tool";
- O << "{\nprivate:\n"
- << Indent1 << "static const char* InputLanguages_[];\n\n";
+ O << "{\nprivate:\n";
+ O.indent(Indent1) << "static const char* InputLanguages_[];\n\n";
O << "public:\n";
EmitNameMethod(D, O);
@@ -1687,15 +1813,15 @@ void EmitOptionDefinitions (const OptionDescriptions& descs,
O << ", cl::Prefix";
if (val.isRequired()) {
- if (OptionType::IsList(val.Type) && !val.isMultiVal())
+ if (val.isList() && !val.isMultiVal())
O << ", cl::OneOrMore";
else
O << ", cl::Required";
}
- else if (val.isOneOrMore() && OptionType::IsList(val.Type)) {
+ else if (val.isOneOrMore() && val.isList()) {
O << ", cl::OneOrMore";
}
- else if (val.isZeroOrOne() && OptionType::IsList(val.Type)) {
+ else if (val.isZeroOrOne() && val.isList()) {
O << ", cl::ZeroOrOne";
}
@@ -1707,7 +1833,12 @@ void EmitOptionDefinitions (const OptionDescriptions& descs,
}
if (val.MultiVal > 1)
- O << ", cl::multi_val(" << val.MultiVal << ")";
+ O << ", cl::multi_val(" << val.MultiVal << ')';
+
+ if (val.InitVal) {
+ const std::string& str = val.InitVal->getAsString();
+ O << ", cl::init(" << str << ')';
+ }
if (!val.Help.empty())
O << ", cl::desc(\"" << val.Help << "\")";
@@ -1762,9 +1893,9 @@ void EmitPopulateLanguageMap (const RecordKeeper& Records, raw_ostream& O)
const ListInit* Suffixes = LangToSuffixes->getValueAsListInit("suffixes");
for (unsigned i = 0; i < Suffixes->size(); ++i)
- O << Indent1 << "langMap[\""
- << InitPtrToString(Suffixes->getElement(i))
- << "\"] = \"" << Lang << "\";\n";
+ O.indent(Indent1) << "langMap[\""
+ << InitPtrToString(Suffixes->getElement(i))
+ << "\"] = \"" << Lang << "\";\n";
}
}
@@ -1773,21 +1904,22 @@ void EmitPopulateLanguageMap (const RecordKeeper& Records, raw_ostream& O)
/// IncDecWeight - Helper function passed to EmitCaseConstructHandler()
/// by EmitEdgeClass().
-void IncDecWeight (const Init* i, const char* IndentLevel,
+void IncDecWeight (const Init* i, unsigned IndentLevel,
raw_ostream& O) {
const DagInit& d = InitPtrToDag(i);
const std::string& OpName = d.getOperator()->getAsString();
if (OpName == "inc_weight") {
- O << IndentLevel << "ret += ";
+ O.indent(IndentLevel) << "ret += ";
}
else if (OpName == "dec_weight") {
- O << IndentLevel << "ret -= ";
+ O.indent(IndentLevel) << "ret -= ";
}
else if (OpName == "error") {
- O << IndentLevel << "throw std::runtime_error(\"" <<
- (d.getNumArgs() >= 1 ? InitPtrToString(d.getArg(0))
- : "Unknown error!")
+ O.indent(IndentLevel)
+ << "throw std::runtime_error(\"" <<
+ (d.getNumArgs() >= 1 ? InitPtrToString(d.getArg(0))
+ : "Unknown error!")
<< "\");\n";
return;
}
@@ -1810,19 +1942,20 @@ void EmitEdgeClass (unsigned N, const std::string& Target,
// Class constructor.
O << "class Edge" << N << ": public Edge {\n"
- << "public:\n"
- << Indent1 << "Edge" << N << "() : Edge(\"" << Target
- << "\") {}\n\n"
+ << "public:\n";
+ O.indent(Indent1) << "Edge" << N << "() : Edge(\"" << Target
+ << "\") {}\n\n";
// Function Weight().
- << Indent1 << "unsigned Weight(const InputLanguagesSet& InLangs) const {\n"
- << Indent2 << "unsigned ret = 0;\n";
+ O.indent(Indent1)
+ << "unsigned Weight(const InputLanguagesSet& InLangs) const {\n";
+ O.indent(Indent2) << "unsigned ret = 0;\n";
// Handle the 'case' construct.
EmitCaseConstructHandler(Case, Indent2, IncDecWeight, false, OptDescs, O);
- O << Indent2 << "return ret;\n"
- << Indent1 << "};\n\n};\n\n";
+ O.indent(Indent2) << "return ret;\n";
+ O.indent(Indent1) << "};\n\n};\n\n";
}
/// EmitEdgeClasses - Emit Edge* classes that represent graph edges.
@@ -1852,7 +1985,7 @@ void EmitPopulateCompilationGraph (const RecordVector& EdgeVector,
for (ToolDescriptions::const_iterator B = ToolDescs.begin(),
E = ToolDescs.end(); B != E; ++B)
- O << Indent1 << "G.insertNode(new " << (*B)->Name << "());\n";
+ O.indent(Indent1) << "G.insertNode(new " << (*B)->Name << "());\n";
O << '\n';
@@ -1866,7 +1999,7 @@ void EmitPopulateCompilationGraph (const RecordVector& EdgeVector,
const std::string& NodeB = Edge->getValueAsString("b");
DagInit* Weight = Edge->getValueAsDag("weight");
- O << Indent1 << "G.insertEdge(\"" << NodeA << "\", ";
+ O.indent(Indent1) << "G.insertEdge(\"" << NodeA << "\", ";
if (isDagEmpty(Weight))
O << "new SimpleEdge(\"" << NodeB << "\")";
@@ -1955,7 +2088,7 @@ void EmitHookDeclarations(const ToolDescriptions& ToolDescs, raw_ostream& O) {
O << "namespace hooks {\n";
for (StringMap<unsigned>::const_iterator B = HookNames.begin(),
E = HookNames.end(); B != E; ++B) {
- O << Indent1 << "std::string " << B->first() << "(";
+ O.indent(Indent1) << "std::string " << B->first() << "(";
for (unsigned i = 0, j = B->second; i < j; ++i) {
O << "const char* Arg" << i << (i+1 == j ? "" : ", ");
@@ -1968,22 +2101,23 @@ void EmitHookDeclarations(const ToolDescriptions& ToolDescs, raw_ostream& O) {
/// EmitRegisterPlugin - Emit code to register this plugin.
void EmitRegisterPlugin(int Priority, raw_ostream& O) {
- O << "struct Plugin : public llvmc::BasePlugin {\n\n"
- << Indent1 << "int Priority() const { return " << Priority << "; }\n\n"
- << Indent1 << "void PopulateLanguageMap(LanguageMap& langMap) const\n"
- << Indent1 << "{ PopulateLanguageMapLocal(langMap); }\n\n"
- << Indent1
- << "void PopulateCompilationGraph(CompilationGraph& graph) const\n"
- << Indent1 << "{ PopulateCompilationGraphLocal(graph); }\n"
- << "};\n\n"
-
- << "static llvmc::RegisterPlugin<Plugin> RP;\n\n";
+ O << "struct Plugin : public llvmc::BasePlugin {\n\n";
+ O.indent(Indent1) << "int Priority() const { return "
+ << Priority << "; }\n\n";
+ O.indent(Indent1) << "void PopulateLanguageMap(LanguageMap& langMap) const\n";
+ O.indent(Indent1) << "{ PopulateLanguageMapLocal(langMap); }\n\n";
+ O.indent(Indent1)
+ << "void PopulateCompilationGraph(CompilationGraph& graph) const\n";
+ O.indent(Indent1) << "{ PopulateCompilationGraphLocal(graph); }\n"
+ << "};\n\n"
+ << "static llvmc::RegisterPlugin<Plugin> RP;\n\n";
}
/// EmitIncludes - Emit necessary #include directives and some
/// additional declarations.
void EmitIncludes(raw_ostream& O) {
- O << "#include \"llvm/CompilerDriver/CompilationGraph.h\"\n"
+ O << "#include \"llvm/CompilerDriver/BuiltinOptions.h\"\n"
+ << "#include \"llvm/CompilerDriver/CompilationGraph.h\"\n"
<< "#include \"llvm/CompilerDriver/ForceLinkageMacros.h\"\n"
<< "#include \"llvm/CompilerDriver/Plugin.h\"\n"
<< "#include \"llvm/CompilerDriver/Tool.h\"\n\n"
diff --git a/utils/TableGen/Record.cpp b/utils/TableGen/Record.cpp
index 8f31624..a551166 100644
--- a/utils/TableGen/Record.cpp
+++ b/utils/TableGen/Record.cpp
@@ -1319,6 +1319,8 @@ void RecordVal::print(raw_ostream &OS, bool PrintSem) const {
if (PrintSem) OS << ";\n";
}
+unsigned Record::LastID = 0;
+
void Record::setName(const std::string &Name) {
if (Records.getDef(getName()) == this) {
Records.removeDef(getName());
@@ -1382,11 +1384,11 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, const Record &R) {
/// getValueInit - Return the initializer for a value with the specified name,
/// or throw an exception if the field does not exist.
///
-Init *Record::getValueInit(const std::string &FieldName) const {
+Init *Record::getValueInit(StringRef FieldName) const {
const RecordVal *R = getValue(FieldName);
if (R == 0 || R->getValue() == 0)
throw "Record `" + getName() + "' does not have a field named `" +
- FieldName + "'!\n";
+ FieldName.str() + "'!\n";
return R->getValue();
}
@@ -1395,15 +1397,15 @@ Init *Record::getValueInit(const std::string &FieldName) const {
/// value as a string, throwing an exception if the field does not exist or if
/// the value is not a string.
///
-std::string Record::getValueAsString(const std::string &FieldName) const {
+std::string Record::getValueAsString(StringRef FieldName) const {
const RecordVal *R = getValue(FieldName);
if (R == 0 || R->getValue() == 0)
throw "Record `" + getName() + "' does not have a field named `" +
- FieldName + "'!\n";
+ FieldName.str() + "'!\n";
if (const StringInit *SI = dynamic_cast<const StringInit*>(R->getValue()))
return SI->getValue();
- throw "Record `" + getName() + "', field `" + FieldName +
+ throw "Record `" + getName() + "', field `" + FieldName.str() +
"' does not have a string initializer!";
}
@@ -1411,15 +1413,15 @@ std::string Record::getValueAsString(const std::string &FieldName) const {
/// its value as a BitsInit, throwing an exception if the field does not exist
/// or if the value is not the right type.
///
-BitsInit *Record::getValueAsBitsInit(const std::string &FieldName) const {
+BitsInit *Record::getValueAsBitsInit(StringRef FieldName) const {
const RecordVal *R = getValue(FieldName);
if (R == 0 || R->getValue() == 0)
throw "Record `" + getName() + "' does not have a field named `" +
- FieldName + "'!\n";
+ FieldName.str() + "'!\n";
if (BitsInit *BI = dynamic_cast<BitsInit*>(R->getValue()))
return BI;
- throw "Record `" + getName() + "', field `" + FieldName +
+ throw "Record `" + getName() + "', field `" + FieldName.str() +
"' does not have a BitsInit initializer!";
}
@@ -1427,15 +1429,15 @@ BitsInit *Record::getValueAsBitsInit(const std::string &FieldName) const {
/// its value as a ListInit, throwing an exception if the field does not exist
/// or if the value is not the right type.
///
-ListInit *Record::getValueAsListInit(const std::string &FieldName) const {
+ListInit *Record::getValueAsListInit(StringRef FieldName) const {
const RecordVal *R = getValue(FieldName);
if (R == 0 || R->getValue() == 0)
throw "Record `" + getName() + "' does not have a field named `" +
- FieldName + "'!\n";
+ FieldName.str() + "'!\n";
if (ListInit *LI = dynamic_cast<ListInit*>(R->getValue()))
return LI;
- throw "Record `" + getName() + "', field `" + FieldName +
+ throw "Record `" + getName() + "', field `" + FieldName.str() +
"' does not have a list initializer!";
}
@@ -1444,14 +1446,14 @@ ListInit *Record::getValueAsListInit(const std::string &FieldName) const {
/// not exist or if the value is not the right type.
///
std::vector<Record*>
-Record::getValueAsListOfDefs(const std::string &FieldName) const {
+Record::getValueAsListOfDefs(StringRef FieldName) const {
ListInit *List = getValueAsListInit(FieldName);
std::vector<Record*> Defs;
for (unsigned i = 0; i < List->getSize(); i++) {
if (DefInit *DI = dynamic_cast<DefInit*>(List->getElement(i))) {
Defs.push_back(DI->getDef());
} else {
- throw "Record `" + getName() + "', field `" + FieldName +
+ throw "Record `" + getName() + "', field `" + FieldName.str() +
"' list is not entirely DefInit!";
}
}
@@ -1462,15 +1464,15 @@ Record::getValueAsListOfDefs(const std::string &FieldName) const {
/// value as an int64_t, throwing an exception if the field does not exist or if
/// the value is not the right type.
///
-int64_t Record::getValueAsInt(const std::string &FieldName) const {
+int64_t Record::getValueAsInt(StringRef FieldName) const {
const RecordVal *R = getValue(FieldName);
if (R == 0 || R->getValue() == 0)
throw "Record `" + getName() + "' does not have a field named `" +
- FieldName + "'!\n";
+ FieldName.str() + "'!\n";
if (IntInit *II = dynamic_cast<IntInit*>(R->getValue()))
return II->getValue();
- throw "Record `" + getName() + "', field `" + FieldName +
+ throw "Record `" + getName() + "', field `" + FieldName.str() +
"' does not have an int initializer!";
}
@@ -1479,14 +1481,14 @@ int64_t Record::getValueAsInt(const std::string &FieldName) const {
/// not exist or if the value is not the right type.
///
std::vector<int64_t>
-Record::getValueAsListOfInts(const std::string &FieldName) const {
+Record::getValueAsListOfInts(StringRef FieldName) const {
ListInit *List = getValueAsListInit(FieldName);
std::vector<int64_t> Ints;
for (unsigned i = 0; i < List->getSize(); i++) {
if (IntInit *II = dynamic_cast<IntInit*>(List->getElement(i))) {
Ints.push_back(II->getValue());
} else {
- throw "Record `" + getName() + "', field `" + FieldName +
+ throw "Record `" + getName() + "', field `" + FieldName.str() +
"' does not have a list of ints initializer!";
}
}
@@ -1497,15 +1499,15 @@ Record::getValueAsListOfInts(const std::string &FieldName) const {
/// value as a Record, throwing an exception if the field does not exist or if
/// the value is not the right type.
///
-Record *Record::getValueAsDef(const std::string &FieldName) const {
+Record *Record::getValueAsDef(StringRef FieldName) const {
const RecordVal *R = getValue(FieldName);
if (R == 0 || R->getValue() == 0)
throw "Record `" + getName() + "' does not have a field named `" +
- FieldName + "'!\n";
+ FieldName.str() + "'!\n";
if (DefInit *DI = dynamic_cast<DefInit*>(R->getValue()))
return DI->getDef();
- throw "Record `" + getName() + "', field `" + FieldName +
+ throw "Record `" + getName() + "', field `" + FieldName.str() +
"' does not have a def initializer!";
}
@@ -1513,15 +1515,15 @@ Record *Record::getValueAsDef(const std::string &FieldName) const {
/// value as a bit, throwing an exception if the field does not exist or if
/// the value is not the right type.
///
-bool Record::getValueAsBit(const std::string &FieldName) const {
+bool Record::getValueAsBit(StringRef FieldName) const {
const RecordVal *R = getValue(FieldName);
if (R == 0 || R->getValue() == 0)
throw "Record `" + getName() + "' does not have a field named `" +
- FieldName + "'!\n";
+ FieldName.str() + "'!\n";
if (BitInit *BI = dynamic_cast<BitInit*>(R->getValue()))
return BI->getValue();
- throw "Record `" + getName() + "', field `" + FieldName +
+ throw "Record `" + getName() + "', field `" + FieldName.str() +
"' does not have a bit initializer!";
}
@@ -1529,27 +1531,27 @@ bool Record::getValueAsBit(const std::string &FieldName) const {
/// value as an Dag, throwing an exception if the field does not exist or if
/// the value is not the right type.
///
-DagInit *Record::getValueAsDag(const std::string &FieldName) const {
+DagInit *Record::getValueAsDag(StringRef FieldName) const {
const RecordVal *R = getValue(FieldName);
if (R == 0 || R->getValue() == 0)
throw "Record `" + getName() + "' does not have a field named `" +
- FieldName + "'!\n";
+ FieldName.str() + "'!\n";
if (DagInit *DI = dynamic_cast<DagInit*>(R->getValue()))
return DI;
- throw "Record `" + getName() + "', field `" + FieldName +
+ throw "Record `" + getName() + "', field `" + FieldName.str() +
"' does not have a dag initializer!";
}
-std::string Record::getValueAsCode(const std::string &FieldName) const {
+std::string Record::getValueAsCode(StringRef FieldName) const {
const RecordVal *R = getValue(FieldName);
if (R == 0 || R->getValue() == 0)
throw "Record `" + getName() + "' does not have a field named `" +
- FieldName + "'!\n";
+ FieldName.str() + "'!\n";
if (const CodeInit *CI = dynamic_cast<const CodeInit*>(R->getValue()))
return CI->getValue();
- throw "Record `" + getName() + "', field `" + FieldName +
+ throw "Record `" + getName() + "', field `" + FieldName.str() +
"' does not have a code initializer!";
}
diff --git a/utils/TableGen/Record.h b/utils/TableGen/Record.h
index 11db910..1b33743 100644
--- a/utils/TableGen/Record.h
+++ b/utils/TableGen/Record.h
@@ -782,7 +782,7 @@ public:
// Clone - Clone this operator, replacing arguments with the new list
virtual OpInit *clone(std::vector<Init *> &Operands) = 0;
- virtual int getNumOperands(void) const = 0;
+ virtual int getNumOperands() const = 0;
virtual Init *getOperand(int i) = 0;
// Fold - If possible, fold this to a simpler init. Return this if not
@@ -820,7 +820,7 @@ public:
return new UnOpInit(getOpcode(), *Operands.begin(), getType());
}
- int getNumOperands(void) const { return 1; }
+ int getNumOperands() const { return 1; }
Init *getOperand(int i) {
assert(i == 0 && "Invalid operand id for unary operator");
return getOperand();
@@ -864,7 +864,7 @@ public:
return new BinOpInit(getOpcode(), Operands[0], Operands[1], getType());
}
- int getNumOperands(void) const { return 2; }
+ int getNumOperands() const { return 2; }
Init *getOperand(int i) {
assert((i == 0 || i == 1) && "Invalid operand id for binary operator");
if (i == 0) {
@@ -909,7 +909,7 @@ public:
getType());
}
- int getNumOperands(void) const { return 3; }
+ int getNumOperands() const { return 3; }
Init *getOperand(int i) {
assert((i == 0 || i == 1 || i == 2) &&
"Invalid operand id for ternary operator");
@@ -1220,6 +1220,10 @@ inline raw_ostream &operator<<(raw_ostream &OS, const RecordVal &RV) {
}
class Record {
+ static unsigned LastID;
+
+ // Unique record ID.
+ unsigned ID;
std::string Name;
SMLoc Loc;
std::vector<std::string> TemplateArgs;
@@ -1227,9 +1231,12 @@ class Record {
std::vector<Record*> SuperClasses;
public:
- explicit Record(const std::string &N, SMLoc loc) : Name(N), Loc(loc) {}
+ explicit Record(const std::string &N, SMLoc loc) :
+ ID(LastID++), Name(N), Loc(loc) {}
~Record() {}
+ unsigned getID() const { return ID; }
+
const std::string &getName() const { return Name; }
void setName(const std::string &Name); // Also updates RecordKeeper.
@@ -1241,24 +1248,24 @@ public:
const std::vector<RecordVal> &getValues() const { return Values; }
const std::vector<Record*> &getSuperClasses() const { return SuperClasses; }
- bool isTemplateArg(const std::string &Name) const {
+ bool isTemplateArg(StringRef Name) const {
for (unsigned i = 0, e = TemplateArgs.size(); i != e; ++i)
if (TemplateArgs[i] == Name) return true;
return false;
}
- const RecordVal *getValue(const std::string &Name) const {
+ const RecordVal *getValue(StringRef Name) const {
for (unsigned i = 0, e = Values.size(); i != e; ++i)
if (Values[i].getName() == Name) return &Values[i];
return 0;
}
- RecordVal *getValue(const std::string &Name) {
+ RecordVal *getValue(StringRef Name) {
for (unsigned i = 0, e = Values.size(); i != e; ++i)
if (Values[i].getName() == Name) return &Values[i];
return 0;
}
- void addTemplateArg(const std::string &Name) {
+ void addTemplateArg(StringRef Name) {
assert(!isTemplateArg(Name) && "Template arg already defined!");
TemplateArgs.push_back(Name);
}
@@ -1268,7 +1275,7 @@ public:
Values.push_back(RV);
}
- void removeValue(const std::string &Name) {
+ void removeValue(StringRef Name) {
assert(getValue(Name) && "Cannot remove an entry that does not exist!");
for (unsigned i = 0, e = Values.size(); i != e; ++i)
if (Values[i].getName() == Name) {
@@ -1285,7 +1292,7 @@ public:
return false;
}
- bool isSubClassOf(const std::string &Name) const {
+ bool isSubClassOf(StringRef Name) const {
for (unsigned i = 0, e = SuperClasses.size(); i != e; ++i)
if (SuperClasses[i]->getName() == Name)
return true;
@@ -1316,67 +1323,67 @@ public:
/// getValueInit - Return the initializer for a value with the specified name,
/// or throw an exception if the field does not exist.
///
- Init *getValueInit(const std::string &FieldName) const;
+ Init *getValueInit(StringRef FieldName) const;
/// getValueAsString - This method looks up the specified field and returns
/// its value as a string, throwing an exception if the field does not exist
/// or if the value is not a string.
///
- std::string getValueAsString(const std::string &FieldName) const;
+ std::string getValueAsString(StringRef FieldName) const;
/// getValueAsBitsInit - This method looks up the specified field and returns
/// its value as a BitsInit, throwing an exception if the field does not exist
/// or if the value is not the right type.
///
- BitsInit *getValueAsBitsInit(const std::string &FieldName) const;
+ BitsInit *getValueAsBitsInit(StringRef FieldName) const;
/// getValueAsListInit - This method looks up the specified field and returns
/// its value as a ListInit, throwing an exception if the field does not exist
/// or if the value is not the right type.
///
- ListInit *getValueAsListInit(const std::string &FieldName) const;
+ ListInit *getValueAsListInit(StringRef FieldName) const;
/// getValueAsListOfDefs - This method looks up the specified field and
/// returns its value as a vector of records, throwing an exception if the
/// field does not exist or if the value is not the right type.
///
- std::vector<Record*> getValueAsListOfDefs(const std::string &FieldName) const;
+ std::vector<Record*> getValueAsListOfDefs(StringRef FieldName) const;
/// getValueAsListOfInts - This method looks up the specified field and returns
/// its value as a vector of integers, throwing an exception if the field does
/// not exist or if the value is not the right type.
///
- std::vector<int64_t> getValueAsListOfInts(const std::string &FieldName) const;
+ std::vector<int64_t> getValueAsListOfInts(StringRef FieldName) const;
/// getValueAsDef - This method looks up the specified field and returns its
/// value as a Record, throwing an exception if the field does not exist or if
/// the value is not the right type.
///
- Record *getValueAsDef(const std::string &FieldName) const;
+ Record *getValueAsDef(StringRef FieldName) const;
/// getValueAsBit - This method looks up the specified field and returns its
/// value as a bit, throwing an exception if the field does not exist or if
/// the value is not the right type.
///
- bool getValueAsBit(const std::string &FieldName) const;
+ bool getValueAsBit(StringRef FieldName) const;
/// getValueAsInt - This method looks up the specified field and returns its
/// value as an int64_t, throwing an exception if the field does not exist or
/// if the value is not the right type.
///
- int64_t getValueAsInt(const std::string &FieldName) const;
+ int64_t getValueAsInt(StringRef FieldName) const;
/// getValueAsDag - This method looks up the specified field and returns its
/// value as an Dag, throwing an exception if the field does not exist or if
/// the value is not the right type.
///
- DagInit *getValueAsDag(const std::string &FieldName) const;
+ DagInit *getValueAsDag(StringRef FieldName) const;
/// getValueAsCode - This method looks up the specified field and returns
/// its value as the string data in a CodeInit, throwing an exception if the
/// field does not exist or if the value is not a code object.
///
- std::string getValueAsCode(const std::string &FieldName) const;
+ std::string getValueAsCode(StringRef FieldName) const;
};
raw_ostream &operator<<(raw_ostream &OS, const Record &R);
diff --git a/utils/TableGen/RegisterInfoEmitter.cpp b/utils/TableGen/RegisterInfoEmitter.cpp
index 3297e93..3c7b44a 100644
--- a/utils/TableGen/RegisterInfoEmitter.cpp
+++ b/utils/TableGen/RegisterInfoEmitter.cpp
@@ -222,7 +222,7 @@ void RegisterInfoEmitter::run(raw_ostream &OS) {
// Emit the register list now.
OS << " // " << Name
<< " Register Class Value Types...\n"
- << " static const MVT " << Name
+ << " static const EVT " << Name
<< "[] = {\n ";
for (unsigned i = 0, e = RC.VTs.size(); i != e; ++i)
OS << getEnumName(RC.VTs[i]) << ", ";
@@ -252,7 +252,7 @@ void RegisterInfoEmitter::run(raw_ostream &OS) {
OS << " // " << Name
<< " Sub-register Classes...\n"
<< " static const TargetRegisterClass* const "
- << Name << "SubRegClasses [] = {\n ";
+ << Name << "SubRegClasses[] = {\n ";
bool Empty = true;
@@ -298,7 +298,7 @@ void RegisterInfoEmitter::run(raw_ostream &OS) {
OS << " // " << Name
<< " Super-register Classes...\n"
<< " static const TargetRegisterClass* const "
- << Name << "SuperRegClasses [] = {\n ";
+ << Name << "SuperRegClasses[] = {\n ";
bool Empty = true;
std::map<unsigned, std::set<unsigned> >::iterator I =
@@ -334,7 +334,7 @@ void RegisterInfoEmitter::run(raw_ostream &OS) {
OS << " // " << Name
<< " Register Class sub-classes...\n"
<< " static const TargetRegisterClass* const "
- << Name << "Subclasses [] = {\n ";
+ << Name << "Subclasses[] = {\n ";
bool Empty = true;
for (unsigned rc2 = 0, e2 = RegisterClasses.size(); rc2 != e2; ++rc2) {
@@ -382,7 +382,7 @@ void RegisterInfoEmitter::run(raw_ostream &OS) {
OS << " // " << Name
<< " Register Class super-classes...\n"
<< " static const TargetRegisterClass* const "
- << Name << "Superclasses [] = {\n ";
+ << Name << "Superclasses[] = {\n ";
bool Empty = true;
std::map<unsigned, std::set<unsigned> >::iterator I =
@@ -767,7 +767,7 @@ void RegisterInfoEmitter::run(raw_ostream &OS) {
}
OS<<"\n const TargetRegisterDesc RegisterDescriptors[] = { // Descriptors\n";
- OS << " { \"NOREG\",\t\"NOREG\",\t0,\t0,\t0 },\n";
+ OS << " { \"NOREG\",\t0,\t0,\t0 },\n";
// Now that register alias and sub-registers sets have been emitted, emit the
// register descriptors now.
@@ -775,11 +775,6 @@ void RegisterInfoEmitter::run(raw_ostream &OS) {
for (unsigned i = 0, e = Registers.size(); i != e; ++i) {
const CodeGenRegister &Reg = Registers[i];
OS << " { \"";
- if (!Reg.TheDef->getValueAsString("AsmName").empty())
- OS << Reg.TheDef->getValueAsString("AsmName");
- else
- OS << Reg.getName();
- OS << "\",\t\"";
OS << Reg.getName() << "\",\t";
if (RegisterAliases.count(Reg.TheDef))
OS << Reg.getName() << "_AliasSet,\t";
diff --git a/utils/TableGen/StringToOffsetTable.h b/utils/TableGen/StringToOffsetTable.h
new file mode 100644
index 0000000..d9d7cf4
--- /dev/null
+++ b/utils/TableGen/StringToOffsetTable.h
@@ -0,0 +1,76 @@
+//===- 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/StringMap.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/ADT/StringExtras.h"
+
+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) {
+ unsigned &Entry = StringOffset[Str];
+ if (Entry == 0) {
+ // Add the string to the aggregate if this is the first time found.
+ Entry = AggregateString.size();
+ AggregateString.append(Str.begin(), Str.end());
+ AggregateString += '\0';
+ }
+
+ return Entry;
+ }
+
+ void EmitString(raw_ostream &O) {
+ O << " \"";
+ unsigned CharsPrinted = 0;
+ EscapeString(AggregateString);
+ 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 919ac66..c8cf234 100644
--- a/utils/TableGen/SubtargetEmitter.cpp
+++ b/utils/TableGen/SubtargetEmitter.cpp
@@ -199,12 +199,13 @@ unsigned SubtargetEmitter::CollectAllItinClasses(raw_ostream &OS,
}
//
-// FormItineraryString - Compose a string containing the data initialization
-// for the specified itinerary. N is the number of stages.
+// FormItineraryStageString - Compose a string containing the stage
+// data initialization for the specified itinerary. N is the number
+// of stages.
//
-void SubtargetEmitter::FormItineraryString(Record *ItinData,
- std::string &ItinString,
- unsigned &NStages) {
+void SubtargetEmitter::FormItineraryStageString(Record *ItinData,
+ std::string &ItinString,
+ unsigned &NStages) {
// Get states list
const std::vector<Record*> &StageList =
ItinData->getValueAsListOfDefs("Stages");
@@ -215,7 +216,7 @@ void SubtargetEmitter::FormItineraryString(Record *ItinData,
// Next stage
const Record *Stage = StageList[i];
- // Form string as ,{ cycles, u1 | u2 | ... | un }
+ // Form string as ,{ cycles, u1 | u2 | ... | un, timeinc }
int Cycles = Stage->getValueAsInt("Cycles");
ItinString += " { " + itostr(Cycles) + ", ";
@@ -229,6 +230,9 @@ void SubtargetEmitter::FormItineraryString(Record *ItinData,
if (++j < M) ItinString += " | ";
}
+ int TimeInc = Stage->getValueAsInt("TimeInc");
+ ItinString += ", " + itostr(TimeInc);
+
// Close off stage
ItinString += " }";
if (++i < N) ItinString += ", ";
@@ -236,10 +240,32 @@ void SubtargetEmitter::FormItineraryString(Record *ItinData,
}
//
-// EmitStageData - Generate unique itinerary stages. Record itineraries for
-// processors.
+// FormItineraryOperandCycleString - Compose a string containing the
+// operand cycle initialization for the specified itinerary. N is the
+// number of operands that has cycles specified.
+//
+void SubtargetEmitter::FormItineraryOperandCycleString(Record *ItinData,
+ std::string &ItinString, unsigned &NOperandCycles) {
+ // Get operand cycle list
+ const std::vector<int64_t> &OperandCycleList =
+ ItinData->getValueAsListOfInts("OperandCycles");
+
+ // For each operand cycle
+ unsigned N = NOperandCycles = OperandCycleList.size();
+ for (unsigned i = 0; i < N;) {
+ // Next operand cycle
+ const int OCycle = OperandCycleList[i];
+
+ ItinString += " " + itostr(OCycle);
+ if (++i < N) ItinString += ", ";
+ }
+}
+
+//
+// EmitStageAndOperandCycleData - Generate unique itinerary stages and
+// operand cycle tables. Record itineraries for processors.
//
-void SubtargetEmitter::EmitStageData(raw_ostream &OS,
+void SubtargetEmitter::EmitStageAndOperandCycleData(raw_ostream &OS,
unsigned NItinClasses,
std::map<std::string, unsigned> &ItinClassesMap,
std::vector<std::vector<InstrItinerary> > &ProcList) {
@@ -251,12 +277,16 @@ void SubtargetEmitter::EmitStageData(raw_ostream &OS,
if (ProcItinList.size() < 2) return;
// Begin stages table
- OS << "static const llvm::InstrStage Stages[] = {\n"
- " { 0, 0 }, // No itinerary\n";
+ std::string StageTable = "static const llvm::InstrStage Stages[] = {\n";
+ StageTable += " { 0, 0, 0 }, // No itinerary\n";
- unsigned StageCount = 1;
- unsigned ItinEnum = 1;
- std::map<std::string, unsigned> ItinMap;
+ // Begin operand cycle table
+ std::string OperandCycleTable = "static const unsigned OperandCycles[] = {\n";
+ OperandCycleTable += " 0, // No itinerary\n";
+
+ unsigned StageCount = 1, OperandCycleCount = 1;
+ unsigned ItinStageEnum = 1, ItinOperandCycleEnum = 1;
+ std::map<std::string, unsigned> ItinStageMap, ItinOperandCycleMap;
for (unsigned i = 0, N = ProcItinList.size(); i < N; i++) {
// Next record
Record *Proc = ProcItinList[i];
@@ -280,29 +310,53 @@ void SubtargetEmitter::EmitStageData(raw_ostream &OS,
Record *ItinData = ItinDataList[j];
// Get string and stage count
- std::string ItinString;
+ std::string ItinStageString;
unsigned NStages;
- FormItineraryString(ItinData, ItinString, NStages);
-
- // Check to see if it already exists
- unsigned Find = ItinMap[ItinString];
+ FormItineraryStageString(ItinData, ItinStageString, NStages);
+
+ // Get string and operand cycle count
+ std::string ItinOperandCycleString;
+ unsigned NOperandCycles;
+ FormItineraryOperandCycleString(ItinData, ItinOperandCycleString,
+ NOperandCycles);
+
+ // Check to see if stage already exists and create if it doesn't
+ unsigned FindStage = 0;
+ if (NStages > 0) {
+ FindStage = ItinStageMap[ItinStageString];
+ if (FindStage == 0) {
+ // Emit as { cycles, u1 | u2 | ... | un, timeinc }, // index
+ StageTable += ItinStageString + ", // " + itostr(ItinStageEnum) + "\n";
+ // Record Itin class number.
+ ItinStageMap[ItinStageString] = FindStage = StageCount;
+ StageCount += NStages;
+ ItinStageEnum++;
+ }
+ }
- // If new itinerary
- if (Find == 0) {
- // Emit as { cycles, u1 | u2 | ... | un }, // index
- OS << ItinString << ", // " << ItinEnum << "\n";
- // Record Itin class number.
- ItinMap[ItinString] = Find = StageCount;
- StageCount += NStages;
- ItinEnum++;
+ // Check to see if operand cycle already exists and create if it doesn't
+ unsigned FindOperandCycle = 0;
+ if (NOperandCycles > 0) {
+ FindOperandCycle = ItinOperandCycleMap[ItinOperandCycleString];
+ if (FindOperandCycle == 0) {
+ // Emit as cycle, // index
+ OperandCycleTable += ItinOperandCycleString + ", // " +
+ itostr(ItinOperandCycleEnum) + "\n";
+ // Record Itin class number.
+ ItinOperandCycleMap[ItinOperandCycleString] =
+ FindOperandCycle = OperandCycleCount;
+ OperandCycleCount += NOperandCycles;
+ ItinOperandCycleEnum++;
+ }
}
// Set up itinerary as location and location + stage count
- InstrItinerary Intinerary = { Find, Find + NStages };
+ InstrItinerary Intinerary = { FindStage, FindStage + NStages,
+ FindOperandCycle, FindOperandCycle + NOperandCycles};
// Locate where to inject into processor itinerary table
const std::string &Name = ItinData->getValueAsDef("TheClass")->getName();
- Find = ItinClassesMap[Name];
+ unsigned Find = ItinClassesMap[Name];
// Inject - empty slots will be 0, 0
ItinList[Find] = Intinerary;
@@ -313,13 +367,21 @@ void SubtargetEmitter::EmitStageData(raw_ostream &OS,
}
// Closing stage
- OS << " { 0, 0 } // End itinerary\n";
- // End stages table
- OS << "};\n";
+ StageTable += " { 0, 0, 0 } // End itinerary\n";
+ StageTable += "};\n";
+
+ // Closing operand cycles
+ OperandCycleTable += " 0 // End itinerary\n";
+ OperandCycleTable += "};\n";
+
+ // Emit tables.
+ OS << StageTable;
+ OS << OperandCycleTable;
- // Emit size of table
+ // Emit size of tables
OS<<"\nenum {\n";
- OS<<" StagesSize = sizeof(Stages)/sizeof(llvm::InstrStage)\n";
+ OS<<" StagesSize = sizeof(Stages)/sizeof(llvm::InstrStage),\n";
+ OS<<" OperandCyclesSize = sizeof(OperandCycles)/sizeof(unsigned)\n";
OS<<"};\n";
}
@@ -351,23 +413,25 @@ void SubtargetEmitter::EmitProcessorData(raw_ostream &OS,
// For each itinerary class
std::vector<InstrItinerary> &ItinList = *ProcListIter++;
- for (unsigned j = 0, M = ItinList.size(); j < M;) {
+ for (unsigned j = 0, M = ItinList.size(); j < M; ++j) {
InstrItinerary &Intinerary = ItinList[j];
- // Emit in the form of { first, last } // index
- if (Intinerary.First == 0) {
- OS << " { 0, 0 }";
+ // Emit in the form of
+ // { firstStage, lastStage, firstCycle, lastCycle } // index
+ if (Intinerary.FirstStage == 0) {
+ OS << " { 0, 0, 0, 0 }";
} else {
- OS << " { " << Intinerary.First << ", " << Intinerary.Last << " }";
+ OS << " { " << Intinerary.FirstStage << ", " <<
+ Intinerary.LastStage << ", " <<
+ Intinerary.FirstOperandCycle << ", " <<
+ Intinerary.LastOperandCycle << " }";
}
- // If more in list add comma
- if (++j < M) OS << ",";
-
- OS << " // " << (j - 1) << "\n";
+ OS << ", // " << j << "\n";
}
// End processor itinerary table
+ OS << " { ~0U, ~0U, ~0U, ~0U } // end marker\n";
OS << "};\n";
}
}
@@ -432,7 +496,7 @@ void SubtargetEmitter::EmitData(raw_ostream &OS) {
if (HasItineraries) {
// Emit the stage data
- EmitStageData(OS, NItinClasses, ItinClassesMap, ProcList);
+ EmitStageAndOperandCycleData(OS, NItinClasses, ItinClassesMap, ProcList);
// Emit the processor itinerary data
EmitProcessorData(OS, ProcList);
// Emit the processor lookup data
@@ -479,7 +543,7 @@ void SubtargetEmitter::ParseFeaturesFunction(raw_ostream &OS) {
OS << "\n"
<< " InstrItinerary *Itinerary = (InstrItinerary *)"
<< "Features.getInfo(ProcItinKV, ProcItinKVSize);\n"
- << " InstrItins = InstrItineraryData(Stages, Itinerary);\n";
+ << " InstrItins = InstrItineraryData(Stages, OperandCycles, Itinerary);\n";
}
OS << " return Features.getCPU();\n"
diff --git a/utils/TableGen/SubtargetEmitter.h b/utils/TableGen/SubtargetEmitter.h
index f44278c..1d7088f 100644
--- a/utils/TableGen/SubtargetEmitter.h
+++ b/utils/TableGen/SubtargetEmitter.h
@@ -34,9 +34,11 @@ class SubtargetEmitter : public TableGenBackend {
void CPUKeyValues(raw_ostream &OS);
unsigned CollectAllItinClasses(raw_ostream &OS,
std::map<std::string, unsigned> &ItinClassesMap);
- void FormItineraryString(Record *ItinData, std::string &ItinString,
- unsigned &NStages);
- void EmitStageData(raw_ostream &OS, unsigned NItinClasses,
+ void FormItineraryStageString(Record *ItinData, std::string &ItinString,
+ unsigned &NStages);
+ void FormItineraryOperandCycleString(Record *ItinData, std::string &ItinString,
+ unsigned &NOperandCycles);
+ void EmitStageAndOperandCycleData(raw_ostream &OS, unsigned NItinClasses,
std::map<std::string, unsigned> &ItinClassesMap,
std::vector<std::vector<InstrItinerary> > &ProcList);
void EmitProcessorData(raw_ostream &OS,
diff --git a/utils/TableGen/TGParser.cpp b/utils/TableGen/TGParser.cpp
index ba480e6..7122265 100644
--- a/utils/TableGen/TGParser.cpp
+++ b/utils/TableGen/TGParser.cpp
@@ -974,7 +974,7 @@ Init *TGParser::ParseOperation(Record *CurRec) {
///
/// OperatorType ::= '<' Type '>'
///
-RecTy *TGParser::ParseOperatorType(void) {
+RecTy *TGParser::ParseOperatorType() {
RecTy *Type = 0;
if (Lex.getCode() != tgtok::less) {
diff --git a/utils/TableGen/TGValueTypes.cpp b/utils/TableGen/TGValueTypes.cpp
index e4edca6..122d085 100644
--- a/utils/TableGen/TGValueTypes.cpp
+++ b/utils/TableGen/TGValueTypes.cpp
@@ -7,8 +7,8 @@
//
//===----------------------------------------------------------------------===//
//
-// The MVT type is used by tablegen as well as in LLVM. In order to handle
-// extended types, the MVT type uses support functions that call into
+// The EVT type is used by tablegen as well as in LLVM. In order to handle
+// extended types, the EVT type uses support functions that call into
// LLVM's type system code. These aren't accessible in tablegen, so this
// file provides simple replacements.
//
@@ -43,15 +43,15 @@ public:
};
class ExtendedVectorType : public Type {
- MVT ElementType;
+ EVT ElementType;
unsigned NumElements;
public:
- ExtendedVectorType(MVT elty, unsigned num)
+ ExtendedVectorType(EVT elty, unsigned num)
: ElementType(elty), NumElements(num) {}
unsigned getSizeInBits() const {
return getNumElements() * getElementType().getSizeInBits();
}
- MVT getElementType() const {
+ EVT getElementType() const {
return ElementType;
}
unsigned getNumElements() const {
@@ -64,62 +64,43 @@ static std::map<unsigned, const Type *>
static std::map<std::pair<uintptr_t, uintptr_t>, const Type *>
ExtendedVectorTypeMap;
-MVT MVT::getExtendedIntegerVT(unsigned BitWidth) {
- const Type *&ET = ExtendedIntegerTypeMap[BitWidth];
- if (!ET) ET = new ExtendedIntegerType(BitWidth);
- MVT VT;
- VT.LLVMTy = ET;
- assert(VT.isExtended() && "Type is not extended!");
- return VT;
-}
-
-MVT MVT::getExtendedVectorVT(MVT VT, unsigned NumElements) {
- const Type *&ET = ExtendedVectorTypeMap[std::make_pair(VT.getRawBits(),
- NumElements)];
- if (!ET) ET = new ExtendedVectorType(VT, NumElements);
- MVT ResultVT;
- ResultVT.LLVMTy = ET;
- assert(ResultVT.isExtended() && "Type is not extended!");
- return ResultVT;
-}
-
-bool MVT::isExtendedFloatingPoint() const {
+bool EVT::isExtendedFloatingPoint() const {
assert(isExtended() && "Type is not extended!");
// Extended floating-point types are not supported yet.
return false;
}
-bool MVT::isExtendedInteger() const {
+bool EVT::isExtendedInteger() const {
assert(isExtended() && "Type is not extended!");
return dynamic_cast<const ExtendedIntegerType *>(LLVMTy) != 0;
}
-bool MVT::isExtendedVector() const {
+bool EVT::isExtendedVector() const {
assert(isExtended() && "Type is not extended!");
return dynamic_cast<const ExtendedVectorType *>(LLVMTy) != 0;
}
-bool MVT::isExtended64BitVector() const {
+bool EVT::isExtended64BitVector() const {
assert(isExtended() && "Type is not extended!");
return isExtendedVector() && getSizeInBits() == 64;
}
-bool MVT::isExtended128BitVector() const {
+bool EVT::isExtended128BitVector() const {
assert(isExtended() && "Type is not extended!");
return isExtendedVector() && getSizeInBits() == 128;
}
-MVT MVT::getExtendedVectorElementType() const {
+EVT EVT::getExtendedVectorElementType() const {
assert(isExtendedVector() && "Type is not an extended vector!");
return static_cast<const ExtendedVectorType *>(LLVMTy)->getElementType();
}
-unsigned MVT::getExtendedVectorNumElements() const {
+unsigned EVT::getExtendedVectorNumElements() const {
assert(isExtendedVector() && "Type is not an extended vector!");
return static_cast<const ExtendedVectorType *>(LLVMTy)->getNumElements();
}
-unsigned MVT::getExtendedSizeInBits() const {
+unsigned EVT::getExtendedSizeInBits() const {
assert(isExtended() && "Type is not extended!");
return LLVMTy->getSizeInBits();
}
diff --git a/utils/TableGen/TableGen.cpp b/utils/TableGen/TableGen.cpp
index 6015814..c6d7502 100644
--- a/utils/TableGen/TableGen.cpp
+++ b/utils/TableGen/TableGen.cpp
@@ -17,24 +17,25 @@
#include "Record.h"
#include "TGParser.h"
-#include "llvm/Support/CommandLine.h"
-#include "llvm/System/Signals.h"
-#include "llvm/Support/FileUtilities.h"
-#include "llvm/Support/MemoryBuffer.h"
-#include "llvm/Support/PrettyStackTrace.h"
-#include "llvm/Support/raw_ostream.h"
#include "CallingConvEmitter.h"
#include "CodeEmitterGen.h"
#include "RegisterInfoEmitter.h"
#include "InstrInfoEmitter.h"
#include "InstrEnumEmitter.h"
#include "AsmWriterEmitter.h"
+#include "AsmMatcherEmitter.h"
#include "DAGISelEmitter.h"
#include "FastISelEmitter.h"
#include "SubtargetEmitter.h"
#include "IntrinsicEmitter.h"
#include "LLVMCConfigurationEmitter.h"
#include "ClangDiagnosticsEmitter.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileUtilities.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/System/Signals.h"
#include <algorithm>
#include <cstdio>
using namespace llvm;
@@ -43,7 +44,7 @@ enum ActionType {
PrintRecords,
GenEmitter,
GenRegisterEnums, GenRegister, GenRegisterHeader,
- GenInstrEnums, GenInstrs, GenAsmWriter,
+ GenInstrEnums, GenInstrs, GenAsmWriter, GenAsmMatcher,
GenCallingConv,
GenClangDiagsDefs,
GenClangDiagGroups,
@@ -77,6 +78,8 @@ namespace {
"Generate calling convention descriptions"),
clEnumValN(GenAsmWriter, "gen-asm-writer",
"Generate assembly writer"),
+ clEnumValN(GenAsmMatcher, "gen-asm-matcher",
+ "Generate assembly instruction matcher"),
clEnumValN(GenDAGISel, "gen-dag-isel",
"Generate a DAG instruction selector"),
clEnumValN(GenFastISel, "gen-fast-isel",
@@ -138,7 +141,7 @@ static bool ParseFile(const std::string &Filename,
std::string ErrorStr;
MemoryBuffer *F = MemoryBuffer::getFileOrSTDIN(Filename.c_str(), &ErrorStr);
if (F == 0) {
- errs() << "Could not open input file '" + Filename + "': "
+ errs() << "Could not open input file '" << Filename << "': "
<< ErrorStr <<"\n";
return true;
}
@@ -168,7 +171,7 @@ int main(int argc, char **argv) {
raw_ostream *Out = &outs();
if (OutputFilename != "-") {
std::string Error;
- Out = new raw_fd_ostream(OutputFilename.c_str(), false, Error);
+ Out = new raw_fd_ostream(OutputFilename.c_str(), Error);
if (!Error.empty()) {
errs() << argv[0] << ": error opening " << OutputFilename
@@ -210,6 +213,9 @@ int main(int argc, char **argv) {
case GenAsmWriter:
AsmWriterEmitter(Records).run(*Out);
break;
+ case GenAsmMatcher:
+ AsmMatcherEmitter(Records).run(*Out);
+ break;
case GenClangDiagsDefs:
ClangDiagsDefsEmitter(Records, ClangComponent).run(*Out);
break;
diff --git a/utils/UpdateCMakeLists.pl b/utils/UpdateCMakeLists.pl
new file mode 100755
index 0000000..3aa2f88
--- /dev/null
+++ b/utils/UpdateCMakeLists.pl
@@ -0,0 +1,118 @@
+#!/usr/bin/env perl
+use strict;
+use File::Find;
+use File::Copy;
+use Digest::MD5;
+
+my @fileTypes = ("cpp", "c");
+my %dirFiles;
+my %dirCMake;
+
+sub GetFiles {
+ my $dir = shift;
+ my $x = $dirFiles{$dir};
+ if (!defined $x) {
+ $x = [];
+ $dirFiles{$dir} = $x;
+ }
+ return $x;
+}
+
+sub ProcessFile {
+ my $file = $_;
+ my $dir = $File::Find::dir;
+ # Record if a CMake file was found.
+ if ($file eq "CMakeLists.txt") {
+ $dirCMake{$dir} = $File::Find::name;
+ return 0;
+ }
+ # Grab the extension of the file.
+ $file =~ /\.([^.]+)$/;
+ my $ext = $1;
+ my $files;
+ foreach my $x (@fileTypes) {
+ if ($ext eq $x) {
+ if (!defined $files) {
+ $files = GetFiles($dir);
+ }
+ push @$files, $file;
+ return 0;
+ }
+ }
+ return 0;
+}
+
+sub EmitCMakeList {
+ my $dir = shift;
+ my $files = $dirFiles{$dir};
+
+ if (!defined $files) {
+ return;
+ }
+
+ foreach my $file (sort @$files) {
+ print OUT " ";
+ print OUT $file;
+ print OUT "\n";
+ }
+}
+
+sub UpdateCMake {
+ my $cmakeList = shift;
+ my $dir = shift;
+ my $cmakeListNew = $cmakeList . ".new";
+ open(IN, $cmakeList);
+ open(OUT, ">", $cmakeListNew);
+ my $foundLibrary = 0;
+
+ while(<IN>) {
+ if (!$foundLibrary) {
+ print OUT $_;
+ if (/^add_clang_library\(/ || /^add_llvm_library\(/) {
+ $foundLibrary = 1;
+ EmitCMakeList($dir);
+ }
+ }
+ else {
+ if (/\)/) {
+ print OUT $_;
+ $foundLibrary = 0;
+ }
+ }
+ }
+
+ close(IN);
+ close(OUT);
+
+ open(FILE, $cmakeList) or
+ die("Cannot open $cmakeList when computing digest\n");
+ binmode FILE;
+ my $digestA = Digest::MD5->new->addfile(*FILE)->hexdigest;
+ close(FILE);
+
+ open(FILE, $cmakeListNew) or
+ die("Cannot open $cmakeListNew when computing digest\n");
+ binmode FILE;
+ my $digestB = Digest::MD5->new->addfile(*FILE)->hexdigest;
+ close(FILE);
+
+ if ($digestA ne $digestB) {
+ move($cmakeListNew, $cmakeList);
+ return 1;
+ }
+
+ unlink($cmakeListNew);
+ return 0;
+}
+
+sub UpdateCMakeFiles {
+ foreach my $dir (sort keys %dirCMake) {
+ if (UpdateCMake($dirCMake{$dir}, $dir)) {
+ print "Updated: $dir\n";
+ }
+ }
+}
+
+find({ wanted => \&ProcessFile, follow => 1 }, '.');
+UpdateCMakeFiles();
+
diff --git a/utils/bugpoint/RemoteRunSafely.sh b/utils/bugpoint/RemoteRunSafely.sh
new file mode 100644
index 0000000..f390e33
--- /dev/null
+++ b/utils/bugpoint/RemoteRunSafely.sh
@@ -0,0 +1,105 @@
+#!/bin/sh
+#
+# Program: RemoteRunSafely.sh
+#
+# Synopsis: This script simply runs another program remotely using ssh.
+# It always returns the another program exit code or exit with
+# code 255 which indicates that the program could not be executed.
+#
+# Syntax:
+#
+# RemoteRunSafely.sh <hostname> [-l <login_name>] [-p <port>]
+# <program> <args...>
+#
+# where:
+# <hostname> is the remote host to execute the program,
+# <login_name> is the username on the remote host,
+# <port> is the port used by the remote client,
+# <program> is the path to the program to run,
+# <args...> are the arguments to pass to the program.
+#
+
+printUsageAndExit()
+{
+ echo "Usage:"
+ echo "./RemoteRunSafely.sh <hostname> [-l <login_name>] [-p <port>] " \
+ "<program> <args...>"
+ exit 255
+}
+
+moreArgsExpected()
+{
+ # $1 - remaining number of arguments
+ # $2 - number of arguments to shift
+ if [ $1 -lt $2 ]
+ then
+ echo "Error: Wrong number of argumants."
+ printUsageAndExit
+ fi
+}
+
+# Save a copy of the original arguments in a string before we
+# clobber them with the shift command.
+ORIG_ARGS="$*"
+#DEBUG: echo 'GOT: '$ORIG_ARGS
+
+moreArgsExpected $# 1
+RHOST=$1
+shift 1
+
+RUSER=`id -un`
+RCLIENT=ssh
+RPORT=
+WORKING_DIR=
+
+moreArgsExpected $# 1
+if [ $1 = "-l" ]; then
+ moreArgsExpected $# 2
+ RUSER=$2
+ shift 2
+fi
+moreArgsExpected $# 1
+if [ $1 = "-p" ]; then
+ moreArgsExpected $# 2
+ RPORT="-p $2"
+ shift 2
+fi
+
+moreArgsExpected $# 1
+PROGRAM=$(basename $1)
+WORKING_DIR=$(dirname $1)
+shift 1
+
+#DEBUG: echo 'DIR='${0%%`basename $0`}
+#DEBUG: echo 'RHOST='$RHOST
+#DEBUG: echo 'RUSER='$RUSER
+#DEBUG: echo 'PROGRAM='$PROGRAM
+#DEBUG: echo 'WORKING_DIR='$WORKING_DIR
+#DEBUG: echo 'ARGS='$*
+
+# Sanity check
+if [ "$RHOST" = "" -o "$PROGRAM" = "" ]; then
+ printUsageAndExit
+fi
+
+# Local program file must exist and be execuatble
+local_program=$WORKING_DIR"/"$PROGRAM
+if [ ! -x "$local_program" ]; then
+ echo "File "$local_program" does not exist or is not an executable.."
+ exit 255
+fi
+
+connection=$RUSER'@'$RHOST
+remote="./"$PROGRAM
+(
+ cat $local_program | \
+ $RCLIENT $connection $RPORT \
+ 'rm -f '$remote' ; ' \
+ 'cat > '$remote' ; chmod +x '$remote' ; '$remote' '$*' ; ' \
+ 'err=$? ; rm -f '$remote' ; exit $err'
+)
+err=$?
+
+#DEBUG: echo script exit $err
+exit $err
+
diff --git a/utils/buildit/GNUmakefile b/utils/buildit/GNUmakefile
index 6e51312..e3b334a 100644
--- a/utils/buildit/GNUmakefile
+++ b/utils/buildit/GNUmakefile
@@ -85,6 +85,7 @@ installsrc:
-type f -a -name .DS_Store -o \
-name \*~ -o -name .\#\* \) \
-exec rm -rf {} \;
+ rm -rf "$(SRCROOT)/test"
#######################################################################
diff --git a/utils/buildit/build_llvm b/utils/buildit/build_llvm
index eac68b4..91fbe15 100755
--- a/utils/buildit/build_llvm
+++ b/utils/buildit/build_llvm
@@ -56,13 +56,19 @@ DIR=`pwd`
DARWIN_VERS=`uname -r | sed 's/\..*//'`
echo DARWIN_VERS = $DARWIN_VERS
-# If the user has CC set in their environment unset it now
-unset CC
-
-DT_HOME=$DEST_DIR/Developer/usr
-DEST_ROOT="/Developer$DEST_ROOT"
+if [ "x$RC_ProjectName" = "xllvmCore_Embedded" ]; then
+ DT_HOME=$DEST_DIR/Developer/Platforms/iPhoneOS.platform/Developer/usr
+ DEST_ROOT="/Developer/Platforms/iPhoneOS.platform/Developer$DEST_ROOT"
+else
+ DT_HOME=$DEST_DIR/Developer/usr
+ DEST_ROOT="/Developer$DEST_ROOT"
+fi
if [ "x$DEVELOPER_BIN" != "x" ]; then
- DT_HOME=$DEST_DIR/$DEVELOPER_DIR/usr
+ if [ "x$RC_ProjectName" = "xllvmCore_Embedded" ]; then
+ DT_HOME=$DEST_DIR/Developer/Platforms/iPhoneOS.platform/$DEVELOPER_DIR/usr
+ else
+ DT_HOME=$DEST_DIR/$DEVELOPER_DIR/usr
+ fi
DEST_ROOT="/$DEVELOPER_DIR$DEST_ROOT"
fi
@@ -81,11 +87,23 @@ ln -s $ORIG_SRC_DIR/* $SRC_DIR/ || exit 1
mkdir -p $DIR/obj-llvm || exit 1
cd $DIR/obj-llvm || exit 1
+# If the user has set CC or CXX, respect their wishes. If not,
+# compile with LLVM-GCC/LLVM-G++ if available; if LLVM is not
+# available, fall back to usual GCC/G++ default.
+savedPATH=$PATH ; PATH="$PATH:/Developer/usr/bin"
+XTMPCC=$(which llvm-gcc)
+if [ x$CC = x -a x$XTMPCC != x ] ; then export CC=$XTMPCC ; fi
+XTMPCC=$(which llvm-g++)
+if [ x$CXX = x -a x$XTMPCC != x ] ; then export CXX=$XTMPCC ; fi
+PATH=$savedPATH
+unset XTMPCC savedPATH
+
if [ \! -f Makefile.config ]; then
$SRC_DIR/configure --prefix=$DT_HOME/local \
--enable-targets=arm,x86,powerpc,cbe \
--enable-assertions=$LLVM_ASSERTIONS \
--enable-optimized=$LLVM_OPTIMIZED \
+ --disable-bindings \
|| exit 1
fi
@@ -136,12 +154,13 @@ if [ "x$MAJ_VER" != "x4" -o "x$MIN_VER" != "x0" ]; then
fi
make $JOBS_FLAG $OPTIMIZE_OPTS UNIVERSAL=1 UNIVERSAL_ARCH="$TARGETS" \
+ NO_RUNTIME_LIBS=1 \
LLVM_SUBMIT_VERSION=$LLVM_SUBMIT_VERSION \
LLVM_SUBMIT_SUBVERSION=$LLVM_SUBMIT_SUBVERSION \
CXXFLAGS="-DLLVM_VERSION_INFO='\" Apple Build #$LLVM_VERSION\"'" \
VERBOSE=1
-if ! test $? == 0 ; then
+if [ $? != 0 ] ; then
echo "error: LLVM 'make' failed!"
exit 1
fi
@@ -159,6 +178,7 @@ cd $DIR/obj-llvm || exit 1
# Install the tree into the destination directory.
make $LOCAL_MAKEFLAGS $OPTIMIZE_OPTS UNIVERSAL=1 UNIVERSAL_ARCH="$TARGETS" \
+ NO_RUNTIME_LIBS=1 \
LLVM_SUBMIT_VERSION=$LLVM_SUBMIT_VERSION \
LLVM_SUBMIT_SUBVERSION=$LLVM_SUBMIT_SUBVERSION \
OPTIMIZE_OPTION='-O3' VERBOSE=1 install
diff --git a/utils/count/CMakeLists.txt b/utils/count/CMakeLists.txt
new file mode 100644
index 0000000..e124f61
--- /dev/null
+++ b/utils/count/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_executable(count
+ count.c
+ )
diff --git a/utils/count/Makefile b/utils/count/Makefile
new file mode 100644
index 0000000..8de076a
--- /dev/null
+++ b/utils/count/Makefile
@@ -0,0 +1,20 @@
+##===- utils/count/Makefile --------------------------------*- Makefile -*-===##
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+##===----------------------------------------------------------------------===##
+
+LEVEL = ../..
+TOOLNAME = count
+USEDLIBS =
+
+# This tool has no plugins, optimize startup time.
+TOOL_NO_EXPORTS = 1
+
+# Don't install this utility
+NO_INSTALL = 1
+
+include $(LEVEL)/Makefile.common
diff --git a/utils/count/count.c b/utils/count/count.c
new file mode 100644
index 0000000..a37e1e0
--- /dev/null
+++ b/utils/count/count.c
@@ -0,0 +1,48 @@
+/*===- count.c - The 'count' testing tool ---------------------------------===*\
+ *
+ * The LLVM Compiler Infrastructure
+ *
+ * This file is distributed under the University of Illinois Open Source
+ * License. See LICENSE.TXT for details.
+ *
+\*===----------------------------------------------------------------------===*/
+
+#include <stdlib.h>
+#include <stdio.h>
+
+int main(int argc, char **argv) {
+ unsigned Count, NumLines, NumRead;
+ char Buffer[4096], *End;
+
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s <expected line count>\n", argv[0]);
+ return 2;
+ }
+
+ Count = strtol(argv[1], &End, 10);
+ if (*End != '\0' && End != argv[1]) {
+ fprintf(stderr, "%s: invalid count argument '%s'\n", argv[0], argv[1]);
+ return 2;
+ }
+
+ NumLines = 0;
+ while ((NumRead = fread(Buffer, 1, sizeof(Buffer), stdin))) {
+ unsigned i;
+
+ for (i = 0; i != NumRead; ++i)
+ if (Buffer[i] == '\n')
+ ++NumLines;
+ }
+
+ if (!feof(stdin)) {
+ fprintf(stderr, "%s: error reading stdin\n", argv[0]);
+ return 3;
+ }
+
+ if (Count != NumLines) {
+ fprintf(stderr, "Expected %d lines, got %d.\n", Count, NumLines);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/utils/crosstool/ARM/build-install-linux.sh b/utils/crosstool/ARM/build-install-linux.sh
index 33833b5..f3f8df9 100755
--- a/utils/crosstool/ARM/build-install-linux.sh
+++ b/utils/crosstool/ARM/build-install-linux.sh
@@ -9,7 +9,7 @@ set -o errexit
echo -n "Welcome to LLVM Linux/X86_64 -> Linux/ARM crosstool "
echo "builder/installer; some steps will require sudo privileges."
-readonly INSTALL_ROOT="${INSTALL_ROOT:-/usr/local}"
+readonly INSTALL_ROOT="${INSTALL_ROOT:-/usr/local/crosstool}"
# Both $USER and root *must* have read/write access to this dir.
readonly SCRATCH_ROOT=$(mktemp -d "${TMPDIR:-/tmp}/llvm-project.XXXXXX")
readonly SRC_ROOT="${SCRATCH_ROOT}/src"
@@ -17,6 +17,7 @@ readonly OBJ_ROOT="${SCRATCH_ROOT}/obj"
readonly CROSS_HOST="x86_64-unknown-linux-gnu"
readonly CROSS_TARGET="arm-none-linux-gnueabi"
+readonly CROSS_MARCH="${CROSS_MARCH:-armv6}"
readonly CODE_SOURCERY="${INSTALL_ROOT}/codesourcery"
readonly CODE_SOURCERY_PKG_PATH="${CODE_SOURCERY_PKG_PATH:-${HOME}/codesourcery}"
@@ -32,33 +33,23 @@ readonly CROSS_TARGET_LD="${CODE_SOURCERY_BIN}/${CROSS_TARGET}-ld"
readonly SYSROOT="${CODE_SOURCERY_ROOT}/${CROSS_TARGET}/libc"
-readonly LLVM_PROJECT="${INSTALL_ROOT}/llvm-project"
-readonly LLVM_INSTALL_ROOT="${LLVM_PROJECT}/${CROSS_HOST}/${CROSS_TARGET}"
readonly LLVM_PKG_PATH="${LLVM_PKG_PATH:-${HOME}/llvm-project/snapshots}"
-# Latest SVN revision known to be working in this configuration.
-readonly LLVM_DEFAULT_REV="70786"
+# Latest SVN revisions known to be working in this configuration.
+readonly LLVM_DEFAULT_REV="74530"
+readonly LLVMGCC_DEFAULT_REV="74535"
readonly LLVM_PKG="llvm-${LLVM_SVN_REV:-${LLVM_DEFAULT_REV}}.tar.bz2"
readonly LLVM_SRC_DIR="${SRC_ROOT}/llvm"
readonly LLVM_OBJ_DIR="${OBJ_ROOT}/llvm"
-readonly LLVM_INSTALL_DIR="${LLVM_INSTALL_ROOT}/llvm"
+readonly LLVM_INSTALL_DIR="${INSTALL_ROOT}/${CROSS_TARGET}/llvm"
-readonly LLVMGCC_PKG="llvm-gcc-4.2-${LLVMGCC_SVN_REV:-${LLVM_DEFAULT_REV}}.tar.bz2"
+readonly LLVMGCC_PKG="llvm-gcc-4.2-${LLVMGCC_SVN_REV:-${LLVMGCC_DEFAULT_REV}}.tar.bz2"
readonly LLVMGCC_SRC_DIR="${SRC_ROOT}/llvm-gcc-4.2"
readonly LLVMGCC_OBJ_DIR="${OBJ_ROOT}/llvm-gcc-4.2"
-readonly LLVMGCC_INSTALL_DIR="${LLVM_INSTALL_ROOT}/llvm-gcc-4.2"
+readonly LLVMGCC_INSTALL_DIR="${INSTALL_ROOT}/${CROSS_TARGET}/llvm-gcc-4.2"
-readonly MAKE_OPTS="-j2"
-
-# Verify we aren't going to install into an existing directory as this might
-# create problems as we won't have a clean install.
-verifyNotDir() {
- if [[ -d $1 ]]; then
- echo "Install dir $1 already exists; remove it to continue."
- exit
- fi
-}
+readonly MAKE_OPTS="${MAKE_OPTS:--j2}"
# Params:
# $1: directory to be created
@@ -104,12 +95,9 @@ runAndLog() {
}
installCodeSourcery() {
- # Create CodeSourcery dir, if necessary.
- verifyNotDir ${CODE_SOURCERY}
- sudoCreateDir ${CODE_SOURCERY}
-
- # Unpack the tarball.
+ # Unpack the tarball, creating the CodeSourcery dir, if necessary.
if [[ ! -d ${CODE_SOURCERY_ROOT} ]]; then
+ sudoCreateDir ${CODE_SOURCERY}
cd ${CODE_SOURCERY}
if [[ -e ${CODE_SOURCERY_PKG_PATH}/${CODE_SOURCERY_PKG} ]]; then
runCommand "Unpacking CodeSourcery in ${CODE_SOURCERY}" \
@@ -122,7 +110,7 @@ installCodeSourcery() {
exit
fi
else
- echo "CodeSourcery install dir already exists."
+ echo "CodeSourcery install dir already exists; skipping."
fi
# Verify our CodeSourcery toolchain installation.
@@ -141,7 +129,11 @@ installCodeSourcery() {
}
installLLVM() {
- verifyNotDir ${LLVM_INSTALL_DIR}
+ if [[ -d ${LLVM_INSTALL_DIR} ]]; then
+ echo "LLVM install dir ${LLVM_INSTALL_DIR} exists; skipping."
+ return
+ fi
+
sudoCreateDir ${LLVM_INSTALL_DIR}
# Unpack LLVM tarball; should create the directory "llvm".
@@ -165,7 +157,11 @@ installLLVM() {
}
installLLVMGCC() {
- verifyNotDir ${LLVMGCC_INSTALL_DIR}
+ if [[ -d ${LLVMGCC_INSTALL_DIR} ]]; then
+ echo "LLVM-GCC install dir ${LLVMGCC_INSTALL_DIR} exists; skipping."
+ return
+ fi
+
sudoCreateDir ${LLVMGCC_INSTALL_DIR}
# Unpack LLVM-GCC tarball; should create the directory "llvm-gcc-4.2".
@@ -182,8 +178,9 @@ installLLVMGCC() {
--prefix=${LLVMGCC_INSTALL_DIR} \
--program-prefix=llvm- \
--target=${CROSS_TARGET} \
- --with-gnu-as=${CROSS_TARGET_AS} \
- --with-gnu-ld=${CROSS_TARGET_LD} \
+ --with-arch=${CROSS_MARCH} \
+ --with-as=${CROSS_TARGET_AS} \
+ --with-ld=${CROSS_TARGET_LD} \
--with-sysroot=${SYSROOT}
runAndLog "Building LLVM-GCC" ${LLVMGCC_OBJ_DIR}/llvmgcc-build.log \
make
diff --git a/utils/crosstool/create-snapshots.sh b/utils/crosstool/create-snapshots.sh
index 7c640bc..d70232a 100755
--- a/utils/crosstool/create-snapshots.sh
+++ b/utils/crosstool/create-snapshots.sh
@@ -11,31 +11,31 @@
set -o nounset
set -o errexit
-readonly REV="${1:-HEAD}"
+readonly LLVM_PROJECT_SVN="http://llvm.org/svn/llvm-project"
-runOnModule() {
+getLatestRevisionFromSVN() {
+ svn info ${LLVM_PROJECT_SVN} | egrep ^Revision | sed 's/^Revision: //'
+}
+
+readonly REV="${1:-$(getLatestRevisionFromSVN)}"
+
+createTarballFromSVN() {
local module=$1
local log="${module}.log"
- echo "Running: svn co -r ${REV} ${module}; log in ${log}"
- svn co -r ${REV} http://llvm.org/svn/llvm-project/${module}/trunk ${module} \
- > ${log} 2>&1
-
- # Delete all the ".svn" dirs; they take quite a lot of space.
- echo "Cleaning up .svn dirs"
- find ${module} -type d -name \.svn -print0 | xargs -0 /bin/rm -rf
+ echo "Running: svn export -r ${REV} ${module}; log in ${log}"
+ svn -q export -r ${REV} ${LLVM_PROJECT_SVN}/${module}/trunk \
+ ${module} > ${log} 2>&1
# Create "module-revision.tar.bz2" packages from the SVN checkout dirs.
- local revision=$(grep "Checked out revision" ${log} | \
- sed 's/[^0-9]\+\([0-9]\+\)[^0-9]\+/\1/')
- local tarball="${module}-${revision}.tar.bz2"
+ local tarball="${module}-${REV}.tar.bz2"
echo "Creating tarball: ${tarball}"
tar cjf ${tarball} ${module}
- echo "Cleaning SVN checkout dir ${module}"
+ echo "Cleaning up '${module}'"
rm -rf ${module} ${log}
}
for module in "llvm" "llvm-gcc-4.2"; do
- runOnModule ${module}
+ createTarballFromSVN ${module}
done
diff --git a/utils/emacs/emacs.el b/utils/emacs/emacs.el
index a12848b..969f538 100644
--- a/utils/emacs/emacs.el
+++ b/utils/emacs/emacs.el
@@ -1,6 +1,6 @@
;; LLVM coding style guidelines in emacs
;; Maintainer: LLVM Team, http://llvm.org/
-;; Modified: 2005-04-24
+;; Modified: 2009-07-28
;; Max 80 cols per line, indent by two spaces, no tabs.
;; Apparently, this does not affect tabs in Makefiles.
@@ -17,7 +17,9 @@
'((fill-column . 80)
(c++-indent-level . 2)
(c-basic-offset . 2)
- (indent-tabs-mode . nil)))
+ (indent-tabs-mode . nil)
+ (c-offsets-alist . ((innamespace 0)))))
+
(add-hook 'c-mode-hook
(function
(lambda nil
@@ -26,6 +28,7 @@
(c-set-style "llvm.org")
)
))))
+
(add-hook 'c++-mode-hook
(function
(lambda nil
diff --git a/utils/lit/LitConfig.py b/utils/lit/LitConfig.py
new file mode 100644
index 0000000..4fb0ccc
--- /dev/null
+++ b/utils/lit/LitConfig.py
@@ -0,0 +1,71 @@
+class LitConfig:
+ """LitConfig - Configuration data for a 'lit' test runner instance, shared
+ across all tests.
+
+ The LitConfig object is also used to communicate with client configuration
+ files, it is always passed in as the global variable 'lit' so that
+ configuration files can access common functionality and internal components
+ easily.
+ """
+
+ # Provide access to built-in formats.
+ import LitFormats as formats
+
+ # Provide access to built-in utility functions.
+ import Util as util
+
+ def __init__(self, progname, path, quiet,
+ useValgrind, valgrindArgs,
+ useTclAsSh,
+ noExecute, debug, isWindows):
+ # 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.quiet = bool(quiet)
+ self.useValgrind = bool(useValgrind)
+ self.valgrindArgs = list(valgrindArgs)
+ self.useTclAsSh = bool(useTclAsSh)
+ self.noExecute = noExecute
+ self.debug = debug
+ self.isWindows = bool(isWindows)
+
+ self.numErrors = 0
+ self.numWarnings = 0
+
+ def load_config(self, config, path):
+ """load_config(config, path) - Load a config object from an alternate
+ path."""
+ from TestingConfig import TestingConfig
+ return TestingConfig.frompath(path, config.parent, self,
+ mustExist = True,
+ config = config)
+
+ 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.
+ f = f.f_back.f_back
+ file,line,_,_,_ = inspect.getframeinfo(f)
+ location = '%s:%d' % (os.path.basename(file), line)
+
+ print >>sys.stderr, '%s: %s: %s: %s' % (self.progname, location,
+ kind, message)
+
+ def note(self, message):
+ self._write_message('note', message)
+
+ def warning(self, message):
+ self._write_message('warning', message)
+ self.numWarnings += 1
+
+ def error(self, message):
+ self._write_message('error', message)
+ self.numErrors += 1
+
+ def fatal(self, message):
+ import sys
+ self._write_message('fatal', message)
+ sys.exit(2)
diff --git a/utils/lit/LitFormats.py b/utils/lit/LitFormats.py
new file mode 100644
index 0000000..9b8250d
--- /dev/null
+++ b/utils/lit/LitFormats.py
@@ -0,0 +1,2 @@
+from TestFormats import GoogleTest, ShTest, TclTest, SyntaxCheckTest
+
diff --git a/utils/lit/ProgressBar.py b/utils/lit/ProgressBar.py
new file mode 100644
index 0000000..85c95f5
--- /dev/null
+++ b/utils/lit/ProgressBar.py
@@ -0,0 +1,267 @@
+#!/usr/bin/env python
+
+# Source: http://code.activestate.com/recipes/475116/, with
+# modifications by Daniel Dunbar.
+
+import sys, re, time
+
+class TerminalController:
+ """
+ A class that can be used to portably generate formatted output to
+ a terminal.
+
+ `TerminalController` defines a set of instance variables whose
+ values are initialized to the control sequence necessary to
+ perform a given action. These can be simply included in normal
+ output to the terminal:
+
+ >>> term = TerminalController()
+ >>> print 'This is '+term.GREEN+'green'+term.NORMAL
+
+ Alternatively, the `render()` method can used, which replaces
+ '${action}' with the string required to perform 'action':
+
+ >>> term = TerminalController()
+ >>> print term.render('This is ${GREEN}green${NORMAL}')
+
+ If the terminal doesn't support a given action, then the value of
+ the corresponding instance variable will be set to ''. As a
+ result, the above code will still work on terminals that do not
+ support color, except that their output will not be colored.
+ Also, this means that you can test whether the terminal supports a
+ given action by simply testing the truth value of the
+ corresponding instance variable:
+
+ >>> term = TerminalController()
+ >>> if term.CLEAR_SCREEN:
+ ... print 'This terminal supports clearning the screen.'
+
+ Finally, if the width and height of the terminal are known, then
+ they will be stored in the `COLS` and `LINES` attributes.
+ """
+ # Cursor movement:
+ BOL = '' #: Move the cursor to the beginning of the line
+ UP = '' #: Move the cursor up one line
+ DOWN = '' #: Move the cursor down one line
+ LEFT = '' #: Move the cursor left one char
+ RIGHT = '' #: Move the cursor right one char
+
+ # Deletion:
+ CLEAR_SCREEN = '' #: Clear the screen and move to home position
+ CLEAR_EOL = '' #: Clear to the end of the line.
+ CLEAR_BOL = '' #: Clear to the beginning of the line.
+ CLEAR_EOS = '' #: Clear to the end of the screen
+
+ # Output modes:
+ BOLD = '' #: Turn on bold mode
+ BLINK = '' #: Turn on blink mode
+ DIM = '' #: Turn on half-bright mode
+ REVERSE = '' #: Turn on reverse-video mode
+ NORMAL = '' #: Turn off all modes
+
+ # Cursor display:
+ HIDE_CURSOR = '' #: Make the cursor invisible
+ SHOW_CURSOR = '' #: Make the cursor visible
+
+ # Terminal size:
+ COLS = None #: Width of the terminal (None for unknown)
+ LINES = None #: Height of the terminal (None for unknown)
+
+ # Foreground colors:
+ BLACK = BLUE = GREEN = CYAN = RED = MAGENTA = YELLOW = WHITE = ''
+
+ # Background colors:
+ BG_BLACK = BG_BLUE = BG_GREEN = BG_CYAN = ''
+ BG_RED = BG_MAGENTA = BG_YELLOW = BG_WHITE = ''
+
+ _STRING_CAPABILITIES = """
+ BOL=cr UP=cuu1 DOWN=cud1 LEFT=cub1 RIGHT=cuf1
+ CLEAR_SCREEN=clear CLEAR_EOL=el CLEAR_BOL=el1 CLEAR_EOS=ed BOLD=bold
+ BLINK=blink DIM=dim REVERSE=rev UNDERLINE=smul NORMAL=sgr0
+ HIDE_CURSOR=cinvis SHOW_CURSOR=cnorm""".split()
+ _COLORS = """BLACK BLUE GREEN CYAN RED MAGENTA YELLOW WHITE""".split()
+ _ANSICOLORS = "BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE".split()
+
+ def __init__(self, term_stream=sys.stdout):
+ """
+ Create a `TerminalController` and initialize its attributes
+ with appropriate values for the current terminal.
+ `term_stream` is the stream that will be used for terminal
+ output; if this stream is not a tty, then the terminal is
+ assumed to be a dumb terminal (i.e., have no capabilities).
+ """
+ # Curses isn't available on all platforms
+ try: import curses
+ except: return
+
+ # If the stream isn't a tty, then assume it has no capabilities.
+ if not term_stream.isatty(): return
+
+ # Check the terminal type. If we fail, then assume that the
+ # terminal has no capabilities.
+ try: curses.setupterm()
+ except: return
+
+ # Look up numeric capabilities.
+ self.COLS = curses.tigetnum('cols')
+ self.LINES = curses.tigetnum('lines')
+
+ # Look up string capabilities.
+ for capability in self._STRING_CAPABILITIES:
+ (attrib, cap_name) = capability.split('=')
+ setattr(self, attrib, self._tigetstr(cap_name) or '')
+
+ # Colors
+ 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 '')
+ 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 '')
+ 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 '')
+ 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 '')
+
+ 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 ''
+ return re.sub(r'\$<\d+>[/*]?', '', cap)
+
+ def render(self, template):
+ """
+ Replace each $-substitutions in the given template string with
+ the corresponding terminal control string (if it's defined) or
+ '' (if it's not).
+ """
+ return re.sub(r'\$\$|\${\w+}', self._render_sub, template)
+
+ def _render_sub(self, match):
+ s = match.group()
+ if s == '$$': return s
+ else: return getattr(self, s[2:-1])
+
+#######################################################################
+# Example use case: progress bar
+#######################################################################
+
+class SimpleProgressBar:
+ """
+ A simple progress bar which doesn't need any terminal support.
+
+ This prints out a progress bar like:
+ 'Header: 0 .. 10.. 20.. ...'
+ """
+
+ def __init__(self, header):
+ self.header = header
+ self.atIndex = None
+
+ def update(self, percent, message):
+ if self.atIndex is None:
+ sys.stdout.write(self.header)
+ self.atIndex = 0
+
+ next = int(percent*50)
+ if next == self.atIndex:
+ return
+
+ for i in range(self.atIndex, next):
+ idx = i % 5
+ if idx == 0:
+ sys.stdout.write('%-2d' % (i*2))
+ elif idx == 1:
+ pass # Skip second char
+ elif idx < 4:
+ sys.stdout.write('.')
+ else:
+ sys.stdout.write(' ')
+ sys.stdout.flush()
+ self.atIndex = next
+
+ def clear(self):
+ if self.atIndex is not None:
+ sys.stdout.write('\n')
+ sys.stdout.flush()
+ self.atIndex = None
+
+class ProgressBar:
+ """
+ A 3-line progress bar, which looks like::
+
+ Header
+ 20% [===========----------------------------------]
+ progress message
+
+ The progress bar is colored, if the terminal supports color
+ output; and adjusts to the width of the terminal.
+ """
+ BAR = '%s${GREEN}[${BOLD}%s%s${NORMAL}${GREEN}]${NORMAL}%s\n'
+ HEADER = '${BOLD}${CYAN}%s${NORMAL}\n\n'
+
+ def __init__(self, term, header, useETA=True):
+ self.term = term
+ if not (self.term.CLEAR_EOL and self.term.UP and self.term.BOL):
+ raise ValueError("Terminal isn't capable enough -- you "
+ "should use a simpler progress dispaly.")
+ self.width = self.term.COLS or 75
+ self.bar = term.render(self.BAR)
+ self.header = self.term.render(self.HEADER % header.center(self.width))
+ self.cleared = 1 #: true if we haven't drawn the bar yet.
+ self.useETA = useETA
+ if self.useETA:
+ self.startTime = time.time()
+ self.update(0, '')
+
+ def update(self, percent, message):
+ if self.cleared:
+ sys.stdout.write(self.header)
+ self.cleared = 0
+ prefix = '%3d%% ' % (percent*100,)
+ suffix = ''
+ if self.useETA:
+ elapsed = time.time() - self.startTime
+ if percent > .0001 and elapsed > 1:
+ total = elapsed / percent
+ eta = int(total - elapsed)
+ h = eta//3600.
+ m = (eta//60) % 60
+ s = eta % 60
+ suffix = ' ETA: %02d:%02d:%02d'%(h,m,s)
+ barWidth = self.width - len(prefix) - len(suffix) - 2
+ n = int(barWidth*percent)
+ if len(message) < self.width:
+ message = message + ' '*(self.width - len(message))
+ else:
+ message = '... ' + message[-(self.width-4):]
+ sys.stdout.write(
+ self.term.BOL + self.term.UP + self.term.CLEAR_EOL +
+ (self.bar % (prefix, '='*n, '-'*(barWidth-n), suffix)) +
+ self.term.CLEAR_EOL + message)
+
+ def clear(self):
+ if not self.cleared:
+ sys.stdout.write(self.term.BOL + self.term.CLEAR_EOL +
+ self.term.UP + self.term.CLEAR_EOL +
+ self.term.UP + self.term.CLEAR_EOL)
+ self.cleared = 1
+
+def test():
+ import time
+ tc = TerminalController()
+ p = ProgressBar(tc, 'Tests')
+ for i in range(101):
+ p.update(i/100., str(i))
+ time.sleep(.3)
+
+if __name__=='__main__':
+ test()
diff --git a/utils/lit/ShCommands.py b/utils/lit/ShCommands.py
new file mode 100644
index 0000000..4550437
--- /dev/null
+++ b/utils/lit/ShCommands.py
@@ -0,0 +1,85 @@
+class Command:
+ def __init__(self, args, redirects):
+ self.args = list(args)
+ self.redirects = list(redirects)
+
+ def __repr__(self):
+ return 'Command(%r, %r)' % (self.args, self.redirects)
+
+ def __cmp__(self, other):
+ if not isinstance(other, Command):
+ return -1
+
+ return cmp((self.args, self.redirects),
+ (other.args, other.redirects))
+
+ def toShell(self, file):
+ for arg in self.args:
+ if "'" not in arg:
+ quoted = "'%s'" % arg
+ elif '"' not in arg and '$' not in arg:
+ quoted = '"%s"' % arg
+ else:
+ raise NotImplementedError,'Unable to quote %r' % arg
+ print >>file, quoted,
+
+ # For debugging / validation.
+ import ShUtil
+ dequoted = list(ShUtil.ShLexer(quoted).lex())
+ if dequoted != [arg]:
+ raise NotImplementedError,'Unable to quote %r' % arg
+
+ for r in self.redirects:
+ if len(r[0]) == 1:
+ print >>file, "%s '%s'" % (r[0][0], r[1]),
+ else:
+ print >>file, "%s%s '%s'" % (r[0][1], r[0][0], r[1]),
+
+class Pipeline:
+ def __init__(self, commands, negate=False, pipe_err=False):
+ self.commands = commands
+ self.negate = negate
+ self.pipe_err = pipe_err
+
+ def __repr__(self):
+ return 'Pipeline(%r, %r, %r)' % (self.commands, self.negate,
+ self.pipe_err)
+
+ def __cmp__(self, other):
+ if not isinstance(other, Pipeline):
+ return -1
+
+ return cmp((self.commands, self.negate, self.pipe_err),
+ (other.commands, other.negate, self.pipe_err))
+
+ def toShell(self, file, pipefail=False):
+ if pipefail != self.pipe_err:
+ raise ValueError,'Inconsistent "pipefail" attribute!'
+ if self.negate:
+ print >>file, '!',
+ for cmd in self.commands:
+ cmd.toShell(file)
+ if cmd is not self.commands[-1]:
+ print >>file, '|\n ',
+
+class Seq:
+ def __init__(self, lhs, op, rhs):
+ assert op in (';', '&', '||', '&&')
+ self.op = op
+ self.lhs = lhs
+ self.rhs = rhs
+
+ def __repr__(self):
+ return 'Seq(%r, %r, %r)' % (self.lhs, self.op, self.rhs)
+
+ def __cmp__(self, other):
+ if not isinstance(other, Seq):
+ return -1
+
+ return cmp((self.lhs, self.op, self.rhs),
+ (other.lhs, other.op, other.rhs))
+
+ def toShell(self, file, pipefail=False):
+ self.lhs.toShell(file, pipefail)
+ print >>file, ' %s\n' % self.op
+ self.rhs.toShell(file, pipefail)
diff --git a/utils/lit/ShUtil.py b/utils/lit/ShUtil.py
new file mode 100644
index 0000000..c4bbb3d
--- /dev/null
+++ b/utils/lit/ShUtil.py
@@ -0,0 +1,346 @@
+import itertools
+
+import Util
+from ShCommands import Command, Pipeline, Seq
+
+class ShLexer:
+ def __init__(self, data, win32Escapes = False):
+ self.data = data
+ self.pos = 0
+ self.end = len(data)
+ self.win32Escapes = win32Escapes
+
+ def eat(self):
+ c = self.data[self.pos]
+ self.pos += 1
+ return c
+
+ def look(self):
+ return self.data[self.pos]
+
+ def maybe_eat(self, c):
+ """
+ maybe_eat(c) - Consume the character c if it is the next character,
+ returning True if a character was consumed. """
+ if self.data[self.pos] == c:
+ self.pos += 1
+ return True
+ return False
+
+ def lex_arg_fast(self, c):
+ # Get the leading whitespace free section.
+ chunk = self.data[self.pos - 1:].split(None, 1)[0]
+
+ # If it has special characters, the fast path failed.
+ if ('|' in chunk or '&' in chunk or
+ '<' in chunk or '>' in chunk or
+ "'" in chunk or '"' in chunk or
+ '\\' in chunk):
+ return None
+
+ self.pos = self.pos - 1 + len(chunk)
+ return chunk
+
+ def lex_arg_slow(self, c):
+ if c in "'\"":
+ str = self.lex_arg_quoted(c)
+ else:
+ str = c
+ while self.pos != self.end:
+ c = self.look()
+ if c.isspace() or c in "|&":
+ break
+ elif c in '><':
+ # This is an annoying case; we treat '2>' as a single token so
+ # we don't have to track whitespace tokens.
+
+ # If the parse string isn't an integer, do the usual thing.
+ if not str.isdigit():
+ break
+
+ # Otherwise, lex the operator and convert to a redirection
+ # token.
+ num = int(str)
+ tok = self.lex_one_token()
+ assert isinstance(tok, tuple) and len(tok) == 1
+ return (tok[0], num)
+ elif c == '"':
+ self.eat()
+ str += self.lex_arg_quoted('"')
+ elif not self.win32Escapes and c == '\\':
+ # Outside of a string, '\\' escapes everything.
+ self.eat()
+ if self.pos == self.end:
+ Util.warning("escape at end of quoted argument in: %r" %
+ self.data)
+ return str
+ str += self.eat()
+ else:
+ str += self.eat()
+ return str
+
+ def lex_arg_quoted(self, delim):
+ str = ''
+ while self.pos != self.end:
+ c = self.eat()
+ if c == delim:
+ return str
+ elif c == '\\' and delim == '"':
+ # Inside a '"' quoted string, '\\' only escapes the quote
+ # character and backslash, otherwise it is preserved.
+ if self.pos == self.end:
+ Util.warning("escape at end of quoted argument in: %r" %
+ self.data)
+ return str
+ c = self.eat()
+ if c == '"': #
+ str += '"'
+ elif c == '\\':
+ str += '\\'
+ else:
+ str += '\\' + c
+ else:
+ str += c
+ Util.warning("missing quote character in %r" % self.data)
+ return str
+
+ def lex_arg_checked(self, c):
+ pos = self.pos
+ res = self.lex_arg_fast(c)
+ end = self.pos
+
+ self.pos = pos
+ reference = self.lex_arg_slow(c)
+ if res is not None:
+ if res != reference:
+ raise ValueError,"Fast path failure: %r != %r" % (res, reference)
+ if self.pos != end:
+ raise ValueError,"Fast path failure: %r != %r" % (self.pos, end)
+ return reference
+
+ def lex_arg(self, c):
+ return self.lex_arg_fast(c) or self.lex_arg_slow(c)
+
+ def lex_one_token(self):
+ """
+ lex_one_token - Lex a single 'sh' token. """
+
+ c = self.eat()
+ if c in ';!':
+ return (c,)
+ if c == '|':
+ if self.maybe_eat('|'):
+ return ('||',)
+ return (c,)
+ if c == '&':
+ if self.maybe_eat('&'):
+ return ('&&',)
+ if self.maybe_eat('>'):
+ return ('&>',)
+ return (c,)
+ if c == '>':
+ if self.maybe_eat('&'):
+ return ('>&',)
+ if self.maybe_eat('>'):
+ return ('>>',)
+ return (c,)
+ if c == '<':
+ if self.maybe_eat('&'):
+ return ('<&',)
+ if self.maybe_eat('>'):
+ return ('<<',)
+ return (c,)
+
+ return self.lex_arg(c)
+
+ def lex(self):
+ while self.pos != self.end:
+ if self.look().isspace():
+ self.eat()
+ else:
+ yield self.lex_one_token()
+
+###
+
+class ShParser:
+ def __init__(self, data, win32Escapes = False):
+ self.data = data
+ self.tokens = ShLexer(data, win32Escapes = win32Escapes).lex()
+
+ def lex(self):
+ try:
+ return self.tokens.next()
+ except StopIteration:
+ return None
+
+ def look(self):
+ next = self.lex()
+ if next is not None:
+ self.tokens = itertools.chain([next], self.tokens)
+ return next
+
+ def parse_command(self):
+ tok = self.lex()
+ if not tok:
+ raise ValueError,"empty command!"
+ if isinstance(tok, tuple):
+ raise ValueError,"syntax error near unexpected token %r" % tok[0]
+
+ args = [tok]
+ redirects = []
+ while 1:
+ tok = self.look()
+
+ # EOF?
+ if tok is None:
+ break
+
+ # If this is an argument, just add it to the current command.
+ if isinstance(tok, str):
+ args.append(self.lex())
+ continue
+
+ # Otherwise see if it is a terminator.
+ assert isinstance(tok, tuple)
+ if tok[0] in ('|',';','&','||','&&'):
+ break
+
+ # Otherwise it must be a redirection.
+ op = self.lex()
+ arg = self.lex()
+ if not arg:
+ raise ValueError,"syntax error near token %r" % op[0]
+ redirects.append((op, arg))
+
+ return Command(args, redirects)
+
+ def parse_pipeline(self):
+ negate = False
+ if self.look() == ('!',):
+ self.lex()
+ negate = True
+
+ commands = [self.parse_command()]
+ while self.look() == ('|',):
+ self.lex()
+ commands.append(self.parse_command())
+ return Pipeline(commands, negate)
+
+ def parse(self):
+ lhs = self.parse_pipeline()
+
+ while self.look():
+ operator = self.lex()
+ assert isinstance(operator, tuple) and len(operator) == 1
+
+ if not self.look():
+ raise ValueError, "missing argument to operator %r" % operator[0]
+
+ # FIXME: Operator precedence!!
+ lhs = Seq(lhs, operator[0], self.parse_pipeline())
+
+ return lhs
+
+###
+
+import unittest
+
+class TestShLexer(unittest.TestCase):
+ def lex(self, str, *args, **kwargs):
+ return list(ShLexer(str, *args, **kwargs).lex())
+
+ def test_basic(self):
+ self.assertEqual(self.lex('a|b>c&d<e'),
+ ['a', ('|',), 'b', ('>',), 'c', ('&',), 'd',
+ ('<',), 'e'])
+
+ def test_redirection_tokens(self):
+ self.assertEqual(self.lex('a2>c'),
+ ['a2', ('>',), 'c'])
+ self.assertEqual(self.lex('a 2>c'),
+ ['a', ('>',2), 'c'])
+
+ def test_quoting(self):
+ self.assertEqual(self.lex(""" 'a' """),
+ ['a'])
+ self.assertEqual(self.lex(""" "hello\\"world" """),
+ ['hello"world'])
+ self.assertEqual(self.lex(""" "hello\\'world" """),
+ ["hello\\'world"])
+ self.assertEqual(self.lex(""" "hello\\\\world" """),
+ ["hello\\world"])
+ self.assertEqual(self.lex(""" he"llo wo"rld """),
+ ["hello world"])
+ self.assertEqual(self.lex(""" a\\ b a\\\\b """),
+ ["a b", "a\\b"])
+ self.assertEqual(self.lex(""" "" "" """),
+ ["", ""])
+ self.assertEqual(self.lex(""" a\\ b """, win32Escapes = True),
+ ['a\\', 'b'])
+
+class TestShParse(unittest.TestCase):
+ def parse(self, str):
+ return ShParser(str).parse()
+
+ def test_basic(self):
+ self.assertEqual(self.parse('echo hello'),
+ Pipeline([Command(['echo', 'hello'], [])], False))
+ self.assertEqual(self.parse('echo ""'),
+ Pipeline([Command(['echo', ''], [])], False))
+
+ def test_redirection(self):
+ self.assertEqual(self.parse('echo hello > c'),
+ Pipeline([Command(['echo', 'hello'],
+ [((('>'),), 'c')])], False))
+ self.assertEqual(self.parse('echo hello > c >> d'),
+ Pipeline([Command(['echo', 'hello'], [(('>',), 'c'),
+ (('>>',), 'd')])], False))
+ self.assertEqual(self.parse('a 2>&1'),
+ Pipeline([Command(['a'], [(('>&',2), '1')])], False))
+
+ def test_pipeline(self):
+ self.assertEqual(self.parse('a | b'),
+ Pipeline([Command(['a'], []),
+ Command(['b'], [])],
+ False))
+
+ self.assertEqual(self.parse('a | b | c'),
+ Pipeline([Command(['a'], []),
+ Command(['b'], []),
+ Command(['c'], [])],
+ False))
+
+ self.assertEqual(self.parse('! a'),
+ Pipeline([Command(['a'], [])],
+ True))
+
+ def test_list(self):
+ self.assertEqual(self.parse('a ; b'),
+ Seq(Pipeline([Command(['a'], [])], False),
+ ';',
+ Pipeline([Command(['b'], [])], False)))
+
+ self.assertEqual(self.parse('a & b'),
+ Seq(Pipeline([Command(['a'], [])], False),
+ '&',
+ Pipeline([Command(['b'], [])], False)))
+
+ self.assertEqual(self.parse('a && b'),
+ Seq(Pipeline([Command(['a'], [])], False),
+ '&&',
+ Pipeline([Command(['b'], [])], False)))
+
+ self.assertEqual(self.parse('a || b'),
+ Seq(Pipeline([Command(['a'], [])], False),
+ '||',
+ Pipeline([Command(['b'], [])], False)))
+
+ self.assertEqual(self.parse('a && b || c'),
+ Seq(Seq(Pipeline([Command(['a'], [])], False),
+ '&&',
+ Pipeline([Command(['b'], [])], False)),
+ '||',
+ Pipeline([Command(['c'], [])], False)))
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/utils/lit/TODO b/utils/lit/TODO
new file mode 100644
index 0000000..4d00d2c
--- /dev/null
+++ b/utils/lit/TODO
@@ -0,0 +1,19 @@
+ - Move temp directory name into local test config.
+
+ - Add --show-unsupported, don't show by default?
+
+ - Finish documentation.
+
+ - Optionally use multiprocessing.
+
+ - Support llvmc and ocaml tests.
+
+ - Support valgrind in all configs, and LLVM style valgrind.
+
+ - Provide test suite config for running unit tests.
+
+ - Support a timeout / ulimit.
+
+ - Support "disabling" tests? The advantage of making this distinct from XFAIL
+ is it makes it more obvious that it is a temporary measure (and lit can put
+ in a separate category).
diff --git a/utils/lit/TclUtil.py b/utils/lit/TclUtil.py
new file mode 100644
index 0000000..4a3f345
--- /dev/null
+++ b/utils/lit/TclUtil.py
@@ -0,0 +1,322 @@
+import itertools
+
+from ShCommands import Command, Pipeline
+
+def tcl_preprocess(data):
+ # Tcl has a preprocessing step to replace escaped newlines.
+ i = data.find('\\\n')
+ if i == -1:
+ return data
+
+ # Replace '\\\n' and subsequent whitespace by a single space.
+ n = len(data)
+ str = data[:i]
+ i += 2
+ while i < n and data[i] in ' \t':
+ i += 1
+ return str + ' ' + data[i:]
+
+class TclLexer:
+ """TclLexer - Lex a string into "words", following the Tcl syntax."""
+
+ def __init__(self, data):
+ self.data = tcl_preprocess(data)
+ self.pos = 0
+ self.end = len(self.data)
+
+ def at_end(self):
+ return self.pos == self.end
+
+ def eat(self):
+ c = self.data[self.pos]
+ self.pos += 1
+ return c
+
+ def look(self):
+ return self.data[self.pos]
+
+ def maybe_eat(self, c):
+ """
+ maybe_eat(c) - Consume the character c if it is the next character,
+ returning True if a character was consumed. """
+ if self.data[self.pos] == c:
+ self.pos += 1
+ return True
+ return False
+
+ def escape(self, c):
+ if c == 'a':
+ return '\x07'
+ elif c == 'b':
+ return '\x08'
+ elif c == 'f':
+ return '\x0c'
+ elif c == 'n':
+ return '\n'
+ elif c == 'r':
+ return '\r'
+ elif c == 't':
+ return '\t'
+ elif c == 'v':
+ return '\x0b'
+ elif c in 'uxo':
+ raise ValueError,'Invalid quoted character %r' % c
+ else:
+ return c
+
+ def lex_braced(self):
+ # Lex until whitespace or end of string, the opening brace has already
+ # been consumed.
+
+ str = ''
+ while 1:
+ if self.at_end():
+ raise ValueError,"Unterminated '{' quoted word"
+
+ c = self.eat()
+ if c == '}':
+ break
+ elif c == '{':
+ str += '{' + self.lex_braced() + '}'
+ elif c == '\\' and self.look() in '{}':
+ str += self.eat()
+ else:
+ str += c
+
+ return str
+
+ def lex_quoted(self):
+ str = ''
+
+ while 1:
+ if self.at_end():
+ raise ValueError,"Unterminated '\"' quoted word"
+
+ c = self.eat()
+ if c == '"':
+ break
+ elif c == '\\':
+ if self.at_end():
+ raise ValueError,'Missing quoted character'
+
+ str += self.escape(self.eat())
+ else:
+ str += c
+
+ return str
+
+ def lex_unquoted(self, process_all=False):
+ # Lex until whitespace or end of string.
+ str = ''
+ while not self.at_end():
+ if not process_all:
+ if self.look().isspace() or self.look() == ';':
+ break
+
+ c = self.eat()
+ if c == '\\':
+ if self.at_end():
+ raise ValueError,'Missing quoted character'
+
+ str += self.escape(self.eat())
+ elif c == '[':
+ raise NotImplementedError, ('Command substitution is '
+ 'not supported')
+ elif c == '$' and not self.at_end() and (self.look().isalpha() or
+ self.look() == '{'):
+ raise NotImplementedError, ('Variable substitution is '
+ 'not supported')
+ else:
+ str += c
+
+ return str
+
+ def lex_one_token(self):
+ if self.maybe_eat('"'):
+ return self.lex_quoted()
+ elif self.maybe_eat('{'):
+ # Check for argument substitution.
+ if not self.maybe_eat('*'):
+ return self.lex_braced()
+
+ if not self.maybe_eat('}'):
+ return '*' + self.lex_braced()
+
+ if self.at_end() or self.look().isspace():
+ return '*'
+
+ raise NotImplementedError, "Argument substitution is unsupported"
+ else:
+ return self.lex_unquoted()
+
+ def lex(self):
+ while not self.at_end():
+ c = self.look()
+ if c in ' \t':
+ self.eat()
+ elif c in ';\n':
+ self.eat()
+ yield (';',)
+ else:
+ yield self.lex_one_token()
+
+class TclExecCommand:
+ kRedirectPrefixes1 = ('<', '>')
+ kRedirectPrefixes2 = ('<@', '<<', '2>', '>&', '>>', '>@')
+ kRedirectPrefixes3 = ('2>@', '2>>', '>>&', '>&@')
+ kRedirectPrefixes4 = ('2>@1',)
+
+ def __init__(self, args):
+ self.args = iter(args)
+
+ def lex(self):
+ try:
+ return self.args.next()
+ except StopIteration:
+ return None
+
+ def look(self):
+ next = self.lex()
+ if next is not None:
+ self.args = itertools.chain([next], self.args)
+ return next
+
+ def parse_redirect(self, tok, length):
+ if len(tok) == length:
+ arg = self.lex()
+ if arg is None:
+ raise ValueError,'Missing argument to %r redirection' % tok
+ else:
+ tok,arg = tok[:length],tok[length:]
+
+ if tok[0] == '2':
+ op = (tok[1:],2)
+ else:
+ op = (tok,)
+ return (op, arg)
+
+ def parse_pipeline(self):
+ if self.look() is None:
+ raise ValueError,"Expected at least one argument to exec"
+
+ commands = [Command([],[])]
+ while 1:
+ arg = self.lex()
+ if arg is None:
+ break
+ elif arg == '|':
+ commands.append(Command([],[]))
+ elif arg == '|&':
+ # Write this as a redirect of stderr; it must come first because
+ # stdout may have already been redirected.
+ commands[-1].redirects.insert(0, (('>&',2),'1'))
+ commands.append(Command([],[]))
+ elif arg[:4] in TclExecCommand.kRedirectPrefixes4:
+ commands[-1].redirects.append(self.parse_redirect(arg, 4))
+ elif arg[:3] in TclExecCommand.kRedirectPrefixes3:
+ commands[-1].redirects.append(self.parse_redirect(arg, 3))
+ elif arg[:2] in TclExecCommand.kRedirectPrefixes2:
+ commands[-1].redirects.append(self.parse_redirect(arg, 2))
+ elif arg[:1] in TclExecCommand.kRedirectPrefixes1:
+ commands[-1].redirects.append(self.parse_redirect(arg, 1))
+ else:
+ commands[-1].args.append(arg)
+
+ return Pipeline(commands, False, pipe_err=True)
+
+ def parse(self):
+ ignoreStderr = False
+ keepNewline = False
+
+ # Parse arguments.
+ while 1:
+ next = self.look()
+ if not isinstance(next, str) or next[0] != '-':
+ break
+
+ if next == '--':
+ self.lex()
+ break
+ elif next == '-ignorestderr':
+ ignoreStderr = True
+ elif next == '-keepnewline':
+ keepNewline = True
+ else:
+ raise ValueError,"Invalid exec argument %r" % next
+
+ return (ignoreStderr, keepNewline, self.parse_pipeline())
+
+###
+
+import unittest
+
+class TestTclLexer(unittest.TestCase):
+ def lex(self, str, *args, **kwargs):
+ return list(TclLexer(str, *args, **kwargs).lex())
+
+ def test_preprocess(self):
+ self.assertEqual(tcl_preprocess('a b'), 'a b')
+ self.assertEqual(tcl_preprocess('a\\\nb c'), 'a b c')
+
+ def test_unquoted(self):
+ self.assertEqual(self.lex('a b c'),
+ ['a', 'b', 'c'])
+ self.assertEqual(self.lex(r'a\nb\tc\ '),
+ ['a\nb\tc '])
+ self.assertEqual(self.lex(r'a \\\$b c $\\'),
+ ['a', r'\$b', 'c', '$\\'])
+
+ def test_braced(self):
+ self.assertEqual(self.lex('a {b c} {}'),
+ ['a', 'b c', ''])
+ self.assertEqual(self.lex(r'a {b {c\n}}'),
+ ['a', 'b {c\\n}'])
+ self.assertEqual(self.lex(r'a {b\{}'),
+ ['a', 'b{'])
+ self.assertEqual(self.lex(r'{*}'), ['*'])
+ self.assertEqual(self.lex(r'{*} a'), ['*', 'a'])
+ self.assertEqual(self.lex(r'{*} a'), ['*', 'a'])
+ self.assertEqual(self.lex('{a\\\n b}'),
+ ['a b'])
+
+ def test_quoted(self):
+ self.assertEqual(self.lex('a "b c"'),
+ ['a', 'b c'])
+
+ def test_terminators(self):
+ self.assertEqual(self.lex('a\nb'),
+ ['a', (';',), 'b'])
+ self.assertEqual(self.lex('a;b'),
+ ['a', (';',), 'b'])
+ self.assertEqual(self.lex('a ; b'),
+ ['a', (';',), 'b'])
+
+class TestTclExecCommand(unittest.TestCase):
+ def parse(self, str):
+ return TclExecCommand(list(TclLexer(str).lex())).parse()
+
+ def test_basic(self):
+ self.assertEqual(self.parse('echo hello'),
+ (False, False,
+ Pipeline([Command(['echo', 'hello'], [])],
+ False, True)))
+ self.assertEqual(self.parse('echo hello | grep hello'),
+ (False, False,
+ Pipeline([Command(['echo', 'hello'], []),
+ Command(['grep', 'hello'], [])],
+ False, True)))
+
+ def test_redirect(self):
+ self.assertEqual(self.parse('echo hello > a >b >>c 2> d |& e'),
+ (False, False,
+ Pipeline([Command(['echo', 'hello'],
+ [(('>&',2),'1'),
+ (('>',),'a'),
+ (('>',),'b'),
+ (('>>',),'c'),
+ (('>',2),'d')]),
+ Command(['e'], [])],
+ False, True)))
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/utils/lit/Test.py b/utils/lit/Test.py
new file mode 100644
index 0000000..d3f6274
--- /dev/null
+++ b/utils/lit/Test.py
@@ -0,0 +1,71 @@
+import os
+
+# Test results.
+
+class TestResult:
+ def __init__(self, name, isFailure):
+ self.name = name
+ self.isFailure = 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)
+
+# Test classes.
+
+class TestFormat:
+ """TestFormat - Test information provider."""
+
+ def __init__(self, name):
+ self.name = name
+
+class TestSuite:
+ """TestSuite - Information on a group of tests.
+
+ A test suite groups together a set of logically related tests.
+ """
+
+ def __init__(self, name, source_root, exec_root, config):
+ self.name = name
+ self.source_root = source_root
+ self.exec_root = exec_root
+ # The test suite configuration.
+ self.config = config
+
+ def getSourcePath(self, components):
+ return os.path.join(self.source_root, *components)
+
+ def getExecPath(self, components):
+ return os.path.join(self.exec_root, *components)
+
+class Test:
+ """Test - Information on a single test instance."""
+
+ def __init__(self, suite, path_in_suite, config):
+ self.suite = suite
+ self.path_in_suite = path_in_suite
+ self.config = config
+ # The test result code, 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
+
+ def setResult(self, result, output, elapsed):
+ assert self.result is None, "Test result already set!"
+ self.result = result
+ self.output = output
+ self.elapsed = elapsed
+
+ def getFullName(self):
+ return self.suite.config.name + '::' + '/'.join(self.path_in_suite)
+
+ def getSourcePath(self):
+ return self.suite.getSourcePath(self.path_in_suite)
+
+ def getExecPath(self):
+ return self.suite.getExecPath(self.path_in_suite)
diff --git a/utils/lit/TestFormats.py b/utils/lit/TestFormats.py
new file mode 100644
index 0000000..61bdb18
--- /dev/null
+++ b/utils/lit/TestFormats.py
@@ -0,0 +1,144 @@
+import os
+
+import Test
+import TestRunner
+import Util
+
+class GoogleTest(object):
+ def __init__(self, test_sub_dir, test_suffix):
+ self.test_sub_dir = str(test_sub_dir)
+ self.test_suffix = str(test_suffix)
+
+ def getGTestTests(self, path):
+ """getGTestTests(path) - [name]
+
+ Return the tests available in gtest executable."""
+
+ lines = Util.capture([path, '--gtest_list_tests']).split('\n')
+ 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
+
+ def getTestsInDirectory(self, testSuite, path_in_suite,
+ litConfig, localConfig):
+ source_path = testSuite.getSourcePath(path_in_suite)
+ for filename in os.listdir(source_path):
+ # Check for the one subdirectory (build directory) tests will be in.
+ if filename != self.test_sub_dir:
+ continue
+
+ filepath = os.path.join(source_path, filename)
+ for subfilename in os.listdir(filepath):
+ if subfilename.endswith(self.test_suffix):
+ execpath = os.path.join(filepath, subfilename)
+
+ # Discover the tests in this executable.
+ for name in self.getGTestTests(execpath):
+ testPath = path_in_suite + (filename, subfilename, name)
+ yield Test.Test(testSuite, testPath, localConfig)
+
+ def execute(self, test, litConfig):
+ testPath,testName = os.path.split(test.getSourcePath())
+
+ cmd = [testPath, '--gtest_filter=' + testName]
+ out, err, exitCode = TestRunner.executeCommand(cmd)
+
+ if not exitCode:
+ return Test.PASS,''
+
+ return 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):
+ 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 Test.Test(testSuite, path_in_suite + (filename,),
+ localConfig)
+
+class ShTest(FileBasedTest):
+ def __init__(self, execute_external = False, require_and_and = False):
+ self.execute_external = execute_external
+ self.require_and_and = require_and_and
+
+ def execute(self, test, litConfig):
+ return TestRunner.executeShTest(test, litConfig,
+ self.execute_external,
+ self.require_and_and)
+
+class TclTest(FileBasedTest):
+ def execute(self, test, litConfig):
+ return TestRunner.executeTclTest(test, litConfig)
+
+###
+
+import re
+import tempfile
+
+class SyntaxCheckTest:
+ # FIXME: Refactor into generic test for running some command on a directory
+ # of inputs.
+
+ def __init__(self, compiler, dir, recursive, pattern, extra_cxx_args=[]):
+ self.compiler = str(compiler)
+ self.dir = str(dir)
+ self.recursive = bool(recursive)
+ self.pattern = re.compile(pattern)
+ self.extra_cxx_args = list(extra_cxx_args)
+
+ def getTestsInDirectory(self, testSuite, path_in_suite,
+ litConfig, localConfig):
+ for dirname,subdirs,filenames in os.walk(self.dir):
+ if not self.recursive:
+ subdirs[:] = []
+
+ for filename in filenames:
+ if (not self.pattern.match(filename) or
+ filename in localConfig.excludes):
+ continue
+
+ path = os.path.join(dirname,filename)
+ suffix = path[len(self.dir):]
+ if suffix.startswith(os.sep):
+ suffix = suffix[1:]
+ test = Test.Test(testSuite,
+ path_in_suite + tuple(suffix.split(os.sep)),
+ localConfig)
+ # FIXME: Hack?
+ test.source_path = path
+ yield test
+
+ def execute(self, test, litConfig):
+ tmp = tempfile.NamedTemporaryFile(suffix='.cpp')
+ print >>tmp, '#include "%s"' % test.source_path
+ tmp.flush()
+
+ cmd = [self.compiler, '-x', 'c++', '-fsyntax-only', tmp.name]
+ cmd.extend(self.extra_cxx_args)
+ out, err, exitCode = TestRunner.executeCommand(cmd)
+
+ diags = out + err
+ if not exitCode and not diags.strip():
+ return Test.PASS,''
+
+ return Test.FAIL, diags
diff --git a/utils/lit/TestRunner.py b/utils/lit/TestRunner.py
new file mode 100644
index 0000000..7b549ac
--- /dev/null
+++ b/utils/lit/TestRunner.py
@@ -0,0 +1,505 @@
+import os, signal, subprocess, sys
+import StringIO
+
+import ShUtil
+import Test
+import Util
+
+import platform
+import tempfile
+
+class InternalShellError(Exception):
+ def __init__(self, command, message):
+ self.command = command
+ self.message = message
+
+# Don't use close_fds on Windows.
+kUseCloseFDs = 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)
+ 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 == ';':
+ res = executeShCmd(cmd.lhs, cfg, cwd, results)
+ return executeShCmd(cmd.rhs, cfg, cwd, results)
+
+ if cmd.op == '&':
+ raise NotImplementedError,"unsupported test command: '&'"
+
+ if cmd.op == '||':
+ res = executeShCmd(cmd.lhs, cfg, cwd, results)
+ if res != 0:
+ res = executeShCmd(cmd.rhs, cfg, cwd, results)
+ return res
+ if cmd.op == '&&':
+ res = executeShCmd(cmd.lhs, cfg, cwd, results)
+ if res is None:
+ return res
+
+ if res == 0:
+ res = executeShCmd(cmd.rhs, cfg, cwd, results)
+ return res
+
+ raise ValueError,'Unknown shell command: %r' % cmd.op
+
+ assert isinstance(cmd, ShUtil.Pipeline)
+ procs = []
+ input = subprocess.PIPE
+ stderrTempFiles = []
+ # To avoid deadlock, we use a single stderr stream for piped
+ # output. This is null until we have seen some output using
+ # stderr.
+ for i,j in enumerate(cmd.commands):
+ redirects = [(0,), (1,), (2,)]
+ for r in j.redirects:
+ if r[0] == ('>',2):
+ redirects[2] = [r[1], 'w', None]
+ elif r[0] == ('>&',2) and r[1] in '012':
+ redirects[2] = redirects[int(r[1])]
+ elif r[0] == ('>&',) or r[0] == ('&>',):
+ redirects[1] = redirects[2] = [r[1], 'w', None]
+ elif r[0] == ('>',):
+ redirects[1] = [r[1], 'w', None]
+ elif r[0] == ('<',):
+ redirects[0] = [r[1], 'r', None]
+ else:
+ raise NotImplementedError,"Unsupported redirect: %r" % (r,)
+
+ final_redirects = []
+ for index,r in enumerate(redirects):
+ if r == (0,):
+ result = input
+ elif r == (1,):
+ if index == 0:
+ raise NotImplementedError,"Unsupported redirect for stdin"
+ elif index == 1:
+ result = subprocess.PIPE
+ else:
+ result = subprocess.STDOUT
+ elif r == (2,):
+ if index != 2:
+ raise NotImplementedError,"Unsupported redirect on stdout"
+ result = subprocess.PIPE
+ else:
+ if r[2] is None:
+ r[2] = open(r[0], r[1])
+ result = r[2]
+ final_redirects.append(result)
+
+ stdin, stdout, stderr = final_redirects
+
+ # If stderr wants to come from stdout, but stdout isn't a pipe, then put
+ # stderr on a pipe and treat it as stdout.
+ if (stderr == subprocess.STDOUT and stdout != subprocess.PIPE):
+ stderr = subprocess.PIPE
+ stderrIsStdout = True
+ else:
+ stderrIsStdout = False
+
+ # Don't allow stderr on a PIPE except for the last
+ # process, this could deadlock.
+ #
+ # FIXME: This is slow, but so is deadlock.
+ if stderr == subprocess.PIPE and j != cmd.commands[-1]:
+ stderr = tempfile.TemporaryFile(mode='w+b')
+ stderrTempFiles.append((i, stderr))
+
+ # Resolve the executable path ourselves.
+ args = list(j.args)
+ args[0] = Util.which(args[0], cfg.environment['PATH'])
+ if not args[0]:
+ raise InternalShellError(j, '%r: command not found' % j.args[0])
+
+ procs.append(subprocess.Popen(args, cwd=cwd,
+ stdin = stdin,
+ stdout = stdout,
+ stderr = stderr,
+ env = cfg.environment,
+ close_fds = kUseCloseFDs))
+
+ # Immediately close stdin for any process taking stdin from us.
+ if stdin == subprocess.PIPE:
+ procs[-1].stdin.close()
+ procs[-1].stdin = None
+
+ # Update the current stdin source.
+ if stdout == subprocess.PIPE:
+ input = procs[-1].stdout
+ elif stderrIsStdout:
+ input = procs[-1].stderr
+ else:
+ input = subprocess.PIPE
+
+ # FIXME: There is probably still deadlock potential here. Yawn.
+ procData = [None] * len(procs)
+ procData[-1] = procs[-1].communicate()
+
+ for i in range(len(procs) - 1):
+ if procs[i].stdout is not None:
+ out = procs[i].stdout.read()
+ else:
+ out = ''
+ if procs[i].stderr is not None:
+ err = procs[i].stderr.read()
+ else:
+ err = ''
+ procData[i] = (out,err)
+
+ # Read stderr out of the temp files.
+ for i,f in stderrTempFiles:
+ f.seek(0, 0)
+ procData[i] = (procData[i][0], f.read())
+
+ exitCode = None
+ for i,(out,err) in enumerate(procData):
+ res = procs[i].wait()
+ # Detect Ctrl-C in subprocess.
+ if res == -signal.SIGINT:
+ raise KeyboardInterrupt
+
+ results.append((cmd.commands[i], out, err, res))
+ if cmd.pipe_err:
+ # Python treats the exit code as a signed char.
+ if res < 0:
+ exitCode = min(exitCode, res)
+ else:
+ exitCode = max(exitCode, res)
+ else:
+ exitCode = res
+
+ if cmd.negate:
+ exitCode = not exitCode
+
+ return exitCode
+
+def executeScriptInternal(test, litConfig, tmpBase, commands, cwd):
+ ln = ' &&\n'.join(commands)
+ try:
+ cmd = ShUtil.ShParser(ln, litConfig.isWindows).parse()
+ except:
+ return (Test.FAIL, "shell parser error on: %r" % ln)
+
+ results = []
+ try:
+ exitCode = executeShCmd(cmd, test.config, cwd, results)
+ except InternalShellError,e:
+ out = ''
+ err = e.message
+ exitCode = 255
+
+ out = err = ''
+ for i,(cmd, cmd_out,cmd_err,res) in enumerate(results):
+ out += 'Command %d: %s\n' % (i, ' '.join('"%s"' % s for s in cmd.args))
+ out += 'Command %d Result: %r\n' % (i, res)
+ out += 'Command %d Output:\n%s\n\n' % (i, cmd_out)
+ out += 'Command %d Stderr:\n%s\n\n' % (i, cmd_err)
+
+ return out, err, exitCode
+
+def executeTclScriptInternal(test, litConfig, tmpBase, commands, cwd):
+ import TclUtil
+ cmds = []
+ for ln in commands:
+ # Given the unfortunate way LLVM's test are written, the line gets
+ # backslash substitution done twice.
+ ln = TclUtil.TclLexer(ln).lex_unquoted(process_all = True)
+
+ try:
+ tokens = list(TclUtil.TclLexer(ln).lex())
+ except:
+ return (Test.FAIL, "Tcl lexer error on: %r" % ln)
+
+ # Validate there are no control tokens.
+ for t in tokens:
+ if not isinstance(t, str):
+ return (Test.FAIL,
+ "Invalid test line: %r containing %r" % (ln, t))
+
+ try:
+ cmds.append(TclUtil.TclExecCommand(tokens).parse_pipeline())
+ except:
+ return (Test.FAIL, "Tcl 'exec' parse error on: %r" % ln)
+
+ cmd = cmds[0]
+ for c in cmds[1:]:
+ cmd = ShUtil.Seq(cmd, '&&', c)
+
+ if litConfig.useTclAsSh:
+ script = tmpBase + '.script'
+
+ # Write script file
+ f = open(script,'w')
+ print >>f, 'set -o pipefail'
+ cmd.toShell(f, pipefail = True)
+ f.close()
+
+ if 0:
+ print >>sys.stdout, cmd
+ print >>sys.stdout, open(script).read()
+ print >>sys.stdout
+ return '', '', 0
+
+ command = ['/bin/bash', script]
+ out,err,exitCode = executeCommand(command, cwd=cwd,
+ env=test.config.environment)
+
+ # Tcl commands fail on standard error output.
+ if err:
+ exitCode = 1
+ out = 'Command has output on stderr!\n\n' + out
+
+ return out,err,exitCode
+ else:
+ results = []
+ try:
+ exitCode = executeShCmd(cmd, test.config, cwd, results)
+ except InternalShellError,e:
+ results.append((e.command, '', e.message + '\n', 255))
+ exitCode = 255
+
+ out = err = ''
+
+ # Tcl commands fail on standard error output.
+ if [True for _,_,err,res in results if err]:
+ exitCode = 1
+ out += 'Command has output on stderr!\n\n'
+
+ for i,(cmd, cmd_out, cmd_err, res) in enumerate(results):
+ out += 'Command %d: %s\n' % (i, ' '.join('"%s"' % s for s in cmd.args))
+ out += 'Command %d Result: %r\n' % (i, res)
+ out += 'Command %d Output:\n%s\n\n' % (i, cmd_out)
+ out += 'Command %d Stderr:\n%s\n\n' % (i, cmd_err)
+
+ return out, err, exitCode
+
+def executeScript(test, litConfig, tmpBase, commands, cwd):
+ script = tmpBase + '.script'
+ if litConfig.isWindows:
+ script += '.bat'
+
+ # Write script file
+ f = open(script,'w')
+ if litConfig.isWindows:
+ f.write('\nif %ERRORLEVEL% NEQ 0 EXIT\n'.join(commands))
+ else:
+ f.write(' &&\n'.join(commands))
+ f.write('\n')
+ f.close()
+
+ if litConfig.isWindows:
+ command = ['cmd','/c', script]
+ else:
+ command = ['/bin/sh', script]
+ if litConfig.useValgrind:
+ # FIXME: Running valgrind on sh is overkill. We probably could just
+ # run on clang with no real loss.
+ valgrindArgs = ['valgrind', '-q',
+ '--tool=memcheck', '--trace-children=yes',
+ '--error-exitcode=123']
+ valgrindArgs.extend(litConfig.valgrindArgs)
+
+ command = valgrindArgs + command
+
+ return executeCommand(command, cwd=cwd, env=test.config.environment)
+
+def parseIntegratedTestScript(test, xfailHasColon, requireAndAnd):
+ """parseIntegratedTestScript - Scan an LLVM/Clang style integrated test
+ script and extract the lines to 'RUN' as well as 'XFAIL' and 'XTARGET'
+ information. The RUN lines also will have variable substitution performed.
+ """
+
+ # Get the temporary location, this is always relative to the test suite
+ # root, not test source root.
+ #
+ # FIXME: This should not be here?
+ sourcepath = test.getSourcePath()
+ execpath = test.getExecPath()
+ execdir,execbase = os.path.split(execpath)
+ tmpBase = os.path.join(execdir, 'Output', execbase)
+
+ # We use #_MARKER_# to hide %% while we do the other substitutions.
+ substitutions = [('%%', '#_MARKER_#')]
+ substitutions.extend(test.config.substitutions)
+ substitutions.extend([('%s', sourcepath),
+ ('%S', os.path.dirname(sourcepath)),
+ ('%p', os.path.dirname(sourcepath)),
+ ('%t', tmpBase + '.tmp'),
+ # FIXME: Remove this once we kill DejaGNU.
+ ('%abs_tmp', tmpBase + '.tmp'),
+ ('#_MARKER_#', '%')])
+
+ # Collect the test lines from the script.
+ script = []
+ xfails = []
+ xtargets = []
+ for ln in open(sourcepath):
+ if 'RUN:' in ln:
+ # Isolate the command to run.
+ index = ln.index('RUN:')
+ ln = ln[index+4:]
+
+ # Trim trailing whitespace.
+ ln = ln.rstrip()
+
+ # Collapse lines with trailing '\\'.
+ if script and script[-1][-1] == '\\':
+ script[-1] = script[-1][:-1] + ln
+ else:
+ script.append(ln)
+ elif xfailHasColon and 'XFAIL:' in ln:
+ items = ln[ln.index('XFAIL:') + 6:].split(',')
+ xfails.extend([s.strip() for s in items])
+ elif not xfailHasColon and 'XFAIL' in ln:
+ items = ln[ln.index('XFAIL') + 5:].split(',')
+ xfails.extend([s.strip() for s in items])
+ elif 'XTARGET:' in ln:
+ items = ln[ln.index('XTARGET:') + 8:].split(',')
+ xtargets.extend([s.strip() for s in items])
+ elif 'END.' in ln:
+ # Check for END. lines.
+ if ln[ln.index('END.'):].strip() == 'END.':
+ break
+
+ # Apply substitutions to the script.
+ def processLine(ln):
+ # Apply substitutions
+ for a,b in substitutions:
+ ln = ln.replace(a,b)
+
+ # Strip the trailing newline and any extra whitespace.
+ return ln.strip()
+ script = map(processLine, script)
+
+ # Verify the script contains a run line.
+ if not script:
+ return (Test.UNRESOLVED, "Test has no run line!")
+
+ if script[-1][-1] == '\\':
+ return (Test.UNRESOLVED, "Test has unterminated run lines (with '\\')")
+
+ # Validate interior lines for '&&', a lovely historical artifact.
+ if requireAndAnd:
+ for i in range(len(script) - 1):
+ ln = script[i]
+
+ if not ln.endswith('&&'):
+ return (Test.FAIL,
+ ("MISSING \'&&\': %s\n" +
+ "FOLLOWED BY : %s\n") % (ln, script[i + 1]))
+
+ # Strip off '&&'
+ script[i] = ln[:-2]
+
+ return script,xfails,xtargets,tmpBase,execdir
+
+def formatTestOutput(status, out, err, exitCode, script):
+ output = StringIO.StringIO()
+ print >>output, "Script:"
+ print >>output, "--"
+ print >>output, '\n'.join(script)
+ print >>output, "--"
+ print >>output, "Exit Code: %r" % exitCode
+ print >>output, "Command Output (stdout):"
+ print >>output, "--"
+ output.write(out)
+ print >>output, "--"
+ print >>output, "Command Output (stderr):"
+ print >>output, "--"
+ output.write(err)
+ print >>output, "--"
+ return (status, output.getvalue())
+
+def executeTclTest(test, litConfig):
+ if test.config.unsupported:
+ return (Test.UNSUPPORTED, 'Test is unsupported')
+
+ res = parseIntegratedTestScript(test, True, False)
+ if len(res) == 2:
+ return res
+
+ script, xfails, xtargets, tmpBase, execdir = res
+
+ if litConfig.noExecute:
+ return (Test.PASS, '')
+
+ # Create the output directory if it does not already exist.
+ Util.mkdir_p(os.path.dirname(tmpBase))
+
+ res = executeTclScriptInternal(test, litConfig, tmpBase, script, execdir)
+ if len(res) == 2:
+ return res
+
+ isXFail = False
+ for item in xfails:
+ if item == '*' or item in test.suite.config.target_triple:
+ isXFail = True
+ break
+
+ # If this is XFAIL, see if it is expected to pass on this target.
+ if isXFail:
+ for item in xtargets:
+ if item == '*' or item in test.suite.config.target_triple:
+ isXFail = False
+ break
+
+ out,err,exitCode = res
+ if isXFail:
+ ok = exitCode != 0
+ status = (Test.XPASS, Test.XFAIL)[ok]
+ else:
+ ok = exitCode == 0
+ status = (Test.FAIL, Test.PASS)[ok]
+
+ if ok:
+ return (status,'')
+
+ return formatTestOutput(status, out, err, exitCode, script)
+
+def executeShTest(test, litConfig, useExternalSh, requireAndAnd):
+ if test.config.unsupported:
+ return (Test.UNSUPPORTED, 'Test is unsupported')
+
+ res = parseIntegratedTestScript(test, False, requireAndAnd)
+ if len(res) == 2:
+ return res
+
+ script, xfails, xtargets, tmpBase, execdir = res
+
+ if litConfig.noExecute:
+ return (Test.PASS, '')
+
+ # Create the output directory if it does not already exist.
+ 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:
+ return res
+
+ out,err,exitCode = res
+ if xfails:
+ ok = exitCode != 0
+ status = (Test.XPASS, Test.XFAIL)[ok]
+ else:
+ ok = exitCode == 0
+ status = (Test.FAIL, Test.PASS)[ok]
+
+ if ok:
+ return (status,'')
+
+ return formatTestOutput(status, out, err, exitCode, script)
diff --git a/utils/lit/TestingConfig.py b/utils/lit/TestingConfig.py
new file mode 100644
index 0000000..e4874d7
--- /dev/null
+++ b/utils/lit/TestingConfig.py
@@ -0,0 +1,96 @@
+import os
+
+class TestingConfig:
+ """"
+ TestingConfig - Information on the tests inside a suite.
+ """
+
+ @staticmethod
+ def frompath(path, parent, litConfig, mustExist, config = None):
+ if config is None:
+ # Set the environment based on the command line arguments.
+ environment = {
+ 'PATH' : os.pathsep.join(litConfig.path +
+ [os.environ.get('PATH','')]),
+ 'SYSTEMROOT' : os.environ.get('SYSTEMROOT',''),
+ 'LLVM_DISABLE_CRT_DEBUG' : '1',
+ }
+
+ 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 = [])
+
+ 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:
+ exec f in cfg_globals
+ except SystemExit,status:
+ # We allow normal system exit inside a config file to just
+ # return control without error.
+ if status.args:
+ raise
+ f.close()
+ elif mustExist:
+ litConfig.fatal('unable to load config from %r ' % path)
+
+ config.finish(litConfig)
+ return config
+
+ def __init__(self, parent, name, suffixes, test_format,
+ environment, substitutions, unsupported, on_clone,
+ test_exec_root, test_source_root, excludes):
+ self.parent = parent
+ self.name = str(name)
+ self.suffixes = set(suffixes)
+ self.test_format = test_format
+ 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)
+
+ 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)
+ 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."""
+
+ self.name = str(self.name)
+ self.suffixes = set(self.suffixes)
+ self.environment = dict(self.environment)
+ self.substitutions = list(self.substitutions)
+ if self.test_exec_root is not None:
+ # FIXME: This should really only be suite in test suite config
+ # files. Should we distinguish them?
+ self.test_exec_root = str(self.test_exec_root)
+ if self.test_source_root is not None:
+ # FIXME: This should really only be suite in test suite config
+ # files. Should we distinguish them?
+ self.test_source_root = str(self.test_source_root)
+ self.excludes = set(self.excludes)
diff --git a/utils/lit/Util.py b/utils/lit/Util.py
new file mode 100644
index 0000000..e62a8ed
--- /dev/null
+++ b/utils/lit/Util.py
@@ -0,0 +1,124 @@
+import os, sys
+
+def detectCPUs():
+ """
+ Detects the number of CPUs on a system. Cribbed from pp.
+ """
+ # Linux, Unix and MacOS:
+ if hasattr(os, "sysconf"):
+ if os.sysconf_names.has_key("SC_NPROCESSORS_ONLN"):
+ # Linux & Unix:
+ ncpus = os.sysconf("SC_NPROCESSORS_ONLN")
+ if isinstance(ncpus, int) and ncpus > 0:
+ return ncpus
+ else: # OSX:
+ return int(os.popen2("sysctl -n hw.ncpu")[1].read())
+ # Windows:
+ if os.environ.has_key("NUMBER_OF_PROCESSORS"):
+ ncpus = int(os.environ["NUMBER_OF_PROCESSORS"]);
+ if ncpus > 0:
+ return ncpus
+ return 1 # Default
+
+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
+
+ parent = os.path.dirname(path)
+ if parent != path:
+ mkdir_p(parent)
+
+ try:
+ os.mkdir(path)
+ except OSError,e:
+ # Ignore EEXIST, which may occur during a race condition.
+ if e.errno != errno.EEXIST:
+ raise
+
+def capture(args):
+ 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)
+ out,_ = p.communicate()
+ return out
+
+def which(command, paths = None):
+ """which(command, [paths]) - Look up the given command in the paths string
+ (or the PATH environment variable, if unspecified)."""
+
+ if paths is None:
+ paths = os.environ.get('PATH','')
+
+ # Check for absolute match first.
+ if os.path.exists(command):
+ return command
+
+ # Would be nice if Python had a lib function for this.
+ if not paths:
+ paths = os.defpath
+
+ # Get suffixes to search.
+ pathext = os.environ.get('PATHEXT', '').split(os.pathsep)
+
+ # Search the paths...
+ for path in paths.split(os.pathsep):
+ for ext in pathext:
+ p = os.path.join(path, command + ext)
+ if os.path.exists(p):
+ return p
+
+ return None
+
+def printHistogram(items, title = 'Items'):
+ import itertools, math
+
+ items.sort(key = lambda (_,v): v)
+
+ maxValue = max([v for _,v in items])
+
+ # Select first "nice" bar height that produces more than 10 bars.
+ power = int(math.ceil(math.log(maxValue, 10)))
+ for inc in itertools.cycle((5, 2, 2.5, 1)):
+ barH = inc * 10**power
+ N = int(math.ceil(maxValue / barH))
+ if N > 10:
+ break
+ elif inc == 1:
+ power -= 1
+
+ histo = [set() for i in range(N)]
+ for name,v in items:
+ bin = min(int(N * v/maxValue), N-1)
+ histo[bin].add(name)
+
+ barW = 40
+ hr = '-' * (barW + 34)
+ print '\nSlowest %s:' % title
+ print hr
+ for name,value in items[-20:]:
+ print '%.2fs: %s' % (value, name)
+ print '\n%s Times:' % title
+ print hr
+ pDigits = int(math.ceil(math.log(maxValue, 10)))
+ pfDigits = max(0, 3-pDigits)
+ if pfDigits:
+ pDigits += pfDigits + 1
+ cDigits = int(math.ceil(math.log(len(items), 10)))
+ print "[%s] :: [%s] :: [%s]" % ('Range'.center((pDigits+1)*2 + 3),
+ 'Percentage'.center(barW),
+ 'Count'.center(cDigits*2 + 1))
+ print hr
+ for i,row in enumerate(histo):
+ pct = float(len(row)) / len(items)
+ w = int(barW * pct)
+ print "[%*.*fs,%*.*fs)" % (pDigits, pfDigits, i*barH,
+ pDigits, pfDigits, (i+1)*barH),
+ print ":: [%s%s] :: [%*d/%*d]" % ('*'*w, ' '*(barW-w),
+ cDigits, len(row),
+ cDigits, len(items))
+
diff --git a/utils/lit/lit.py b/utils/lit/lit.py
new file mode 100755
index 0000000..5b24286
--- /dev/null
+++ b/utils/lit/lit.py
@@ -0,0 +1,531 @@
+#!/usr/bin/env python
+
+"""
+lit - LLVM Integrated Tester.
+
+See lit.pod for more information.
+"""
+
+import math, os, platform, random, re, sys, time, threading, traceback
+
+import ProgressBar
+import TestRunner
+import Util
+
+from TestingConfig import TestingConfig
+import LitConfig
+import Test
+
+# FIXME: Rename to 'config.lit', 'site.lit', and 'local.lit' ?
+kConfigName = 'lit.cfg'
+kSiteConfigName = 'lit.site.cfg'
+kLocalConfigName = 'lit.local.cfg'
+
+class TestingProgressDisplay:
+ 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()
+ elif self.opts.quiet:
+ pass
+ elif self.opts.succinct:
+ sys.stdout.write('\n')
+
+ def handleUpdate(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:
+ return
+
+ if self.progressBar:
+ self.progressBar.clear()
+
+ print '%s: %s (%d of %d)' % (test.result.name, test.getFullName(),
+ self.completed, self.numTests)
+
+ if test.result.isFailure and self.opts.showOutput:
+ print "%s TEST '%s' FAILED %s" % ('*'*20, test.getFullName(),
+ '*'*20)
+ print test.output
+ print "*" * 20
+
+ 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()
+
+ 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()
+ try:
+ item = self.iter.next()
+ except StopIteration:
+ 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 = 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 dirContainsTestSuite(path):
+ cfgpath = os.path.join(path, kSiteConfigName)
+ if os.path.exists(cfgpath):
+ return cfgpath
+ cfgpath = os.path.join(path, kConfigName)
+ if os.path.exists(cfgpath):
+ return cfgpath
+
+def getTestSuite(item, litConfig, cache):
+ """getTestSuite(item, litConfig, cache) -> (suite, relative_path)
+
+ Find the test suite containing @arg item.
+
+ @retval (None, ...) - Indicates no test suite contains @arg item.
+ @retval (suite, relative_path) - The suite that @arg item is in, and its
+ relative path inside that suite.
+ """
+ def search1(path):
+ # Check for a site config or a lit config.
+ cfgpath = dirContainsTestSuite(path)
+
+ # If we didn't find a config file, keep looking.
+ if not cfgpath:
+ parent,base = os.path.split(path)
+ if parent == path:
+ return (None, ())
+
+ ts, relative = search(parent)
+ return (ts, relative + (base,))
+
+ # We found a config file, load it.
+ if litConfig.debug:
+ litConfig.note('loading suite config %r' % cfgpath)
+
+ cfg = TestingConfig.frompath(cfgpath, None, litConfig, mustExist = True)
+ 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), ()
+
+ def search(path):
+ # Check for an already instantiated test suite.
+ res = cache.get(path)
+ if res is None:
+ cache[path] = res = search1(path)
+ return res
+
+ # Canonicalize the path.
+ item = os.path.realpath(item)
+
+ # Skip files and virtual components.
+ components = []
+ while not os.path.isdir(item):
+ parent,base = os.path.split(item)
+ if parent == item:
+ return (None, ())
+ components.append(base)
+ item = parent
+ components.reverse()
+
+ ts, relative = search(item)
+ return ts, tuple(relative + tuple(components))
+
+def getLocalConfig(ts, path_in_suite, litConfig, cache):
+ def search1(path_in_suite):
+ # Get the parent config.
+ if not path_in_suite:
+ parent = ts.config
+ else:
+ parent = search(path_in_suite[:-1])
+
+ # Load the local configuration.
+ source_path = ts.getSourcePath(path_in_suite)
+ cfgpath = os.path.join(source_path, kLocalConfigName)
+ if litConfig.debug:
+ litConfig.note('loading local config %r' % cfgpath)
+ return TestingConfig.frompath(cfgpath, parent, litConfig,
+ mustExist = False,
+ config = parent.clone(cfgpath))
+
+ def search(path_in_suite):
+ key = (ts, path_in_suite)
+ res = cache.get(key)
+ if res is None:
+ cache[key] = res = search1(path_in_suite)
+ return res
+
+ return search(path_in_suite)
+
+def getTests(path, litConfig, testSuiteCache, localConfigCache):
+ # Find the test suite for this input and its relative path.
+ ts,path_in_suite = getTestSuite(path, litConfig, testSuiteCache)
+ if ts is None:
+ litConfig.warning('unable to find test suite for %r' % path)
+ return ()
+
+ if litConfig.debug:
+ litConfig.note('resolved input %r to %r::%r' % (path, ts.name,
+ path_in_suite))
+
+ return getTestsInSuite(ts, path_in_suite, litConfig,
+ testSuiteCache, localConfigCache)
+
+def getTestsInSuite(ts, path_in_suite, litConfig,
+ testSuiteCache, localConfigCache):
+ # Check that the source path exists (errors here are reported by the
+ # caller).
+ source_path = ts.getSourcePath(path_in_suite)
+ if not os.path.exists(source_path):
+ return
+
+ # Check if the user named a test directly.
+ if not os.path.isdir(source_path):
+ lc = getLocalConfig(ts, path_in_suite[:-1], litConfig, localConfigCache)
+ yield Test.Test(ts, path_in_suite, lc)
+ return
+
+ # Otherwise we have a directory to search for tests, start by getting the
+ # local configuration.
+ lc = getLocalConfig(ts, path_in_suite, litConfig, localConfigCache)
+
+ # Search for tests.
+ for res in lc.test_format.getTestsInDirectory(ts, path_in_suite,
+ litConfig, lc):
+ yield res
+
+ # Search subdirectories.
+ for filename in os.listdir(source_path):
+ # FIXME: This doesn't belong here?
+ if filename in ('Output', '.svn') or filename in lc.excludes:
+ continue
+
+ # Ignore non-directories.
+ file_sourcepath = os.path.join(source_path, filename)
+ if not os.path.isdir(file_sourcepath):
+ continue
+
+ # Check for nested test suites, first in the execpath in case there is a
+ # site configuration and then in the source path.
+ file_execpath = ts.getExecPath(path_in_suite + (filename,))
+ if dirContainsTestSuite(file_execpath):
+ subiter = getTests(file_execpath, litConfig,
+ testSuiteCache, localConfigCache)
+ elif dirContainsTestSuite(file_sourcepath):
+ subiter = getTests(file_sourcepath, litConfig,
+ testSuiteCache, localConfigCache)
+ else:
+ # Otherwise, continue loading from inside this test suite.
+ subiter = getTestsInSuite(ts, path_in_suite + (filename,),
+ litConfig, testSuiteCache,
+ localConfigCache)
+
+ for res in subiter:
+ yield res
+
+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()
+ try:
+ for t in testers:
+ t.join()
+ except KeyboardInterrupt:
+ sys.exit(2)
+
+def main():
+ global options
+ from optparse import OptionParser, OptionGroup
+ parser = OptionParser("usage: %prog [options] {file-or-path}")
+
+ parser.add_option("-j", "--threads", dest="numThreads", metavar="N",
+ help="Number of testing threads",
+ type=int, action="store", default=None)
+
+ group = OptionGroup(parser, "Output Format")
+ # FIXME: I find these names very confusing, although I like the
+ # functionality.
+ group.add_option("-q", "--quiet", dest="quiet",
+ help="Suppress no error output",
+ action="store_true", default=False)
+ group.add_option("-s", "--succinct", dest="succinct",
+ help="Reduce amount of output",
+ action="store_true", default=False)
+ group.add_option("-v", "--verbose", dest="showOutput",
+ help="Show all test output",
+ action="store_true", default=False)
+ group.add_option("", "--no-progress-bar", dest="useProgressBar",
+ help="Do not use curses based progress bar",
+ action="store_false", default=True)
+ parser.add_option_group(group)
+
+ group = OptionGroup(parser, "Test Execution")
+ group.add_option("", "--path", dest="path",
+ help="Additional paths to add to testing environment",
+ action="append", type=str, default=[])
+ group.add_option("", "--vg", dest="useValgrind",
+ help="Run tests under valgrind",
+ action="store_true", default=False)
+ group.add_option("", "--vg-arg", dest="valgrindArgs", metavar="ARG",
+ help="Specify an extra argument for valgrind",
+ type=str, action="append", default=[])
+ group.add_option("", "--time-tests", dest="timeTests",
+ help="Track elapsed wall time for each test",
+ action="store_true", default=False)
+ group.add_option("", "--no-execute", dest="noExecute",
+ help="Don't execute any tests (assume PASS)",
+ action="store_true", default=False)
+ parser.add_option_group(group)
+
+ group = OptionGroup(parser, "Test Selection")
+ group.add_option("", "--max-tests", dest="maxTests", metavar="N",
+ help="Maximum number of tests to run",
+ action="store", type=int, default=None)
+ group.add_option("", "--max-time", dest="maxTime", metavar="N",
+ help="Maximum time to spend testing (in seconds)",
+ action="store", type=float, default=None)
+ group.add_option("", "--shuffle", dest="shuffle",
+ help="Run tests in random order",
+ action="store_true", default=False)
+ parser.add_option_group(group)
+
+ group = OptionGroup(parser, "Debug and Experimental Options")
+ group.add_option("", "--debug", dest="debug",
+ help="Enable debugging (for 'lit' development)",
+ action="store_true", default=False)
+ group.add_option("", "--show-suites", dest="showSuites",
+ help="Show discovered test suites",
+ action="store_true", default=False)
+ group.add_option("", "--no-tcl-as-sh", dest="useTclAsSh",
+ help="Don't run Tcl scripts using 'sh'",
+ action="store_false", default=True)
+ parser.add_option_group(group)
+
+ (opts, args) = parser.parse_args()
+
+ if not args:
+ parser.error('No inputs specified')
+
+ if opts.numThreads is None:
+ opts.numThreads = Util.detectCPUs()
+
+ inputs = args
+
+ # Create the global config object.
+ litConfig = LitConfig.LitConfig(progname = os.path.basename(sys.argv[0]),
+ path = opts.path,
+ quiet = opts.quiet,
+ useValgrind = opts.useValgrind,
+ valgrindArgs = opts.valgrindArgs,
+ useTclAsSh = opts.useTclAsSh,
+ noExecute = opts.noExecute,
+ debug = opts.debug,
+ isWindows = (platform.system()=='Windows'))
+
+ # Load the tests from the inputs.
+ tests = []
+ testSuiteCache = {}
+ localConfigCache = {}
+ for input in inputs:
+ prev = len(tests)
+ tests.extend(getTests(input, litConfig,
+ testSuiteCache, localConfigCache))
+ if prev == len(tests):
+ litConfig.warning('input %r contained no tests' % input)
+
+ # If there were any errors during test discovery, exit now.
+ if litConfig.numErrors:
+ print >>sys.stderr, '%d errors, exiting.' % litConfig.numErrors
+ sys.exit(2)
+
+ if opts.showSuites:
+ suitesAndTests = dict([(ts,[])
+ for ts,_ in testSuiteCache.values()])
+ for t in tests:
+ suitesAndTests[t.suite].append(t)
+
+ print '-- Test Suites --'
+ suitesAndTests = suitesAndTests.items()
+ suitesAndTests.sort(key = lambda (ts,_): ts.name)
+ for ts,tests in suitesAndTests:
+ print ' %s - %d tests' %(ts.name, len(tests))
+ print ' Source Root: %s' % ts.source_root
+ print ' Exec Root : %s' % ts.exec_root
+
+ # Select and order the tests.
+ numTotalTests = len(tests)
+ if opts.shuffle:
+ random.shuffle(tests)
+ else:
+ tests.sort(key = lambda t: t.getFullName())
+ if opts.maxTests is not None:
+ tests = tests[:opts.maxTests]
+
+ extra = ''
+ if len(tests) != numTotalTests:
+ extra = ' of %d' % numTotalTests
+ header = '-- Testing: %d%s tests, %d threads --'%(len(tests),extra,
+ opts.numThreads)
+
+ progressBar = None
+ if not opts.quiet:
+ if opts.succinct and opts.useProgressBar:
+ try:
+ tc = ProgressBar.TerminalController()
+ progressBar = ProgressBar.ProgressBar(tc, header)
+ except ValueError:
+ print header
+ progressBar = ProgressBar.SimpleProgressBar('Testing: ')
+ else:
+ print header
+
+ # Don't create more threads than tests.
+ opts.numThreads = min(len(tests), opts.numThreads)
+
+ startTime = time.time()
+ display = TestingProgressDisplay(opts, len(tests), progressBar)
+ provider = TestProvider(tests, opts.maxTime)
+ runTests(opts.numThreads, litConfig, provider, display)
+ display.finish()
+
+ if not opts.quiet:
+ print 'Testing Time: %.2fs'%(time.time() - startTime)
+
+ # Update results for any tests which weren't run.
+ for t in tests:
+ if t.result is None:
+ t.setResult(Test.UNRESOLVED, '', 0.0)
+
+ # 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:
+ hasFailures = True
+
+ # FIXME: Show unresolved and (optionally) unsupported tests.
+ for title,code in (('Unexpected Passing Tests', Test.XPASS),
+ ('Failing Tests', Test.FAIL)):
+ elts = byCode.get(code)
+ if not elts:
+ continue
+ print '*'*20
+ print '%s (%d):' % (title, len(elts))
+ for t in elts:
+ print ' %s' % t.getFullName()
+ print
+
+ if opts.timeTests:
+ byTime = list(tests)
+ byTime.sort(key = lambda t: t.elapsed)
+ if byTime:
+ Util.printHistogram([(t.getFullName(), t.elapsed) for t in byTime],
+ title='Tests')
+
+ for name,code in (('Expected Passes ', Test.PASS),
+ ('Expected Failures ', Test.XFAIL),
+ ('Unsupported Tests ', Test.UNSUPPORTED),
+ ('Unresolved Tests ', Test.UNRESOLVED),
+ ('Unexpected Passes ', Test.XPASS),
+ ('Unexpected Failures', Test.FAIL),):
+ if opts.quiet and not code.isFailure:
+ continue
+ N = len(byCode.get(code,[]))
+ if N:
+ print ' %s: %d' % (name,N)
+
+ # If we encountered any additional errors, exit abnormally.
+ if litConfig.numErrors:
+ print >>sys.stderr, '\n%d error(s), exiting.' % litConfig.numErrors
+ sys.exit(2)
+
+ # Warn about warnings.
+ if litConfig.numWarnings:
+ print >>sys.stderr, '\n%d warning(s) in tests.' % litConfig.numWarnings
+
+ if hasFailures:
+ sys.exit(1)
+ sys.exit(0)
+
+if __name__=='__main__':
+ # 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.
+ import sys
+ sys.setcheckinterval(1000)
+ main()
diff --git a/utils/llvm.grm b/utils/llvm.grm
index 2ca849d..86a707a 100644
--- a/utils/llvm.grm
+++ b/utils/llvm.grm
@@ -53,8 +53,8 @@ FPVAL ::= ESAPINTVAL ^ "." ^ EUAPINTVAL | "0x" ^ HexDigitSeq ;
The rest of this file is derived directly from llvmAsmParser.y.
*)
-ArithmeticOps ::= add | fadd | sub | fsub | mul | fmul |
- udiv | sdiv | fdiv | urem | srem | frem ;
+ArithmeticOps ::= + OptNW add | fadd | OptNW sub | fsub | OptNW mul | fmul |
+ udiv | OptExact sdiv | fdiv | urem | srem | frem ;
LogicalOps ::= shl | lshr | ashr | and | or | xor;
CastOps ::= trunc | zext | sext | fptrunc | fpext | bitcast |
uitofp | sitofp | fptoui | fptosi | inttoptr | ptrtoint ;
@@ -89,6 +89,7 @@ GVInternalLinkage
| appending
| dllexport
| common
+ | private
;
GVExternalLinkage
@@ -160,6 +161,7 @@ FuncAttr ::= noreturn
| signext
| readnone
| readonly
+ | inlinehint
| noinline
| alwaysinline
| optsize
@@ -232,14 +234,12 @@ ConstVal::= Types "[" ^ ConstVector ^ "]"
| Types FPVAL ;
ConstExpr::= CastOps "(" ^ ConstVal to Types ^ ")"
- | getelementptr "(" ^ ConstVal IndexList ^ ")"
+ | getelementptr OptInBounds "(" ^ ConstVal IndexList ^ ")"
| select "(" ^ ConstVal ^ "," ConstVal ^ "," ConstVal ^ ")"
| ArithmeticOps "(" ^ ConstVal ^ "," ConstVal ^ ")"
| LogicalOps "(" ^ ConstVal ^ "," ConstVal ^ ")"
| icmp IPredicates "(" ^ ConstVal ^ "," ConstVal ^ ")"
| fcmp FPredicates "(" ^ ConstVal ^ "," ConstVal ^ ")"
- | vicmp IPredicates "(" ^ ConstVal ^ "," ConstVal ^ ")"
- | vfcmp FPredicates "(" ^ ConstVal ^ "," ConstVal ^ ")"
| extractelement "(" ^ ConstVal ^ "," ConstVal ^ ")"
| insertelement "(" ^ ConstVal ^ "," ConstVal ^ "," ConstVal ^ ")"
| shufflevector "(" ^ ConstVal ^ "," ConstVal ^ "," ConstVal ^ ")"
@@ -382,8 +382,6 @@ InstVal ::=
| LogicalOps Types ValueRef ^ "," ValueRef
| icmp IPredicates Types ValueRef ^ "," ValueRef
| fcmp FPredicates Types ValueRef ^ "," ValueRef
- | vicmp IPredicates Types ValueRef ^ "," ValueRef
- | vfcmp FPredicates Types ValueRef ^ "," ValueRef
| CastOps ResolvedVal to Types
| select ResolvedVal ^ "," ResolvedVal ^ "," ResolvedVal
| "va_arg" ResolvedVal ^ "," Types
@@ -396,6 +394,11 @@ InstVal ::=
| MemoryInst ;
OptVolatile ::= - volatile | _ ;
+OptExact ::= - exact | _ ;
+OptNSW ::= - nsw | _ ;
+OptNUW ::= - nuw | _ ;
+OptNW ::= OptNUW OptNSW ;
+OptInBounds ::= - inbounds | _ ;
MemoryInst ::= malloc Types OptCAlign
| malloc Types ^ "," INTTYPE ValueRef OptCAlign
@@ -405,6 +408,6 @@ MemoryInst ::= malloc Types OptCAlign
| OptVolatile load Types ValueRef OptCAlign
| OptVolatile store ResolvedVal ^ "," Types ValueRef OptCAlign
| getresult Types ValueRef ^ "," EUINT64VAL
- | getelementptr Types ValueRef IndexList
+ | getelementptr OptInBounds Types ValueRef IndexList
| extractvalue Types ValueRef ^ ConstantIndexList
| insertvalue Types ValueRef ^ "," Types ValueRef ^ ConstantIndexList ;
diff --git a/utils/llvmdo b/utils/llvmdo
index b666f22..26f2183 100755
--- a/utils/llvmdo
+++ b/utils/llvmdo
@@ -53,7 +53,7 @@ if test "$1" = "-dirs" ; then
LLVMDO_DIRS="$2"
shift ; shift
elif test -z "$LLVMDO_DIRS" ; then
- LLVMDO_DIRS="include lib tools utils runtime autoconf docs test examples projects"
+ LLVMDO_DIRS="include lib tools utils runtime autoconf docs test examples projects cmake"
fi
if test "$1" = "-code-only" ; then
@@ -126,6 +126,7 @@ files_to_match="\
-o -name *.tr \
-o -name *.y \
-o -name Make* \
+ -o -name *.cmake \
-o -name llvmdo \
-o -name llvmgrep \
-o -name check-each-file \
diff --git a/utils/llvmgrep b/utils/llvmgrep
index 7d7355b..540f059 100755
--- a/utils/llvmgrep
+++ b/utils/llvmgrep
@@ -33,7 +33,7 @@ if test -d "$TOPDIR" ; then
*) grep_cmd="egrep -l -n" ;;
esac
./utils/llvmdo -topdir "$TOPDIR" \
- -dirs "include lib tools utils docs examples test projects" $grep_cmd "$*"
+ -dirs "include lib tools utils docs examples test unittests projects cmake" $grep_cmd "$*"
else
echo "Can't find LLVM top directory"
fi
diff --git a/utils/not/CMakeLists.txt b/utils/not/CMakeLists.txt
new file mode 100644
index 0000000..407c82e
--- /dev/null
+++ b/utils/not/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_executable(not
+ not.cpp
+ )
+
+target_link_libraries(not LLVMSystem)
+if( MINGW )
+ target_link_libraries(not imagehlp psapi)
+endif( MINGW )
+if( LLVM_ENABLE_THREADS AND HAVE_LIBPTHREAD )
+ target_link_libraries(not pthread)
+endif()
diff --git a/utils/not/Makefile b/utils/not/Makefile
new file mode 100644
index 0000000..fef4802
--- /dev/null
+++ b/utils/not/Makefile
@@ -0,0 +1,21 @@
+##===- utils/not/Makefile ----------------------------------*- Makefile -*-===##
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+##===----------------------------------------------------------------------===##
+
+LEVEL = ../..
+TOOLNAME = not
+USEDLIBS = LLVMSupport.a LLVMSystem.a
+
+# This tool has no plugins, optimize startup time.
+TOOL_NO_EXPORTS = 1
+
+# Don't install this utility
+NO_INSTALL = 1
+
+include $(LEVEL)/Makefile.common
+
diff --git a/utils/not/not.cpp b/utils/not/not.cpp
new file mode 100644
index 0000000..dd89b8f
--- /dev/null
+++ b/utils/not/not.cpp
@@ -0,0 +1,17 @@
+//===- not.cpp - The 'not' testing tool -----------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/System/Path.h"
+#include "llvm/System/Program.h"
+using namespace llvm;
+
+int main(int argc, const char **argv) {
+ sys::Path Program = sys::Program::FindProgramByName(argv[1]);
+ return !sys::Program::ExecuteAndWait(Program, argv + 1);
+}
diff --git a/utils/unittest/Makefile b/utils/unittest/Makefile
index 2f3e601..6a09341 100644
--- a/utils/unittest/Makefile
+++ b/utils/unittest/Makefile
@@ -8,6 +8,6 @@
##===----------------------------------------------------------------------===##
LEVEL = ../..
-PARALLEL_DIRS = googletest
+PARALLEL_DIRS = googletest UnitTestMain
include $(LEVEL)/Makefile.common
diff --git a/utils/unittest/UnitTestMain/Makefile b/utils/unittest/UnitTestMain/Makefile
new file mode 100644
index 0000000..aadff21
--- /dev/null
+++ b/utils/unittest/UnitTestMain/Makefile
@@ -0,0 +1,21 @@
+##===- utils/unittest/UnitTestMain/Makefile ----------------*- Makefile -*-===##
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+##===----------------------------------------------------------------------===##
+
+LEVEL = ../../..
+
+include $(LEVEL)/Makefile.config
+NO_MISSING_FIELD_INITIALIZERS := $(shell $(CXX) -Wno-missing-field-initializers -fsyntax-only -xc /dev/null 2>/dev/null && echo -Wno-missing-field-initializers)
+NO_VARIADIC_MACROS := $(shell $(CXX) -Wno-variadic-macros -fsyntax-only -xc /dev/null 2>/dev/null && echo -Wno-variadic-macros)
+
+LIBRARYNAME = UnitTestMain
+BUILD_ARCHIVE = 1
+CPP.Flags += -I$(LLVM_SRC_ROOT)/utils/unittest/googletest/include
+CPP.Flags += $(NO_MISSING_FIELD_INITIALIZERS) $(NO_VARIADIC_MACROS)
+
+include $(LEVEL)/Makefile.common
diff --git a/utils/unittest/UnitTestMain/TestMain.cpp b/utils/unittest/UnitTestMain/TestMain.cpp
new file mode 100644
index 0000000..d97dca8
--- /dev/null
+++ b/utils/unittest/UnitTestMain/TestMain.cpp
@@ -0,0 +1,15 @@
+//===--- utils/unittest/UnitTestMain/TestMain.cpp - unittest driver -------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "gtest/gtest.h"
+
+int main(int argc, char **argv) {
+ testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/utils/unittest/googletest/Makefile b/utils/unittest/googletest/Makefile
index d709878..29fe679 100644
--- a/utils/unittest/googletest/Makefile
+++ b/utils/unittest/googletest/Makefile
@@ -8,17 +8,17 @@
##===----------------------------------------------------------------------===##
LEVEL := ../../..
+
include $(LEVEL)/Makefile.config
NO_MISSING_FIELD_INITIALIZERS := $(shell $(CXX) -Wno-missing-field-initializers -fsyntax-only -xc /dev/null 2>/dev/null && echo -Wno-missing-field-initializers)
NO_VARIADIC_MACROS := $(shell $(CXX) -Wno-variadic-macros -fsyntax-only -xc /dev/null 2>/dev/null && echo -Wno-variadic-macros)
-
LIBRARYNAME = GoogleTest
BUILD_ARCHIVE = 1
-CPP.Flags += -I$(LLVM_SRC_ROOT)/utils/unittest/googletest/include/
+CPP.Flags += -I$(LLVM_SRC_ROOT)/utils/unittest/googletest/include
CPP.Flags += $(NO_MISSING_FIELD_INITIALIZERS) $(NO_VARIADIC_MACROS)
-ifeq ($(OS),MingW)
+ifeq ($(HOST_OS),MingW)
CPP.Flags += -DGTEST_OS_WINDOWS=1
endif
diff --git a/utils/unittest/googletest/README.LLVM b/utils/unittest/googletest/README.LLVM
index 2c673cc..e907a5e 100644
--- a/utils/unittest/googletest/README.LLVM
+++ b/utils/unittest/googletest/README.LLVM
@@ -24,3 +24,8 @@ $ perl -pi -e 's|^#include "src/|#include "gtest/internal/|' *.cc
$ rm -f gtest-all.cc gtest_main.cc
$ 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/internal/gtest-internal.h b/utils/unittest/googletest/include/gtest/internal/gtest-internal.h
index 37faaae..242ffea 100644
--- a/utils/unittest/googletest/include/gtest/internal/gtest-internal.h
+++ b/utils/unittest/googletest/include/gtest/internal/gtest-internal.h
@@ -56,6 +56,8 @@
#include <gtest/internal/gtest-filepath.h>
#include <gtest/internal/gtest-type-util.h>
+#include "llvm/Support/raw_os_ostream.h"
+
// Due to C++ preprocessor weirdness, we need double indirection to
// concatenate two tokens when one of them is __LINE__. Writing
//
@@ -92,9 +94,27 @@
// ::operator<<;" in the definition of Message's operator<<. That fix
// doesn't require a helper function, but unfortunately doesn't
// compile with MSVC.
+
+// LLVM INTERNAL CHANGE: To allow operator<< to work with both
+// std::ostreams and LLVM's raw_ostreams, we define a special
+// std::ostream with an implicit conversion to raw_ostream& and stream
+// to that. This causes the compiler to prefer std::ostream overloads
+// but still find raw_ostream& overloads.
+namespace llvm {
+class convertible_fwd_ostream : public std::ostream {
+ std::ostream& os_;
+ raw_os_ostream ros_;
+
+public:
+ convertible_fwd_ostream(std::ostream& os)
+ : std::ostream(os.rdbuf()), os_(os), ros_(*this) {}
+ operator raw_ostream&() { return ros_; }
+};
+}
template <typename T>
inline void GTestStreamToHelper(std::ostream* os, const T& val) {
- *os << val;
+ llvm::convertible_fwd_ostream cos(*os);
+ cos << val;
}
namespace testing {
diff --git a/utils/unittest/googletest/include/gtest/internal/gtest-port.h b/utils/unittest/googletest/include/gtest/internal/gtest-port.h
index 6a1593e..3e49993 100644
--- a/utils/unittest/googletest/include/gtest/internal/gtest-port.h
+++ b/utils/unittest/googletest/include/gtest/internal/gtest-port.h
@@ -185,6 +185,8 @@
#define GTEST_OS_ZOS
#elif defined(__sun) && defined(__SVR4)
#define GTEST_OS_SOLARIS
+#elif defined(__HAIKU__)
+#define GTEST_OS_HAIKU
#endif // _MSC_VER
// Determines whether ::std::string and ::string are available.
@@ -225,7 +227,7 @@
// TODO(wan@google.com): uses autoconf to detect whether ::std::wstring
// is available.
-#if defined(GTEST_OS_CYGWIN) || defined(GTEST_OS_SOLARIS)
+#if defined(GTEST_OS_CYGWIN) || defined(GTEST_OS_SOLARIS) || defined(GTEST_OS_HAIKU)
// At least some versions of cygwin don't support ::std::wstring.
// Solaris' libc++ doesn't support it either.
#define GTEST_HAS_STD_WSTRING 0
diff --git a/utils/valgrind/x86_64-pc-linux-gnu_gcc-4.3.3.supp b/utils/valgrind/x86_64-pc-linux-gnu_gcc-4.3.3.supp
new file mode 100644
index 0000000..a86be6c
--- /dev/null
+++ b/utils/valgrind/x86_64-pc-linux-gnu_gcc-4.3.3.supp
@@ -0,0 +1,23 @@
+{
+ libstdcxx_overlapped_memcpy_in_stable_sort_1
+ Memcheck:Overlap
+ fun:memcpy
+ ...
+ fun:_ZSt11stable_sortIN9__gnu_cxx17__normal_iteratorIPSt4pairIPKN4llvm5ValueEjESt6vectorIS7_SaIS7_EEEEN12_GLOBAL__N_116CstSortPredicateEEvT_SF_T0_
+}
+
+{
+ libstdcxx_overlapped_memcpy_in_stable_sort_2
+ Memcheck:Overlap
+ fun:memcpy
+ ...
+ fun:_ZSt11stable_sortIN9__gnu_cxx17__normal_iteratorIPSt4pairIPKN4llvm5ValueEjESt6vectorIS7_SaIS7_EEEEN12_GLOBAL__N_116CstSortPredicateEEvT_SF_T0_
+}
+
+{
+ libstdcxx_overlapped_memcpy_in_stable_sort_3
+ Memcheck:Overlap
+ fun:memcpy
+ ...
+ fun:_ZSt11stable_sortIN9__gnu_cxx17__normal_iteratorIPSt4pairIPKN4llvm4TypeEjESt6vectorIS7_SaIS7_EEEEPFbRKS7_SE_EEvT_SH_T0_
+}
diff --git a/utils/vim/llvm.vim b/utils/vim/llvm.vim
index b4104f9..2cc266b 100644
--- a/utils/vim/llvm.vim
+++ b/utils/vim/llvm.vim
@@ -29,6 +29,7 @@ syn keyword llvmStatement icmp fcmp
syn keyword llvmStatement eq ne ugt uge ult ule sgt sge slt sle
syn keyword llvmStatement oeq ogt oge olt ole one ord ueq ugt uge
syn keyword llvmStatement ult ule une uno
+syn keyword llvmStatement nuw nsw exact inbounds
syn keyword llvmStatement phi call select shl lshr ashr va_arg
syn keyword llvmStatement trunc zext sext
syn keyword llvmStatement fptrunc fpext fptoui fptosi uitofp sitofp
@@ -40,7 +41,7 @@ syn keyword llvmStatement extractvalue insertvalue
" Keywords.
syn keyword llvmKeyword define declare global constant
-syn keyword llvmKeyword internal external
+syn keyword llvmKeyword internal external private
syn keyword llvmKeyword linkonce linkonce_odr weak weak_odr appending
syn keyword llvmKeyword common extern_weak
syn keyword llvmKeyword thread_local dllimport dllexport
@@ -49,8 +50,9 @@ syn keyword llvmKeyword except deplibs
syn keyword llvmKeyword volatile fastcc coldcc cc ccc
syn keyword llvmKeyword x86_stdcallcc x86_fastcallcc
syn keyword llvmKeyword signext zeroext inreg sret nounwind noreturn
-syn keyword llvmKeyword nocapture byval nest readnone readonly
-syn keyword llvmKeyword noinline alwaysinline optsize ssp sspreq
+syn keyword llvmKeyword nocapture byval nest readnone readonly noalias
+syn keyword llvmKeyword inlinehint noinline alwaysinline optsize ssp sspreq
+syn keyword llvmKeyword noredzone noimplicitfloat naked
syn keyword llvmKeyword module asm align tail to
syn keyword llvmKeyword addrspace section alias sideeffect c gc
syn keyword llvmKeyword target datalayout triple
OpenPOWER on IntegriCloud