diff options
Diffstat (limited to 'utils')
37 files changed, 1214 insertions, 423 deletions
diff --git a/utils/FileCheck/FileCheck.cpp b/utils/FileCheck/FileCheck.cpp index 07294a3..8d5af58 100644 --- a/utils/FileCheck/FileCheck.cpp +++ b/utils/FileCheck/FileCheck.cpp @@ -115,6 +115,9 @@ public: void PrintFailureInfo(const SourceMgr &SM, StringRef Buffer, const StringMap<StringRef> &VariableTable) const; + bool hasVariable() const { return !(VariableUses.empty() && + VariableDefs.empty()); } + void setMatchNot(bool Not) { MatchNot = Not; } bool getMatchNot() const { return MatchNot; } @@ -594,17 +597,21 @@ struct CheckString { /// to a CHECK: directive. bool IsCheckNext; + /// IsCheckLabel - This is true if this is a CHECK-LABEL: directive (as + /// opposed to a CHECK: directive. + bool IsCheckLabel; + /// DagNotStrings - These are all of the strings that are disallowed from /// occurring between this match string and the previous one (or start of /// file). std::vector<Pattern> DagNotStrings; - CheckString(const Pattern &P, SMLoc L, bool isCheckNext) - : Pat(P), Loc(L), IsCheckNext(isCheckNext) {} + CheckString(const Pattern &P, SMLoc L, bool isCheckNext, bool isCheckLabel) + : Pat(P), Loc(L), IsCheckNext(isCheckNext), IsCheckLabel(isCheckLabel) {} /// Check - Match check string and its "not strings" and/or "dag strings". - size_t Check(const SourceMgr &SM, StringRef Buffer, size_t &MatchLen, - StringMap<StringRef> &VariableTable) const; + size_t Check(const SourceMgr &SM, StringRef Buffer, bool IsLabel, + size_t &MatchLen, StringMap<StringRef> &VariableTable) const; /// CheckNext - Verify there is a single line in the given buffer. bool CheckNext(const SourceMgr &SM, StringRef Buffer) const; @@ -667,7 +674,7 @@ static bool ReadCheckFile(SourceMgr &SM, std::vector<CheckString> &CheckStrings) { OwningPtr<MemoryBuffer> File; if (error_code ec = - MemoryBuffer::getFileOrSTDIN(CheckFilename.c_str(), File)) { + MemoryBuffer::getFileOrSTDIN(CheckFilename, File)) { errs() << "Could not open check file '" << CheckFilename << "': " << ec.message() << '\n'; return true; @@ -703,7 +710,8 @@ static bool ReadCheckFile(SourceMgr &SM, // When we find a check prefix, keep track of whether we find CHECK: or // CHECK-NEXT: - bool IsCheckNext = false, IsCheckNot = false, IsCheckDag = false; + bool IsCheckNext = false, IsCheckNot = false, IsCheckDag = false, + IsCheckLabel = false; // Verify that the : is present after the prefix. if (Buffer[CheckPrefix.size()] == ':') { @@ -720,6 +728,10 @@ static bool ReadCheckFile(SourceMgr &SM, memcmp(Buffer.data()+CheckPrefix.size(), "-DAG:", 5) == 0) { Buffer = Buffer.substr(CheckPrefix.size()+5); IsCheckDag = true; + } else if (Buffer.size() > CheckPrefix.size()+7 && + memcmp(Buffer.data()+CheckPrefix.size(), "-LABEL:", 7) == 0) { + Buffer = Buffer.substr(CheckPrefix.size()+7); + IsCheckLabel = true; } else { Buffer = Buffer.substr(1); continue; @@ -740,6 +752,15 @@ static bool ReadCheckFile(SourceMgr &SM, if (P.ParsePattern(Buffer.substr(0, EOL), SM, LineNumber)) return true; + // Verify that CHECK-LABEL lines do not define or use variables + if (IsCheckLabel && P.hasVariable()) { + SM.PrintMessage(SMLoc::getFromPointer(CheckPrefixStart), + SourceMgr::DK_Error, + "found '"+CheckPrefix+"-LABEL:' with variable definition" + " or use'"); + return true; + } + P.setMatchNot(IsCheckNot); P.setMatchDag(IsCheckDag); @@ -763,7 +784,8 @@ static bool ReadCheckFile(SourceMgr &SM, // Okay, add the string we captured to the output vector and move on. CheckStrings.push_back(CheckString(P, PatternLoc, - IsCheckNext)); + IsCheckNext, + IsCheckLabel)); std::swap(DagNotMatches, CheckStrings.back().DagNotStrings); } @@ -771,6 +793,7 @@ static bool ReadCheckFile(SourceMgr &SM, if (!DagNotMatches.empty()) { CheckStrings.push_back(CheckString(Pattern(true), SMLoc::getFromPointer(Buffer.data()), + false, false)); std::swap(DagNotMatches, CheckStrings.back().DagNotStrings); } @@ -829,15 +852,17 @@ static unsigned CountNumNewlinesBetween(StringRef Range) { } size_t CheckString::Check(const SourceMgr &SM, StringRef Buffer, - size_t &MatchLen, + bool IsLabel, size_t &MatchLen, StringMap<StringRef> &VariableTable) const { size_t LastPos = 0; std::vector<const Pattern *> NotStrings; - // Match "dag strings" (with mixed "not strings" if any). - LastPos = CheckDag(SM, Buffer, NotStrings, VariableTable); - if (LastPos == StringRef::npos) - return StringRef::npos; + if (!IsLabel) { + // Match "dag strings" (with mixed "not strings" if any). + LastPos = CheckDag(SM, Buffer, NotStrings, VariableTable); + if (LastPos == StringRef::npos) + return StringRef::npos; + } // Match itself from the last position after matching CHECK-DAG. StringRef MatchBuffer = Buffer.substr(LastPos); @@ -848,17 +873,19 @@ size_t CheckString::Check(const SourceMgr &SM, StringRef Buffer, } MatchPos += LastPos; - StringRef SkippedRegion = Buffer.substr(LastPos, MatchPos); + if (!IsLabel) { + StringRef SkippedRegion = Buffer.substr(LastPos, MatchPos); - // If this check is a "CHECK-NEXT", verify that the previous match was on - // the previous line (i.e. that there is one newline between them). - if (CheckNext(SM, SkippedRegion)) - return StringRef::npos; + // 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 (CheckNext(SM, SkippedRegion)) + return StringRef::npos; - // If this match had "not strings", verify that they don't exist in the - // skipped region. - if (CheckNot(SM, SkippedRegion, NotStrings, VariableTable)) - return StringRef::npos; + // If this match had "not strings", verify that they don't exist in the + // skipped region. + if (CheckNot(SM, SkippedRegion, NotStrings, VariableTable)) + return StringRef::npos; + } return MatchPos; } @@ -986,8 +1013,7 @@ size_t CheckString::CheckDag(const SourceMgr &SM, StringRef Buffer, // CHECK-DAG, verify that there's no 'not' strings occurred in that // region. StringRef SkippedRegion = Buffer.substr(LastPos, MatchPos); - size_t Pos = CheckNot(SM, SkippedRegion, NotStrings, VariableTable); - if (Pos != StringRef::npos) + if (CheckNot(SM, SkippedRegion, NotStrings, VariableTable)) return StringRef::npos; // Clear "not strings". NotStrings.clear(); @@ -1015,7 +1041,7 @@ int main(int argc, char **argv) { // Open the file to check and add it to SourceMgr. OwningPtr<MemoryBuffer> File; if (error_code ec = - MemoryBuffer::getFileOrSTDIN(InputFilename.c_str(), File)) { + MemoryBuffer::getFileOrSTDIN(InputFilename, File)) { errs() << "Could not open input file '" << InputFilename << "': " << ec.message() << '\n'; return 2; @@ -1040,18 +1066,56 @@ int main(int argc, char **argv) { // file. StringRef Buffer = F->getBuffer(); - for (unsigned StrNo = 0, e = CheckStrings.size(); StrNo != e; ++StrNo) { - const CheckString &CheckStr = CheckStrings[StrNo]; + bool hasError = false; - // Find StrNo in the file. - size_t MatchLen = 0; - size_t MatchPos = CheckStr.Check(SM, Buffer, MatchLen, VariableTable); + unsigned i = 0, j = 0, e = CheckStrings.size(); + + while (true) { + StringRef CheckRegion; + if (j == e) { + CheckRegion = Buffer; + } else { + const CheckString &CheckLabelStr = CheckStrings[j]; + if (!CheckLabelStr.IsCheckLabel) { + ++j; + continue; + } + + // Scan to next CHECK-LABEL match, ignoring CHECK-NOT and CHECK-DAG + size_t MatchLabelLen = 0; + size_t MatchLabelPos = CheckLabelStr.Check(SM, Buffer, true, + MatchLabelLen, VariableTable); + if (MatchLabelPos == StringRef::npos) { + hasError = true; + break; + } - if (MatchPos == StringRef::npos) - return 1; + CheckRegion = Buffer.substr(0, MatchLabelPos + MatchLabelLen); + Buffer = Buffer.substr(MatchLabelPos + MatchLabelLen); + ++j; + } - Buffer = Buffer.substr(MatchPos + MatchLen); + for ( ; i != j; ++i) { + const CheckString &CheckStr = CheckStrings[i]; + + // Check each string within the scanned region, including a second check + // of any final CHECK-LABEL (to verify CHECK-NOT and CHECK-DAG) + size_t MatchLen = 0; + size_t MatchPos = CheckStr.Check(SM, CheckRegion, false, MatchLen, + VariableTable); + + if (MatchPos == StringRef::npos) { + hasError = true; + i = j; + break; + } + + CheckRegion = CheckRegion.substr(MatchPos + MatchLen); + } + + if (j == e) + break; } - return 0; + return hasError ? 1 : 0; } diff --git a/utils/FileUpdate/FileUpdate.cpp b/utils/FileUpdate/FileUpdate.cpp index 9b48f94..fbcd927 100644 --- a/utils/FileUpdate/FileUpdate.cpp +++ b/utils/FileUpdate/FileUpdate.cpp @@ -45,7 +45,7 @@ int main(int argc, char **argv) { // Get the input data. OwningPtr<MemoryBuffer> In; - if (error_code ec = MemoryBuffer::getFileOrSTDIN(InputFilename.c_str(), In)) { + if (error_code ec = MemoryBuffer::getFileOrSTDIN(InputFilename, In)) { errs() << argv[0] << ": error: Unable to get input '" << InputFilename << "': " << ec.message() << '\n'; return 1; @@ -71,7 +71,7 @@ int main(int argc, char **argv) { << "', contents changed.\n"; std::string ErrorStr; tool_output_file OutStream(OutputFilename.c_str(), ErrorStr, - raw_fd_ostream::F_Binary); + sys::fs::F_Binary); if (!ErrorStr.empty()) { errs() << argv[0] << ": Unable to write output '" << OutputFilename << "': " << ErrorStr << '\n'; diff --git a/utils/TableGen/AsmMatcherEmitter.cpp b/utils/TableGen/AsmMatcherEmitter.cpp index 218af21..468ce1c 100644 --- a/utils/TableGen/AsmMatcherEmitter.cpp +++ b/utils/TableGen/AsmMatcherEmitter.cpp @@ -114,6 +114,7 @@ #include <cassert> #include <map> #include <set> +#include <sstream> using namespace llvm; static cl::opt<std::string> @@ -836,9 +837,11 @@ void MatchableInfo::tokenizeAsmString(const AsmMatcherInfo &Info) { } case '.': - if (InTok) - AsmOperands.push_back(AsmOperand(String.slice(Prev, i))); - Prev = i; + if (!Info.AsmParser->getValueAsBit("MnemonicContainsDot")) { + if (InTok) + AsmOperands.push_back(AsmOperand(String.slice(Prev, i))); + Prev = i; + } InTok = true; break; @@ -2066,9 +2069,12 @@ static void emitIsSubclass(CodeGenTarget &Target, OS << " if (A == B)\n"; OS << " return true;\n\n"; - OS << " switch (A) {\n"; - OS << " default:\n"; - OS << " return false;\n"; + std::string OStr; + raw_string_ostream SS(OStr); + unsigned Count = 0; + SS << " switch (A) {\n"; + SS << " default:\n"; + SS << " return false;\n"; for (std::vector<ClassInfo*>::iterator it = Infos.begin(), ie = Infos.end(); it != ie; ++it) { ClassInfo &A = **it; @@ -2084,21 +2090,35 @@ static void emitIsSubclass(CodeGenTarget &Target, if (SuperClasses.empty()) continue; + ++Count; - OS << "\n case " << A.Name << ":\n"; + SS << "\n case " << A.Name << ":\n"; if (SuperClasses.size() == 1) { - OS << " return B == " << SuperClasses.back() << ";\n"; + SS << " return B == " << SuperClasses.back().str() << ";\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"; + if (!SuperClasses.empty()) { + SS << " switch (B) {\n"; + SS << " default: return false;\n"; + for (unsigned i = 0, e = SuperClasses.size(); i != e; ++i) + SS << " case " << SuperClasses[i].str() << ": return true;\n"; + SS << " }\n"; + } else { + // No case statement to emit + SS << " return false;\n"; + } } - OS << " }\n"; + SS << " }\n"; + + // If there were case statements emitted into the string stream, write them + // to the output stream, otherwise write the default. + if (Count) + OS << SS.str(); + else + OS << " return false;\n"; + OS << "}\n\n"; } @@ -2194,18 +2214,24 @@ static void emitOperandDiagnosticTypes(AsmMatcherInfo &Info, raw_ostream &OS) { static void emitGetSubtargetFeatureName(AsmMatcherInfo &Info, raw_ostream &OS) { OS << "// User-level names for subtarget features that participate in\n" << "// instruction matching.\n" - << "static const char *getSubtargetFeatureName(unsigned Val) {\n" - << " switch(Val) {\n"; - for (std::map<Record*, SubtargetFeatureInfo*>::const_iterator - it = Info.SubtargetFeatures.begin(), - ie = Info.SubtargetFeatures.end(); it != ie; ++it) { - SubtargetFeatureInfo &SFI = *it->second; - // FIXME: Totally just a placeholder name to get the algorithm working. - OS << " case " << SFI.getEnumName() << ": return \"" - << SFI.TheDef->getValueAsString("PredicateName") << "\";\n"; + << "static const char *getSubtargetFeatureName(unsigned Val) {\n"; + if (!Info.SubtargetFeatures.empty()) { + OS << " switch(Val) {\n"; + for (std::map<Record*, SubtargetFeatureInfo*>::const_iterator + it = Info.SubtargetFeatures.begin(), + ie = Info.SubtargetFeatures.end(); it != ie; ++it) { + SubtargetFeatureInfo &SFI = *it->second; + // FIXME: Totally just a placeholder name to get the algorithm working. + OS << " case " << SFI.getEnumName() << ": return \"" + << SFI.TheDef->getValueAsString("PredicateName") << "\";\n"; + } + OS << " default: return \"(unknown)\";\n"; + OS << " }\n"; + } else { + // Nothing to emit, so skip the switch + OS << " return \"(unknown)\";\n"; } - OS << " default: return \"(unknown)\";\n"; - OS << " }\n}\n\n"; + OS << "}\n\n"; } /// emitComputeAvailableFeatures - Emit the function to compute the list of @@ -2303,7 +2329,7 @@ static void emitMnemonicAliasVariant(raw_ostream &OS,const AsmMatcherInfo &Info, } if (AliasesFromMnemonic.empty()) return; - + // Process each alias a "from" mnemonic at a time, building the code executed // by the string remapper. std::vector<StringMatcher::StringPair> Cases; @@ -2632,7 +2658,7 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { << "&Operands);\n"; OS << " void convertToMapAndConstraints(unsigned Kind,\n "; OS << " const SmallVectorImpl<MCParsedAsmOperand*> &Operands);\n"; - OS << " bool mnemonicIsValid(StringRef Mnemonic);\n"; + OS << " bool mnemonicIsValid(StringRef Mnemonic, unsigned VariantID);\n"; OS << " unsigned MatchInstructionImpl(\n"; OS.indent(27); OS << "const SmallVectorImpl<MCParsedAsmOperand*> &Operands,\n" @@ -2754,7 +2780,6 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { << " RequiredFeatures;\n"; OS << " " << getMinimalTypeForRange(Info.Classes.size()) << " Classes[" << MaxNumOperands << "];\n"; - OS << " uint8_t AsmVariantID;\n\n"; OS << " StringRef getMnemonic() const {\n"; OS << " return StringRef(MnemonicTable + Mnemonic + 1,\n"; OS << " MnemonicTable[Mnemonic]);\n"; @@ -2776,51 +2801,73 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { OS << "} // end anonymous namespace.\n\n"; - OS << "static const MatchEntry MatchTable[" - << Info.Matchables.size() << "] = {\n"; + unsigned VariantCount = Target.getAsmParserVariantCount(); + for (unsigned VC = 0; VC != VariantCount; ++VC) { + Record *AsmVariant = Target.getAsmParserVariant(VC); + std::string CommentDelimiter = + AsmVariant->getValueAsString("CommentDelimiter"); + std::string RegisterPrefix = AsmVariant->getValueAsString("RegisterPrefix"); + int AsmVariantNo = AsmVariant->getValueAsInt("Variant"); - for (std::vector<MatchableInfo*>::const_iterator it = - Info.Matchables.begin(), ie = Info.Matchables.end(); - it != ie; ++it) { - MatchableInfo &II = **it; + OS << "static const MatchEntry MatchTable" << VC << "[] = {\n"; - // Store a pascal-style length byte in the mnemonic. - std::string LenMnemonic = char(II.Mnemonic.size()) + II.Mnemonic.str(); - OS << " { " << StringTable.GetOrAddStringOffset(LenMnemonic, false) - << " /* " << II.Mnemonic << " */, " - << Target.getName() << "::" - << II.getResultInst()->TheDef->getName() << ", " - << II.ConversionFnKind << ", "; + for (std::vector<MatchableInfo*>::const_iterator it = + Info.Matchables.begin(), ie = Info.Matchables.end(); + it != ie; ++it) { + MatchableInfo &II = **it; + if (II.AsmVariantID != AsmVariantNo) + continue; - // Write the required features mask. - if (!II.RequiredFeatures.empty()) { - for (unsigned i = 0, e = II.RequiredFeatures.size(); i != e; ++i) { - if (i) OS << "|"; - OS << II.RequiredFeatures[i]->getEnumName(); - } - } else - OS << "0"; + // Store a pascal-style length byte in the mnemonic. + std::string LenMnemonic = char(II.Mnemonic.size()) + II.Mnemonic.str(); + OS << " { " << StringTable.GetOrAddStringOffset(LenMnemonic, false) + << " /* " << II.Mnemonic << " */, " + << Target.getName() << "::" + << II.getResultInst()->TheDef->getName() << ", " + << II.ConversionFnKind << ", "; + + // Write the required features mask. + if (!II.RequiredFeatures.empty()) { + for (unsigned i = 0, e = II.RequiredFeatures.size(); i != e; ++i) { + if (i) OS << "|"; + OS << II.RequiredFeatures[i]->getEnumName(); + } + } else + OS << "0"; - OS << ", { "; - for (unsigned i = 0, e = II.AsmOperands.size(); i != e; ++i) { - MatchableInfo::AsmOperand &Op = II.AsmOperands[i]; + OS << ", { "; + for (unsigned i = 0, e = II.AsmOperands.size(); i != e; ++i) { + MatchableInfo::AsmOperand &Op = II.AsmOperands[i]; - if (i) OS << ", "; - OS << Op.Class->Name; + if (i) OS << ", "; + OS << Op.Class->Name; + } + OS << " }, },\n"; } - OS << " }, " << II.AsmVariantID; - OS << "},\n"; - } - OS << "};\n\n"; + OS << "};\n\n"; + } // A method to determine if a mnemonic is in the list. OS << "bool " << Target.getName() << ClassName << "::\n" - << "mnemonicIsValid(StringRef Mnemonic) {\n"; + << "mnemonicIsValid(StringRef Mnemonic, unsigned VariantID) {\n"; + OS << " // Find the appropriate table for this asm variant.\n"; + OS << " const MatchEntry *Start, *End;\n"; + OS << " switch (VariantID) {\n"; + OS << " default: // unreachable\n"; + for (unsigned VC = 0; VC != VariantCount; ++VC) { + Record *AsmVariant = Target.getAsmParserVariant(VC); + std::string CommentDelimiter = + AsmVariant->getValueAsString("CommentDelimiter"); + std::string RegisterPrefix = AsmVariant->getValueAsString("RegisterPrefix"); + int AsmVariantNo = AsmVariant->getValueAsInt("Variant"); + OS << " case " << AsmVariantNo << ": Start = MatchTable" << VC + << "; End = array_endof(MatchTable" << VC << "); break;\n"; + } + OS << " }\n"; OS << " // Search the table.\n"; OS << " std::pair<const MatchEntry*, const MatchEntry*> MnemonicRange =\n"; - OS << " std::equal_range(MatchTable, MatchTable+" - << Info.Matchables.size() << ", Mnemonic, LessOpcode());\n"; + OS << " std::equal_range(Start, End, Mnemonic, LessOpcode());\n"; OS << " return MnemonicRange.first != MnemonicRange.second;\n"; OS << "}\n\n"; @@ -2862,10 +2909,23 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { OS << " ErrorInfo = ~0U;\n"; // Emit code to search the table. + OS << " // Find the appropriate table for this asm variant.\n"; + OS << " const MatchEntry *Start, *End;\n"; + OS << " switch (VariantID) {\n"; + OS << " default: // unreachable\n"; + for (unsigned VC = 0; VC != VariantCount; ++VC) { + Record *AsmVariant = Target.getAsmParserVariant(VC); + std::string CommentDelimiter = + AsmVariant->getValueAsString("CommentDelimiter"); + std::string RegisterPrefix = AsmVariant->getValueAsString("RegisterPrefix"); + int AsmVariantNo = AsmVariant->getValueAsInt("Variant"); + OS << " case " << AsmVariantNo << ": Start = MatchTable" << VC + << "; End = array_endof(MatchTable" << VC << "); break;\n"; + } + OS << " }\n"; OS << " // Search the table.\n"; OS << " std::pair<const MatchEntry*, const MatchEntry*> MnemonicRange =\n"; - OS << " std::equal_range(MatchTable, MatchTable+" - << Info.Matchables.size() << ", Mnemonic, LessOpcode());\n\n"; + OS << " std::equal_range(Start, End, Mnemonic, LessOpcode());\n\n"; OS << " // Return a more specific error code if no mnemonics match.\n"; OS << " if (MnemonicRange.first == MnemonicRange.second)\n"; @@ -2879,7 +2939,6 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { OS << " assert(Mnemonic == it->getMnemonic());\n"; // Emit check that the subclasses match. - OS << " if (VariantID != it->AsmVariantID) continue;\n"; OS << " bool OperandsValid = true;\n"; OS << " for (unsigned i = 0; i != " << MaxNumOperands << "; ++i) {\n"; OS << " if (i + 1 >= Operands.size()) {\n"; diff --git a/utils/TableGen/AsmWriterInst.cpp b/utils/TableGen/AsmWriterInst.cpp index fe1f756..1c2004f 100644 --- a/utils/TableGen/AsmWriterInst.cpp +++ b/utils/TableGen/AsmWriterInst.cpp @@ -32,10 +32,10 @@ std::string AsmWriterOperand::getCode() const { return "O << '" + Str + "'; "; return "O << \"" + Str + "\"; "; } - + if (OperandType == isLiteralStatementOperand) return Str; - + std::string Result = Str + "(MI"; if (MIOpNo != ~0U) Result += ", " + utostr(MIOpNo); @@ -53,12 +53,12 @@ AsmWriterInst::AsmWriterInst(const CodeGenInstruction &CGI, int FirstOperandColumn, int OperandSpacing) { this->CGI = &CGI; - + // 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 + + + // 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)! std::string AsmString = CGI.FlattenAsmStringVariants(CGI.AsmString, Variant); @@ -67,7 +67,7 @@ AsmWriterInst::AsmWriterInst(const CodeGenInstruction &CGI, std::string::size_type DollarPos = AsmString.find_first_of("$\\", LastEmitted); if (DollarPos == std::string::npos) DollarPos = AsmString.size(); - + // Emit a constant string fragment. if (DollarPos != LastEmitted) { for (; LastEmitted != DollarPos; ++LastEmitted) @@ -82,7 +82,7 @@ AsmWriterInst::AsmWriterInst(const CodeGenInstruction &CGI, AddLiteralString("\\t"); } else { // We recognize a tab as an operand delimeter. - unsigned DestColumn = FirstOperandColumn + + unsigned DestColumn = FirstOperandColumn + CurColumn++ * OperandSpacing; Operands.push_back( AsmWriterOperand( @@ -112,15 +112,15 @@ AsmWriterInst::AsmWriterInst(const CodeGenInstruction &CGI, AddLiteralString("\\t"); break; } - + // We recognize a tab as an operand delimeter. - unsigned DestColumn = FirstOperandColumn + + 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]) + } else if (std::string("${|}\\").find(AsmString[DollarPos+1]) != std::string::npos) { AddLiteralString(std::string(1, AsmString[DollarPos+1])); } else { @@ -137,7 +137,7 @@ AsmWriterInst::AsmWriterInst(const CodeGenInstruction &CGI, } 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. @@ -147,17 +147,17 @@ AsmWriterInst::AsmWriterInst(const CodeGenInstruction &CGI, ++DollarPos; ++VarEnd; } - + while (VarEnd < AsmString.size() && isIdentChar(AsmString[VarEnd])) ++VarEnd; std::string VarName(AsmString.begin()+DollarPos+1, AsmString.begin()+VarEnd); - + // Modifier - Support ${foo:modifier} syntax, where "modifier" is passed // into printOperand. Also support ${:feature}, which is passed into // PrintSpecial. std::string Modifier; - + // In order to avoid starting the next string at the terminating curly // brace, advance the end position past it if we found an opening curly // brace. @@ -165,14 +165,14 @@ AsmWriterInst::AsmWriterInst(const CodeGenInstruction &CGI, if (VarEnd >= AsmString.size()) PrintFatalError("Reached end of string before terminating curly brace in '" + CGI.TheDef->getName() + "'"); - + // Look for a modifier string. if (AsmString[VarEnd] == ':') { ++VarEnd; if (VarEnd >= AsmString.size()) PrintFatalError("Reached end of string before terminating curly brace in '" + CGI.TheDef->getName() + "'"); - + unsigned ModifierStart = VarEnd; while (VarEnd < AsmString.size() && isIdentChar(AsmString[VarEnd])) ++VarEnd; @@ -181,7 +181,7 @@ AsmWriterInst::AsmWriterInst(const CodeGenInstruction &CGI, if (Modifier.empty()) PrintFatalError("Bad operand modifier name in '"+ CGI.TheDef->getName() + "'"); } - + if (AsmString[VarEnd] != '}') PrintFatalError("Variable name beginning with '{' did not end with '}' in '" + CGI.TheDef->getName() + "'"); @@ -190,26 +190,26 @@ AsmWriterInst::AsmWriterInst(const CodeGenInstruction &CGI, if (VarName.empty() && Modifier.empty()) PrintFatalError("Stray '$' in '" + CGI.TheDef->getName() + "' asm string, maybe you want $$?"); - + if (VarName.empty()) { // Just a modifier, pass this into PrintSpecial. - Operands.push_back(AsmWriterOperand("PrintSpecial", - ~0U, - ~0U, + Operands.push_back(AsmWriterOperand("PrintSpecial", + ~0U, + ~0U, Modifier)); } else { // Otherwise, normal operand. unsigned OpNo = CGI.Operands.getOperandNamed(VarName); CGIOperandList::OperandInfo OpInfo = CGI.Operands[OpNo]; - + unsigned MIOp = OpInfo.MIOperandNo; - Operands.push_back(AsmWriterOperand(OpInfo.PrinterMethodName, + Operands.push_back(AsmWriterOperand(OpInfo.PrinterMethodName, OpNo, MIOp, Modifier)); } LastEmitted = VarEnd; } } - + Operands.push_back(AsmWriterOperand("return;", AsmWriterOperand::isLiteralStatementOperand)); } @@ -220,14 +220,13 @@ AsmWriterInst::AsmWriterInst(const CodeGenInstruction &CGI, /// if the instructions are identical return ~0. unsigned AsmWriterInst::MatchesAllButOneOp(const AsmWriterInst &Other)const{ if (Operands.size() != Other.Operands.size()) return ~1; - + unsigned MismatchOperand = ~0U; for (unsigned i = 0, e = Operands.size(); i != e; ++i) { if (Operands[i] != Other.Operands[i]) { if (MismatchOperand != ~0U) // Already have one mismatch? return ~1U; - else - MismatchOperand = i; + MismatchOperand = i; } } return MismatchOperand; diff --git a/utils/TableGen/CodeGenDAGPatterns.cpp b/utils/TableGen/CodeGenDAGPatterns.cpp index 8e5bb77..ee025a0 100644 --- a/utils/TableGen/CodeGenDAGPatterns.cpp +++ b/utils/TableGen/CodeGenDAGPatterns.cpp @@ -438,7 +438,7 @@ bool EEVT::TypeSet::EnforceSmallerThan(EEVT::TypeSet &Other, TreePattern &TP) { int OtherIntSize = 0; int OtherFPSize = 0; - for (SmallVector<MVT::SimpleValueType, 2>::iterator TVI = + for (SmallVectorImpl<MVT::SimpleValueType>::iterator TVI = Other.TypeVec.begin(); TVI != Other.TypeVec.end(); /* NULL */) { @@ -496,7 +496,7 @@ bool EEVT::TypeSet::EnforceSmallerThan(EEVT::TypeSet &Other, TreePattern &TP) { int IntSize = 0; int FPSize = 0; - for (SmallVector<MVT::SimpleValueType, 2>::iterator TVI = + for (SmallVectorImpl<MVT::SimpleValueType>::iterator TVI = TypeVec.begin(); TVI != TypeVec.end(); /* NULL */) { diff --git a/utils/TableGen/CodeGenIntrinsics.h b/utils/TableGen/CodeGenIntrinsics.h index f0570f9..ababfa4 100644 --- a/utils/TableGen/CodeGenIntrinsics.h +++ b/utils/TableGen/CodeGenIntrinsics.h @@ -77,7 +77,9 @@ namespace llvm { bool isNoReturn; enum ArgAttribute { - NoCapture + NoCapture, + ReadOnly, + ReadNone }; std::vector<std::pair<unsigned, ArgAttribute> > ArgumentAttributes; diff --git a/utils/TableGen/CodeGenRegisters.cpp b/utils/TableGen/CodeGenRegisters.cpp index daa7eab..43de2be 100644 --- a/utils/TableGen/CodeGenRegisters.cpp +++ b/utils/TableGen/CodeGenRegisters.cpp @@ -12,6 +12,8 @@ // //===----------------------------------------------------------------------===// +#define DEBUG_TYPE "regalloc-emitter" + #include "CodeGenRegisters.h" #include "CodeGenTarget.h" #include "llvm/ADT/IntEqClasses.h" @@ -19,6 +21,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Twine.h" +#include "llvm/Support/Debug.h" #include "llvm/TableGen/Error.h" using namespace llvm; @@ -938,7 +941,7 @@ CodeGenRegBank::CodeGenRegBank(RecordKeeper &Records) { // Read in the register definitions. std::vector<Record*> Regs = Records.getAllDerivedDefinitions("Register"); - std::sort(Regs.begin(), Regs.end(), LessRecord()); + std::sort(Regs.begin(), Regs.end(), LessRecordRegister()); Registers.reserve(Regs.size()); // Assign the enumeration values. for (unsigned i = 0, e = Regs.size(); i != e; ++i) @@ -947,10 +950,16 @@ CodeGenRegBank::CodeGenRegBank(RecordKeeper &Records) { // Expand tuples and number the new registers. std::vector<Record*> Tups = Records.getAllDerivedDefinitions("RegisterTuples"); + + std::vector<Record*> TupRegsCopy; for (unsigned i = 0, e = Tups.size(); i != e; ++i) { const std::vector<Record*> *TupRegs = Sets.expand(Tups[i]); - for (unsigned j = 0, je = TupRegs->size(); j != je; ++j) - getReg((*TupRegs)[j]); + TupRegsCopy.reserve(TupRegs->size()); + TupRegsCopy.assign(TupRegs->begin(), TupRegs->end()); + std::sort(TupRegsCopy.begin(), TupRegsCopy.end(), LessRecordRegister()); + for (unsigned j = 0, je = TupRegsCopy.size(); j != je; ++j) + getReg((TupRegsCopy)[j]); + TupRegsCopy.clear(); } // Now all the registers are known. Build the object graph of explicit @@ -1082,7 +1091,7 @@ CodeGenRegBank::getCompositeSubRegIndex(CodeGenSubRegIndex *A, } CodeGenSubRegIndex *CodeGenRegBank:: -getConcatSubRegIndex(const SmallVector<CodeGenSubRegIndex*, 8> &Parts) { +getConcatSubRegIndex(const SmallVector<CodeGenSubRegIndex *, 8> &Parts) { assert(Parts.size() > 1 && "Need two parts to concatenate"); // Look for an existing entry. @@ -1323,9 +1332,18 @@ static void computeUberWeights(std::vector<UberRegSet> &UberSets, } if (Weight > MaxWeight) MaxWeight = Weight; - - // Update the set weight. - I->Weight = MaxWeight; + if (I->Weight != MaxWeight) { + DEBUG( + dbgs() << "UberSet " << I - UberSets.begin() << " Weight " << MaxWeight; + for (CodeGenRegister::Set::iterator + UnitI = I->Regs.begin(), UnitE = I->Regs.end(); + UnitI != UnitE; ++UnitI) { + dbgs() << " " << (*UnitI)->getName(); + } + dbgs() << "\n"); + // Update the set weight. + I->Weight = MaxWeight; + } // Find singular determinants. for (CodeGenRegister::Set::iterator RegI = I->Regs.begin(), @@ -1452,7 +1470,23 @@ static bool isRegUnitSubSet(const std::vector<unsigned> &RUSubSet, RUSubSet.begin(), RUSubSet.end()); } -// Iteratively prune unit sets. +/// Iteratively prune unit sets. Prune subsets that are close to the superset, +/// but with one or two registers removed. We occasionally have registers like +/// APSR and PC thrown in with the general registers. We also see many +/// special-purpose register subsets, such as tail-call and Thumb +/// encodings. Generating all possible overlapping sets is combinatorial and +/// overkill for modeling pressure. Ideally we could fix this statically in +/// tablegen by (1) having the target define register classes that only include +/// the allocatable registers and marking other classes as non-allocatable and +/// (2) having a way to mark special purpose classes as "don't-care" classes for +/// the purpose of pressure. However, we make an attempt to handle targets that +/// are not nicely defined by merging nearly identical register unit sets +/// statically. This generates smaller tables. Then, dynamically, we adjust the +/// set limit by filtering the reserved registers. +/// +/// Merge sets only if the units have the same weight. For example, on ARM, +/// Q-tuples with ssub index 0 include all S regs but also include D16+. We +/// should not expand the S set to include D regs. void CodeGenRegBank::pruneUnitSets() { assert(RegClassUnitSets.empty() && "this invalidates RegClassUnitSets"); @@ -1466,9 +1500,14 @@ void CodeGenRegBank::pruneUnitSets() { if (SuperIdx == SubIdx) continue; + unsigned UnitWeight = RegUnits[SubSet.Units[0]].Weight; const RegUnitSet &SuperSet = RegUnitSets[SuperIdx]; if (isRegUnitSubSet(SubSet.Units, SuperSet.Units) - && (SubSet.Units.size() + 3 > SuperSet.Units.size())) { + && (SubSet.Units.size() + 3 > SuperSet.Units.size()) + && UnitWeight == RegUnits[SuperSet.Units[0]].Weight + && UnitWeight == RegUnits[SuperSet.Units.back()].Weight) { + DEBUG(dbgs() << "UnitSet " << SubIdx << " subsumed by " << SuperIdx + << "\n"); break; } } @@ -1493,6 +1532,7 @@ void CodeGenRegBank::pruneUnitSets() { // RegisterInfoEmitter will map each RegClass to its RegUnitClass and any // RegUnitSet that is a superset of that RegUnitClass. void CodeGenRegBank::computeRegUnitSets() { + assert(RegUnitSets.empty() && "dirty RegUnitSets"); // Compute a unique RegUnitSet for each RegClass. const ArrayRef<CodeGenRegisterClass*> &RegClasses = getRegClasses(); @@ -1515,9 +1555,32 @@ void CodeGenRegBank::computeRegUnitSets() { RegUnitSets.pop_back(); } + DEBUG(dbgs() << "\nBefore pruning:\n"; + for (unsigned USIdx = 0, USEnd = RegUnitSets.size(); + USIdx < USEnd; ++USIdx) { + dbgs() << "UnitSet " << USIdx << " " << RegUnitSets[USIdx].Name + << ":"; + ArrayRef<unsigned> Units = RegUnitSets[USIdx].Units; + for (unsigned i = 0, e = Units.size(); i < e; ++i) + dbgs() << " " << RegUnits[Units[i]].Roots[0]->getName(); + dbgs() << "\n"; + }); + // Iteratively prune unit sets. pruneUnitSets(); + DEBUG(dbgs() << "\nBefore union:\n"; + for (unsigned USIdx = 0, USEnd = RegUnitSets.size(); + USIdx < USEnd; ++USIdx) { + dbgs() << "UnitSet " << USIdx << " " << RegUnitSets[USIdx].Name + << ":"; + ArrayRef<unsigned> Units = RegUnitSets[USIdx].Units; + for (unsigned i = 0, e = Units.size(); i < e; ++i) + dbgs() << " " << RegUnits[Units[i]].Roots[0]->getName(); + dbgs() << "\n"; + } + dbgs() << "\nUnion sets:\n"); + // Iterate over all unit sets, including new ones added by this loop. unsigned NumRegUnitSubSets = RegUnitSets.size(); for (unsigned Idx = 0, EndIdx = RegUnitSets.size(); Idx != EndIdx; ++Idx) { @@ -1555,12 +1618,31 @@ void CodeGenRegBank::computeRegUnitSets() { findRegUnitSet(RegUnitSets, RegUnitSets.back()); if (SetI != llvm::prior(RegUnitSets.end())) RegUnitSets.pop_back(); + else { + DEBUG(dbgs() << "UnitSet " << RegUnitSets.size()-1 + << " " << RegUnitSets.back().Name << ":"; + ArrayRef<unsigned> Units = RegUnitSets.back().Units; + for (unsigned i = 0, e = Units.size(); i < e; ++i) + dbgs() << " " << RegUnits[Units[i]].Roots[0]->getName(); + dbgs() << "\n";); + } } } // Iteratively prune unit sets after inferring supersets. pruneUnitSets(); + DEBUG(dbgs() << "\n"; + for (unsigned USIdx = 0, USEnd = RegUnitSets.size(); + USIdx < USEnd; ++USIdx) { + dbgs() << "UnitSet " << USIdx << " " << RegUnitSets[USIdx].Name + << ":"; + ArrayRef<unsigned> Units = RegUnitSets[USIdx].Units; + for (unsigned i = 0, e = Units.size(); i < e; ++i) + dbgs() << " " << RegUnits[Units[i]].Roots[0]->getName(); + dbgs() << "\n"; + }); + // For each register class, list the UnitSets that are supersets. RegClassUnitSets.resize(NumRegClasses); for (unsigned RCIdx = 0, RCEnd = NumRegClasses; RCIdx != RCEnd; ++RCIdx) { @@ -1568,19 +1650,27 @@ void CodeGenRegBank::computeRegUnitSets() { continue; // Recompute the sorted list of units in this class. - std::vector<unsigned> RegUnits; - RegClasses[RCIdx]->buildRegUnitSet(RegUnits); + std::vector<unsigned> RCRegUnits; + RegClasses[RCIdx]->buildRegUnitSet(RCRegUnits); // Don't increase pressure for unallocatable regclasses. - if (RegUnits.empty()) + if (RCRegUnits.empty()) continue; + DEBUG(dbgs() << "RC " << RegClasses[RCIdx]->getName() << " Units: \n"; + for (unsigned i = 0, e = RCRegUnits.size(); i < e; ++i) + dbgs() << RegUnits[RCRegUnits[i]].getRoots()[0]->getName() << " "; + dbgs() << "\n UnitSetIDs:"); + // Find all supersets. for (unsigned USIdx = 0, USEnd = RegUnitSets.size(); USIdx != USEnd; ++USIdx) { - if (isRegUnitSubSet(RegUnits, RegUnitSets[USIdx].Units)) + if (isRegUnitSubSet(RCRegUnits, RegUnitSets[USIdx].Units)) { + DEBUG(dbgs() << " " << USIdx); RegClassUnitSets[RCIdx].push_back(USIdx); + } } + DEBUG(dbgs() << "\n"); assert(!RegClassUnitSets[RCIdx].empty() && "missing unit set for regclass"); } @@ -1614,6 +1704,16 @@ void CodeGenRegBank::computeRegUnitSets() { } } +struct LessUnits { + const CodeGenRegBank &RegBank; + LessUnits(const CodeGenRegBank &RB): RegBank(RB) {} + + bool operator()(unsigned ID1, unsigned ID2) { + return RegBank.getRegPressureSet(ID1).Units.size() + < RegBank.getRegPressureSet(ID2).Units.size(); + } +}; + void CodeGenRegBank::computeDerivedInfo() { computeComposites(); computeSubRegIndexLaneMasks(); @@ -1625,6 +1725,21 @@ void CodeGenRegBank::computeDerivedInfo() { // Compute a unique set of RegUnitSets. One for each RegClass and inferred // supersets for the union of overlapping sets. computeRegUnitSets(); + + // Get the weight of each set. + for (unsigned Idx = 0, EndIdx = RegUnitSets.size(); Idx != EndIdx; ++Idx) + RegUnitSets[Idx].Weight = getRegUnitSetWeight(RegUnitSets[Idx].Units); + + // Find the order of each set. + RegUnitSetOrder.reserve(RegUnitSets.size()); + for (unsigned Idx = 0, EndIdx = RegUnitSets.size(); Idx != EndIdx; ++Idx) + RegUnitSetOrder.push_back(Idx); + + std::stable_sort(RegUnitSetOrder.begin(), RegUnitSetOrder.end(), + LessUnits(*this)); + for (unsigned Idx = 0, EndIdx = RegUnitSets.size(); Idx != EndIdx; ++Idx) { + RegUnitSets[RegUnitSetOrder[Idx]].Order = Idx; + } } // diff --git a/utils/TableGen/CodeGenRegisters.h b/utils/TableGen/CodeGenRegisters.h index f9edc65..37f75b4 100644 --- a/utils/TableGen/CodeGenRegisters.h +++ b/utils/TableGen/CodeGenRegisters.h @@ -433,6 +433,10 @@ namespace llvm { std::string Name; std::vector<unsigned> Units; + unsigned Weight; // Cache the sum of all unit weights. + unsigned Order; // Cache the sort key. + + RegUnitSet() : Weight(0), Order(0) {} }; // Base vector for identifying TopoSigs. The contents uniquely identify a @@ -484,6 +488,9 @@ namespace llvm { // already exist for a register class, we create a new entry in this vector. std::vector<std::vector<unsigned> > RegClassUnitSets; + // Give each register unit set an order based on sorting criteria. + std::vector<unsigned> RegUnitSetOrder; + // Add RC to *2RC maps. void addToMaps(CodeGenRegisterClass*); @@ -534,10 +541,10 @@ namespace llvm { // Find or create a sub-register index representing the concatenation of // non-overlapping sibling indices. CodeGenSubRegIndex * - getConcatSubRegIndex(const SmallVector<CodeGenSubRegIndex*, 8>&); + getConcatSubRegIndex(const SmallVector<CodeGenSubRegIndex *, 8>&); void - addConcatSubRegIndex(const SmallVector<CodeGenSubRegIndex*, 8> &Parts, + addConcatSubRegIndex(const SmallVector<CodeGenSubRegIndex *, 8> &Parts, CodeGenSubRegIndex *Idx) { ConcatIdx.insert(std::make_pair(Parts, Idx)); } @@ -622,6 +629,13 @@ namespace llvm { return Weight; } + unsigned getRegSetIDAt(unsigned Order) const { + return RegUnitSetOrder[Order]; + } + const RegUnitSet &getRegSetAt(unsigned Order) const { + return RegUnitSets[RegUnitSetOrder[Order]]; + } + // Increase a RegUnitWeight. void increaseRegUnitWeight(unsigned RUID, unsigned Inc) { getRegUnit(RUID).Weight += Inc; @@ -631,7 +645,7 @@ namespace llvm { unsigned getNumRegPressureSets() const { return RegUnitSets.size(); } // Get a set of register unit IDs for a given dimension of pressure. - RegUnitSet getRegPressureSet(unsigned Idx) const { + const RegUnitSet &getRegPressureSet(unsigned Idx) const { return RegUnitSets[Idx]; } diff --git a/utils/TableGen/CodeGenSchedule.cpp b/utils/TableGen/CodeGenSchedule.cpp index 8015e34..001e97d 100644 --- a/utils/TableGen/CodeGenSchedule.cpp +++ b/utils/TableGen/CodeGenSchedule.cpp @@ -1102,7 +1102,7 @@ void PredTransitions::getIntersectingVariants( TransVariant &Variant = Variants[VIdx]; // Don't expand variants if the processor models don't intersect. // A zero processor index means any processor. - SmallVector<unsigned, 4> &ProcIndices = TransVec[TransIdx].ProcIndices; + SmallVectorImpl<unsigned> &ProcIndices = TransVec[TransIdx].ProcIndices; if (ProcIndices[0] && Variants[VIdx].ProcIdx) { unsigned Cnt = std::count(ProcIndices.begin(), ProcIndices.end(), Variant.ProcIdx); @@ -1476,6 +1476,19 @@ void CodeGenSchedModels::collectProcResources() { Record *ModelDef = (*RAI)->getValueAsDef("SchedModel"); addReadAdvance(*RAI, getProcModel(ModelDef).Index); } + // Add ProcResGroups that are defined within this processor model, which may + // not be directly referenced but may directly specify a buffer size. + RecVec ProcResGroups = Records.getAllDerivedDefinitions("ProcResGroup"); + for (RecIter RI = ProcResGroups.begin(), RE = ProcResGroups.end(); + RI != RE; ++RI) { + if (!(*RI)->getValueInit("SchedModel")->isComplete()) + continue; + CodeGenProcModel &PM = getProcModel((*RI)->getValueAsDef("SchedModel")); + RecIter I = std::find(PM.ProcResourceDefs.begin(), + PM.ProcResourceDefs.end(), *RI); + if (I == PM.ProcResourceDefs.end()) + PM.ProcResourceDefs.push_back(*RI); + } // Finalize each ProcModel by sorting the record arrays. for (unsigned PIdx = 0, PEnd = ProcModels.size(); PIdx != PEnd; ++PIdx) { CodeGenProcModel &PM = ProcModels[PIdx]; diff --git a/utils/TableGen/CodeGenSchedule.h b/utils/TableGen/CodeGenSchedule.h index 2e0a149..fa964cf 100644 --- a/utils/TableGen/CodeGenSchedule.h +++ b/utils/TableGen/CodeGenSchedule.h @@ -266,11 +266,14 @@ public: return ProcModels[I->second]; } - const CodeGenProcModel &getProcModel(Record *ModelDef) const { + CodeGenProcModel &getProcModel(Record *ModelDef) { ProcModelMapTy::const_iterator I = ProcModelMap.find(ModelDef); assert(I != ProcModelMap.end() && "missing machine model"); return ProcModels[I->second]; } + const CodeGenProcModel &getProcModel(Record *ModelDef) const { + return const_cast<CodeGenSchedModels*>(this)->getProcModel(ModelDef); + } // Iterate over the unique processor models. typedef std::vector<CodeGenProcModel>::const_iterator ProcIter; diff --git a/utils/TableGen/CodeGenTarget.cpp b/utils/TableGen/CodeGenTarget.cpp index 8b292b9..b2c883d 100644 --- a/utils/TableGen/CodeGenTarget.cpp +++ b/utils/TableGen/CodeGenTarget.cpp @@ -298,7 +298,7 @@ struct SortInstByName { /// target, ordered by their enum value. void CodeGenTarget::ComputeInstrsByEnum() const { // The ordering here must match the ordering in TargetOpcodes.h. - const char *const FixedInstrs[] = { + static const char *const FixedInstrs[] = { "PHI", "INLINEASM", "PROLOG_LABEL", @@ -552,6 +552,12 @@ CodeGenIntrinsic::CodeGenIntrinsic(Record *R) { else if (Property->isSubClassOf("NoCapture")) { unsigned ArgNo = Property->getValueAsInt("ArgNo"); ArgumentAttributes.push_back(std::make_pair(ArgNo, NoCapture)); + } else if (Property->isSubClassOf("ReadOnly")) { + unsigned ArgNo = Property->getValueAsInt("ArgNo"); + ArgumentAttributes.push_back(std::make_pair(ArgNo, ReadOnly)); + } else if (Property->isSubClassOf("ReadNone")) { + unsigned ArgNo = Property->getValueAsInt("ArgNo"); + ArgumentAttributes.push_back(std::make_pair(ArgNo, ReadNone)); } else llvm_unreachable("Unknown property!"); } diff --git a/utils/TableGen/FixedLenDecoderEmitter.cpp b/utils/TableGen/FixedLenDecoderEmitter.cpp index 0c3017f..6e45240 100644 --- a/utils/TableGen/FixedLenDecoderEmitter.cpp +++ b/utils/TableGen/FixedLenDecoderEmitter.cpp @@ -879,15 +879,20 @@ emitPredicateFunction(formatted_raw_ostream &OS, PredicateSet &Predicates, OS.indent(Indentation) << "static bool checkDecoderPredicate(unsigned Idx, " << "uint64_t Bits) {\n"; Indentation += 2; - OS.indent(Indentation) << "switch (Idx) {\n"; - OS.indent(Indentation) << "default: llvm_unreachable(\"Invalid index!\");\n"; - unsigned Index = 0; - for (PredicateSet::const_iterator I = Predicates.begin(), E = Predicates.end(); - I != E; ++I, ++Index) { - OS.indent(Indentation) << "case " << Index << ":\n"; - OS.indent(Indentation+2) << "return (" << *I << ");\n"; + if (!Predicates.empty()) { + OS.indent(Indentation) << "switch (Idx) {\n"; + OS.indent(Indentation) << "default: llvm_unreachable(\"Invalid index!\");\n"; + unsigned Index = 0; + for (PredicateSet::const_iterator I = Predicates.begin(), E = Predicates.end(); + I != E; ++I, ++Index) { + OS.indent(Indentation) << "case " << Index << ":\n"; + OS.indent(Indentation+2) << "return (" << *I << ");\n"; + } + OS.indent(Indentation) << "}\n"; + } else { + // No case statement to emit + OS.indent(Indentation) << "llvm_unreachable(\"Invalid index!\");\n"; } - OS.indent(Indentation) << "}\n"; Indentation -= 2; OS.indent(Indentation) << "}\n\n"; } diff --git a/utils/TableGen/InstrInfoEmitter.cpp b/utils/TableGen/InstrInfoEmitter.cpp index d6020a8..c8304de 100644 --- a/utils/TableGen/InstrInfoEmitter.cpp +++ b/utils/TableGen/InstrInfoEmitter.cpp @@ -45,11 +45,25 @@ private: void emitEnums(raw_ostream &OS); typedef std::map<std::vector<std::string>, unsigned> OperandInfoMapTy; + + /// The keys of this map are maps which have OpName enum values as their keys + /// and instruction operand indices as their values. The values of this map + /// are lists of instruction names. + typedef std::map<std::map<unsigned, unsigned>, + std::vector<std::string> > OpNameMapTy; + typedef std::map<std::string, unsigned>::iterator StrUintMapIter; void emitRecord(const CodeGenInstruction &Inst, unsigned Num, Record *InstrInfo, std::map<std::vector<Record*>, unsigned> &EL, const OperandInfoMapTy &OpInfo, raw_ostream &OS); + void initOperandMapData( + const std::vector<const CodeGenInstruction *> NumberedInstructions, + const std::string &Namespace, + std::map<std::string, unsigned> &Operands, + OpNameMapTy &OperandMap); + void emitOperandNameMappings(raw_ostream &OS, const CodeGenTarget &Target, + const std::vector<const CodeGenInstruction*> &NumberedInstructions); // Operand information. void EmitOperandInfo(raw_ostream &OS, OperandInfoMapTy &OperandInfoIDs); @@ -176,6 +190,127 @@ void InstrInfoEmitter::EmitOperandInfo(raw_ostream &OS, } } + +/// Initialize data structures for generating operand name mappings. +/// +/// \param Operands [out] A map used to generate the OpName enum with operand +/// names as its keys and operand enum values as its values. +/// \param OperandMap [out] A map for representing the operand name mappings for +/// each instructions. This is used to generate the OperandMap table as +/// well as the getNamedOperandIdx() function. +void InstrInfoEmitter::initOperandMapData( + const std::vector<const CodeGenInstruction *> NumberedInstructions, + const std::string &Namespace, + std::map<std::string, unsigned> &Operands, + OpNameMapTy &OperandMap) { + + unsigned NumOperands = 0; + for (unsigned i = 0, e = NumberedInstructions.size(); i != e; ++i) { + const CodeGenInstruction *Inst = NumberedInstructions[i]; + if (!Inst->TheDef->getValueAsBit("UseNamedOperandTable")) { + continue; + } + std::map<unsigned, unsigned> OpList; + for (unsigned j = 0, je = Inst->Operands.size(); j != je; ++j) { + const CGIOperandList::OperandInfo &Info = Inst->Operands[j]; + StrUintMapIter I = Operands.find(Info.Name); + + if (I == Operands.end()) { + I = Operands.insert(Operands.begin(), + std::pair<std::string, unsigned>(Info.Name, NumOperands++)); + } + OpList[I->second] = Info.MIOperandNo; + } + OperandMap[OpList].push_back(Namespace + "::" + Inst->TheDef->getName()); + } +} + +/// Generate a table and function for looking up the indices of operands by +/// name. +/// +/// This code generates: +/// - An enum in the llvm::TargetNamespace::OpName namespace, with one entry +/// for each operand name. +/// - A 2-dimensional table called OperandMap for mapping OpName enum values to +/// operand indices. +/// - A function called getNamedOperandIdx(uint16_t Opcode, uint16_t NamedIdx) +/// for looking up the operand index for an instruction, given a value from +/// OpName enum +void InstrInfoEmitter::emitOperandNameMappings(raw_ostream &OS, + const CodeGenTarget &Target, + const std::vector<const CodeGenInstruction*> &NumberedInstructions) { + + const std::string &Namespace = Target.getInstNamespace(); + std::string OpNameNS = "OpName"; + // Map of operand names to their enumeration value. This will be used to + // generate the OpName enum. + std::map<std::string, unsigned> Operands; + OpNameMapTy OperandMap; + + initOperandMapData(NumberedInstructions, Namespace, Operands, OperandMap); + + OS << "#ifdef GET_INSTRINFO_OPERAND_ENUM\n"; + OS << "#undef GET_INSTRINFO_OPERAND_ENUM\n"; + OS << "namespace llvm {"; + OS << "namespace " << Namespace << " {\n"; + OS << "namespace " << OpNameNS << " { \n"; + OS << "enum {\n"; + for (StrUintMapIter i = Operands.begin(), e = Operands.end(); i != e; ++i) + OS << " " << i->first << " = " << i->second << ",\n"; + + OS << "OPERAND_LAST"; + OS << "\n};\n"; + OS << "} // End namespace OpName\n"; + OS << "} // End namespace " << Namespace << "\n"; + OS << "} // End namespace llvm\n"; + OS << "#endif //GET_INSTRINFO_OPERAND_ENUM\n"; + + OS << "#ifdef GET_INSTRINFO_NAMED_OPS\n"; + OS << "#undef GET_INSTRINFO_NAMED_OPS\n"; + OS << "namespace llvm {"; + OS << "namespace " << Namespace << " {\n"; + OS << "int16_t getNamedOperandIdx(uint16_t Opcode, uint16_t NamedIdx) {\n"; + if (!Operands.empty()) { + OS << " static const int16_t OperandMap [][" << Operands.size() + << "] = {\n"; + for (OpNameMapTy::iterator i = OperandMap.begin(), e = OperandMap.end(); + i != e; ++i) { + const std::map<unsigned, unsigned> &OpList = i->first; + OS << "{"; + + // Emit a row of the OperandMap table + for (unsigned ii = 0, ie = Operands.size(); ii != ie; ++ii) + OS << (OpList.count(ii) == 0 ? -1 : (int)OpList.find(ii)->second) + << ", "; + + OS << "},\n"; + } + OS << "};\n"; + + OS << " switch(Opcode) {\n"; + unsigned TableIndex = 0; + for (OpNameMapTy::iterator i = OperandMap.begin(), e = OperandMap.end(); + i != e; ++i) { + std::vector<std::string> &OpcodeList = i->second; + + for (unsigned ii = 0, ie = OpcodeList.size(); ii != ie; ++ii) + OS << " case " << OpcodeList[ii] << ":\n"; + + OS << " return OperandMap[" << TableIndex++ << "][NamedIdx];\n"; + } + OS << " default: return -1;\n"; + OS << " }\n"; + } else { + // There are no operands, so no need to emit anything + OS << " return -1;\n"; + } + OS << "}\n"; + OS << "} // End namespace " << Namespace << "\n"; + OS << "} // End namespace llvm\n"; + OS << "#endif //GET_INSTRINFO_NAMED_OPS\n"; + +} + //===----------------------------------------------------------------------===// // Main Output. //===----------------------------------------------------------------------===// @@ -293,6 +428,8 @@ void InstrInfoEmitter::run(raw_ostream &OS) { OS << "} // End llvm namespace \n"; OS << "#endif // GET_INSTRINFO_CTOR\n\n"; + + emitOperandNameMappings(OS, Target, NumberedInstructions); } void InstrInfoEmitter::emitRecord(const CodeGenInstruction &Inst, unsigned Num, diff --git a/utils/TableGen/IntrinsicEmitter.cpp b/utils/TableGen/IntrinsicEmitter.cpp index df4d847..c508795 100644 --- a/utils/TableGen/IntrinsicEmitter.cpp +++ b/utils/TableGen/IntrinsicEmitter.cpp @@ -131,6 +131,20 @@ void IntrinsicEmitter::EmitEnumInfo(const std::vector<CodeGenIntrinsic> &Ints, OS << "#endif\n\n"; } +struct IntrinsicNameSorter { + IntrinsicNameSorter(const std::vector<CodeGenIntrinsic> &I) + : Ints(I) {} + + // Sort in reverse order of intrinsic name so "abc.def" appears after + // "abd.def.ghi" in the overridden name matcher + bool operator()(unsigned i, unsigned j) { + return Ints[i].Name > Ints[j].Name; + } + +private: + const std::vector<CodeGenIntrinsic> &Ints; +}; + void IntrinsicEmitter:: EmitFnNameRecognizer(const std::vector<CodeGenIntrinsic> &Ints, raw_ostream &OS) { @@ -144,12 +158,16 @@ EmitFnNameRecognizer(const std::vector<CodeGenIntrinsic> &Ints, OS << " StringRef NameR(Name+6, Len-6); // Skip over 'llvm.'\n"; OS << " switch (Name[5]) { // Dispatch on first letter.\n"; OS << " default: break;\n"; + IntrinsicNameSorter Sorter(Ints); // Emit the intrinsic matching stuff by first letter. for (std::map<char, std::vector<unsigned> >::iterator I = IntMapping.begin(), E = IntMapping.end(); I != E; ++I) { OS << " case '" << I->first << "':\n"; std::vector<unsigned> &IntList = I->second; + // Sort intrinsics in reverse order of their names + std::sort(IntList.begin(), IntList.end(), Sorter); + // Emit all the overloaded intrinsics first, build a table of the // non-overloaded ones. std::vector<StringMatcher::StringPair> MatchTable; @@ -579,6 +597,12 @@ EmitAttributes(const std::vector<CodeGenIntrinsic> &Ints, raw_ostream &OS) { case CodeGenIntrinsic::NoCapture: OS << " AttrVec.push_back(Attribute::NoCapture);\n"; break; + case CodeGenIntrinsic::ReadOnly: + OS << " AttrVec.push_back(Attribute::ReadOnly);\n"; + break; + case CodeGenIntrinsic::ReadNone: + OS << " AttrVec.push_back(Attribute::ReadNone);\n"; + break; } ++ai; diff --git a/utils/TableGen/OptParserEmitter.cpp b/utils/TableGen/OptParserEmitter.cpp index 0c1f623..86328bf 100644 --- a/utils/TableGen/OptParserEmitter.cpp +++ b/utils/TableGen/OptParserEmitter.cpp @@ -178,7 +178,7 @@ void EmitOptParser(RecordKeeper &Records, raw_ostream &OS) { OS << "INVALID"; // The other option arguments (unused for groups). - OS << ", INVALID, 0, 0"; + OS << ", INVALID, 0, 0, 0"; // The option help text. if (!isa<UnsetInit>(R.getValueInit("HelpText"))) { @@ -228,6 +228,21 @@ void EmitOptParser(RecordKeeper &Records, raw_ostream &OS) { else OS << "INVALID"; + // The option alias arguments (if any). + // Emitted as a \0 separated list in a string, e.g. ["foo", "bar"] + // would become "foo\0bar\0". Note that the compiler adds an implicit + // terminating \0 at the end. + OS << ", "; + std::vector<std::string> AliasArgs = R.getValueAsListOfStrings("AliasArgs"); + if (AliasArgs.size() == 0) { + OS << "0"; + } else { + OS << "\""; + for (size_t i = 0, e = AliasArgs.size(); i != e; ++i) + OS << AliasArgs[i] << "\\0"; + OS << "\""; + } + // The option flags. const ListInit *LI = R.getValueAsListInit("Flags"); if (LI->empty()) { diff --git a/utils/TableGen/RegisterInfoEmitter.cpp b/utils/TableGen/RegisterInfoEmitter.cpp index 2a337f0..731dccf 100644 --- a/utils/TableGen/RegisterInfoEmitter.cpp +++ b/utils/TableGen/RegisterInfoEmitter.cpp @@ -223,7 +223,7 @@ EmitRegUnitPressure(raw_ostream &OS, const CodeGenRegBank &RegBank, << "getRegPressureSetName(unsigned Idx) const {\n" << " static const char *PressureNameTable[] = {\n"; for (unsigned i = 0; i < NumSets; ++i ) { - OS << " \"" << RegBank.getRegPressureSet(i).Name << "\",\n"; + OS << " \"" << RegBank.getRegSetAt(i).Name << "\",\n"; } OS << " 0 };\n" << " return PressureNameTable[Idx];\n" @@ -235,9 +235,9 @@ EmitRegUnitPressure(raw_ostream &OS, const CodeGenRegBank &RegBank, << "getRegPressureSetLimit(unsigned Idx) const {\n" << " static const unsigned PressureLimitTable[] = {\n"; for (unsigned i = 0; i < NumSets; ++i ) { - const RegUnitSet &RegUnits = RegBank.getRegPressureSet(i); - OS << " " << RegBank.getRegUnitSetWeight(RegUnits.Units) - << ", \t// " << i << ": " << RegUnits.Name << "\n"; + const RegUnitSet &RegUnits = RegBank.getRegSetAt(i); + OS << " " << RegUnits.Weight << ", \t// " << i << ": " + << RegUnits.Name << "\n"; } OS << " 0 };\n" << " return PressureLimitTable[Idx];\n" @@ -252,9 +252,15 @@ EmitRegUnitPressure(raw_ostream &OS, const CodeGenRegBank &RegBank, for (unsigned i = 0, StartIdx = 0, e = NumRCUnitSets; i != e; ++i) { RCSetStarts[i] = StartIdx; ArrayRef<unsigned> PSetIDs = RegBank.getRCPressureSetIDs(i); + std::vector<unsigned> PSets; + PSets.reserve(PSetIDs.size()); for (ArrayRef<unsigned>::iterator PSetI = PSetIDs.begin(), PSetE = PSetIDs.end(); PSetI != PSetE; ++PSetI) { - OS << *PSetI << ", "; + PSets.push_back(RegBank.getRegPressureSet(*PSetI).Order); + } + std::sort(PSets.begin(), PSets.end()); + for (unsigned j = 0, e = PSets.size(); j < e; ++j) { + OS << PSets[j] << ", "; ++StartIdx; } OS << "-1, \t// #" << RCSetStarts[i] << " "; @@ -264,7 +270,7 @@ EmitRegUnitPressure(raw_ostream &OS, const CodeGenRegBank &RegBank, OS << "inferred"; for (ArrayRef<unsigned>::iterator PSetI = PSetIDs.begin(), PSetE = PSetIDs.end(); PSetI != PSetE; ++PSetI) { - OS << "~" << RegBank.getRegPressureSet(*PSetI).Name; + OS << "~" << RegBank.getRegSetAt(*PSetI).Name; } } OS << "\n "; @@ -309,7 +315,7 @@ RegisterInfoEmitter::EmitRegMappingTables(raw_ostream &OS, const std::vector<CodeGenRegister*> &Regs, bool isCtor) { // Collect all information about dwarf register numbers - typedef std::map<Record*, std::vector<int64_t>, LessRecord> DwarfRegNumsMapTy; + typedef std::map<Record*, std::vector<int64_t>, LessRecordRegister> DwarfRegNumsMapTy; DwarfRegNumsMapTy DwarfRegNums; // First, just pull all provided information to the map diff --git a/utils/TableGen/SubtargetEmitter.cpp b/utils/TableGen/SubtargetEmitter.cpp index 993eead..81bb6f8 100644 --- a/utils/TableGen/SubtargetEmitter.cpp +++ b/utils/TableGen/SubtargetEmitter.cpp @@ -634,16 +634,11 @@ void SubtargetEmitter::EmitProcessorResources(const CodeGenProcModel &ProcModel, Record *SuperDef = 0; unsigned SuperIdx = 0; unsigned NumUnits = 0; - bool IsBuffered = true; + int BufferSize = PRDef->getValueAsInt("BufferSize"); if (PRDef->isSubClassOf("ProcResGroup")) { RecVec ResUnits = PRDef->getValueAsListOfDefs("Resources"); for (RecIter RUI = ResUnits.begin(), RUE = ResUnits.end(); RUI != RUE; ++RUI) { - if (!NumUnits) - IsBuffered = (*RUI)->getValueAsBit("Buffered"); - else if(IsBuffered != (*RUI)->getValueAsBit("Buffered")) - PrintFatalError(PRDef->getLoc(), - "Mixing buffered and unbuffered resources."); NumUnits += (*RUI)->getValueAsInt("NumUnits"); } } @@ -655,7 +650,6 @@ void SubtargetEmitter::EmitProcessorResources(const CodeGenProcModel &ProcModel, SuperIdx = ProcModel.getProcResourceIdx(SuperDef); } NumUnits = PRDef->getValueAsInt("NumUnits"); - IsBuffered = PRDef->getValueAsBit("Buffered"); } // Emit the ProcResourceDesc if (i+1 == e) @@ -664,7 +658,7 @@ void SubtargetEmitter::EmitProcessorResources(const CodeGenProcModel &ProcModel, if (PRDef->getName().size() < 15) OS.indent(15 - PRDef->getName().size()); OS << NumUnits << ", " << SuperIdx << ", " - << IsBuffered << "}" << Sep << " // #" << i+1; + << BufferSize << "}" << Sep << " // #" << i+1; if (SuperDef) OS << ", Super=" << SuperDef->getName(); OS << "\n"; @@ -1200,10 +1194,9 @@ void SubtargetEmitter::EmitProcessorModels(raw_ostream &OS) { OS << "\n"; OS << "static const llvm::MCSchedModel " << PI->ModelName << "(\n"; EmitProcessorProp(OS, PI->ModelDef, "IssueWidth", ','); - EmitProcessorProp(OS, PI->ModelDef, "MinLatency", ','); + EmitProcessorProp(OS, PI->ModelDef, "MicroOpBufferSize", ','); EmitProcessorProp(OS, PI->ModelDef, "LoadLatency", ','); EmitProcessorProp(OS, PI->ModelDef, "HighLatency", ','); - EmitProcessorProp(OS, PI->ModelDef, "ILPWindow", ','); EmitProcessorProp(OS, PI->ModelDef, "MispredictPenalty", ','); OS << " " << PI->Index << ", // Processor ID\n"; if (PI->hasInstrSchedModel()) diff --git a/utils/TableGen/X86DisassemblerTables.cpp b/utils/TableGen/X86DisassemblerTables.cpp index 40a0c1b..0838ac4 100644 --- a/utils/TableGen/X86DisassemblerTables.cpp +++ b/utils/TableGen/X86DisassemblerTables.cpp @@ -81,16 +81,20 @@ static inline bool inheritsFrom(InstructionContext child, case IC_64BIT_REXW_OPSIZE: return false; case IC_VEX: - return inheritsFrom(child, IC_VEX_W) || + return inheritsFrom(child, IC_VEX_L_W) || + inheritsFrom(child, IC_VEX_W) || (VEX_LIG && inheritsFrom(child, IC_VEX_L)); case IC_VEX_XS: - return inheritsFrom(child, IC_VEX_W_XS) || + return inheritsFrom(child, IC_VEX_L_W_XS) || + inheritsFrom(child, IC_VEX_W_XS) || (VEX_LIG && inheritsFrom(child, IC_VEX_L_XS)); case IC_VEX_XD: - return inheritsFrom(child, IC_VEX_W_XD) || + return inheritsFrom(child, IC_VEX_L_W_XD) || + inheritsFrom(child, IC_VEX_W_XD) || (VEX_LIG && inheritsFrom(child, IC_VEX_L_XD)); case IC_VEX_OPSIZE: - return inheritsFrom(child, IC_VEX_W_OPSIZE) || + return inheritsFrom(child, IC_VEX_L_W_OPSIZE) || + inheritsFrom(child, IC_VEX_W_OPSIZE) || (VEX_LIG && inheritsFrom(child, IC_VEX_L_OPSIZE)); case IC_VEX_W: case IC_VEX_W_XS: @@ -100,11 +104,90 @@ static inline bool inheritsFrom(InstructionContext child, case IC_VEX_L: case IC_VEX_L_XS: case IC_VEX_L_XD: - return false; case IC_VEX_L_OPSIZE: - return inheritsFrom(child, IC_VEX_L_W_OPSIZE); + return false; + case IC_VEX_L_W: + case IC_VEX_L_W_XS: + case IC_VEX_L_W_XD: case IC_VEX_L_W_OPSIZE: return false; + case IC_EVEX: + return inheritsFrom(child, IC_EVEX_W) || + inheritsFrom(child, IC_EVEX_L_W); + case IC_EVEX_XS: + return inheritsFrom(child, IC_EVEX_W_XS) || + inheritsFrom(child, IC_EVEX_L_W_XS); + case IC_EVEX_XD: + return inheritsFrom(child, IC_EVEX_W_XD) || + inheritsFrom(child, IC_EVEX_L_W_XD); + case IC_EVEX_OPSIZE: + return inheritsFrom(child, IC_EVEX_W_OPSIZE) || + inheritsFrom(child, IC_EVEX_W_OPSIZE); + case IC_EVEX_W: + case IC_EVEX_W_XS: + case IC_EVEX_W_XD: + case IC_EVEX_W_OPSIZE: + return false; + case IC_EVEX_L: + case IC_EVEX_L_XS: + case IC_EVEX_L_XD: + case IC_EVEX_L_OPSIZE: + return false; + case IC_EVEX_L_W: + case IC_EVEX_L_W_XS: + case IC_EVEX_L_W_XD: + case IC_EVEX_L_W_OPSIZE: + return false; + case IC_EVEX_L2: + case IC_EVEX_L2_XS: + case IC_EVEX_L2_XD: + case IC_EVEX_L2_OPSIZE: + return false; + case IC_EVEX_L2_W: + case IC_EVEX_L2_W_XS: + case IC_EVEX_L2_W_XD: + case IC_EVEX_L2_W_OPSIZE: + return false; + case IC_EVEX_K: + return inheritsFrom(child, IC_EVEX_W_K) || + inheritsFrom(child, IC_EVEX_L_W_K); + case IC_EVEX_XS_K: + return inheritsFrom(child, IC_EVEX_W_XS_K) || + inheritsFrom(child, IC_EVEX_L_W_XS_K); + case IC_EVEX_XD_K: + return inheritsFrom(child, IC_EVEX_W_XD_K) || + inheritsFrom(child, IC_EVEX_L_W_XD_K); + case IC_EVEX_OPSIZE_K: + return inheritsFrom(child, IC_EVEX_W_OPSIZE_K) || + inheritsFrom(child, IC_EVEX_W_OPSIZE_K); + case IC_EVEX_W_K: + case IC_EVEX_W_XS_K: + case IC_EVEX_W_XD_K: + case IC_EVEX_W_OPSIZE_K: + return false; + case IC_EVEX_L_K: + case IC_EVEX_L_XS_K: + case IC_EVEX_L_XD_K: + case IC_EVEX_L_OPSIZE_K: + return false; + case IC_EVEX_L_W_K: + case IC_EVEX_L_W_XS_K: + case IC_EVEX_L_W_XD_K: + case IC_EVEX_L_W_OPSIZE_K: + return false; + case IC_EVEX_L2_K: + case IC_EVEX_L2_B: + case IC_EVEX_L2_XS_K: + case IC_EVEX_L2_XD_K: + case IC_EVEX_L2_OPSIZE_K: + case IC_EVEX_L2_OPSIZE_B: + return false; + case IC_EVEX_L2_W_K: + case IC_EVEX_L2_W_XS_K: + case IC_EVEX_L2_W_XD_K: + case IC_EVEX_L2_W_OPSIZE_K: + case IC_EVEX_L2_W_OPSIZE_B: + return false; default: llvm_unreachable("Unknown instruction class"); } @@ -123,10 +206,13 @@ static inline bool outranks(InstructionContext upper, assert(lower < IC_max); #define ENUM_ENTRY(n, r, d) r, +#define ENUM_ENTRY_K_B(n, r, d) ENUM_ENTRY(n, r, d) \ + ENUM_ENTRY(n##_K_B, r, d) ENUM_ENTRY(n##_K, r, d) ENUM_ENTRY(n##_B, r, d) static int ranks[IC_max] = { INSTRUCTION_CONTEXTS }; #undef ENUM_ENTRY +#undef ENUM_ENTRY_K_B return (ranks[upper] > ranks[lower]); } @@ -142,8 +228,11 @@ static inline const char* stringForContext(InstructionContext insnContext) { default: llvm_unreachable("Unhandled instruction class"); #define ENUM_ENTRY(n, r, d) case n: return #n; break; +#define ENUM_ENTRY_K_B(n, r, d) ENUM_ENTRY(n, r, d) ENUM_ENTRY(n##_K_B, r, d)\ + ENUM_ENTRY(n##_K, r, d) ENUM_ENTRY(n##_B, r, d) INSTRUCTION_CONTEXTS #undef ENUM_ENTRY +#undef ENUM_ENTRY_K_B } } diff --git a/utils/TableGen/X86RecognizableInstr.cpp b/utils/TableGen/X86RecognizableInstr.cpp index 46f2052..7962f9b 100644 --- a/utils/TableGen/X86RecognizableInstr.cpp +++ b/utils/TableGen/X86RecognizableInstr.cpp @@ -236,6 +236,10 @@ RecognizableInstr::RecognizableInstr(DisassemblerTables &tables, HasVEX_WPrefix = Rec->getValueAsBit("hasVEX_WPrefix"); HasMemOp4Prefix = Rec->getValueAsBit("hasMemOp4Prefix"); IgnoresVEX_L = Rec->getValueAsBit("ignoresVEX_L"); + HasEVEXPrefix = Rec->getValueAsBit("hasEVEXPrefix"); + HasEVEX_L2Prefix = Rec->getValueAsBit("hasEVEX_L2"); + HasEVEX_K = Rec->getValueAsBit("hasEVEX_K"); + HasEVEX_B = Rec->getValueAsBit("hasEVEX_B"); HasLockPrefix = Rec->getValueAsBit("hasLockPrefix"); IsCodeGenOnly = Rec->getValueAsBit("isCodeGenOnly"); @@ -295,15 +299,97 @@ void RecognizableInstr::processInstr(DisassemblerTables &tables, recogInstr.emitDecodePath(tables); } +#define EVEX_KB(n) (HasEVEX_K && HasEVEX_B? n##_K_B : \ + (HasEVEX_K? n##_K : (HasEVEX_B ? n##_B : n))) + InstructionContext RecognizableInstr::insnContext() const { InstructionContext insnContext; - if (HasVEX_4VPrefix || HasVEX_4VOp3Prefix|| HasVEXPrefix) { + if (HasEVEXPrefix) { + if (HasVEX_LPrefix && HasEVEX_L2Prefix) { + errs() << "Don't support VEX.L if EVEX_L2 is enabled: " << Name << "\n"; + llvm_unreachable("Don't support VEX.L if EVEX_L2 is enabled"); + } + // VEX_L & VEX_W + if (HasVEX_LPrefix && HasVEX_WPrefix) { + if (HasOpSizePrefix) + insnContext = EVEX_KB(IC_EVEX_L_W_OPSIZE); + else if (Prefix == X86Local::XS || Prefix == X86Local::T8XS) + insnContext = EVEX_KB(IC_EVEX_L_W_XS); + else if (Prefix == X86Local::XD || Prefix == X86Local::T8XD || + Prefix == X86Local::TAXD) + insnContext = EVEX_KB(IC_EVEX_L_W_XD); + else + insnContext = EVEX_KB(IC_EVEX_L_W); + } else if (HasVEX_LPrefix) { + // VEX_L + if (HasOpSizePrefix) + insnContext = EVEX_KB(IC_EVEX_L_OPSIZE); + else if (Prefix == X86Local::XS || Prefix == X86Local::T8XS) + insnContext = EVEX_KB(IC_EVEX_L_XS); + else if (Prefix == X86Local::XD || Prefix == X86Local::T8XD || + Prefix == X86Local::TAXD) + insnContext = EVEX_KB(IC_EVEX_L_XD); + else + insnContext = EVEX_KB(IC_EVEX_L); + } + else if (HasEVEX_L2Prefix && HasVEX_WPrefix) { + // EVEX_L2 & VEX_W + if (HasOpSizePrefix) + insnContext = EVEX_KB(IC_EVEX_L2_W_OPSIZE); + else if (Prefix == X86Local::XS || Prefix == X86Local::T8XS) + insnContext = EVEX_KB(IC_EVEX_L2_W_XS); + else if (Prefix == X86Local::XD || Prefix == X86Local::T8XD || + Prefix == X86Local::TAXD) + insnContext = EVEX_KB(IC_EVEX_L2_W_XD); + else + insnContext = EVEX_KB(IC_EVEX_L2_W); + } else if (HasEVEX_L2Prefix) { + // EVEX_L2 + if (HasOpSizePrefix) + insnContext = EVEX_KB(IC_EVEX_L2_OPSIZE); + else if (Prefix == X86Local::XD || Prefix == X86Local::T8XD || + Prefix == X86Local::TAXD) + insnContext = EVEX_KB(IC_EVEX_L2_XD); + else if (Prefix == X86Local::XS || Prefix == X86Local::T8XS) + insnContext = EVEX_KB(IC_EVEX_L2_XS); + else + insnContext = EVEX_KB(IC_EVEX_L2); + } + else if (HasVEX_WPrefix) { + // VEX_W + if (HasOpSizePrefix) + insnContext = EVEX_KB(IC_EVEX_W_OPSIZE); + else if (Prefix == X86Local::XS || Prefix == X86Local::T8XS) + insnContext = EVEX_KB(IC_EVEX_W_XS); + else if (Prefix == X86Local::XD || Prefix == X86Local::T8XD || + Prefix == X86Local::TAXD) + insnContext = EVEX_KB(IC_EVEX_W_XD); + else + insnContext = EVEX_KB(IC_EVEX_W); + } + // No L, no W + else if (HasOpSizePrefix) + insnContext = EVEX_KB(IC_EVEX_OPSIZE); + else if (Prefix == X86Local::XD || Prefix == X86Local::T8XD || + Prefix == X86Local::TAXD) + insnContext = EVEX_KB(IC_EVEX_XD); + else if (Prefix == X86Local::XS || Prefix == X86Local::T8XS) + insnContext = EVEX_KB(IC_EVEX_XS); + else + insnContext = EVEX_KB(IC_EVEX); + /// eof EVEX + } else if (HasVEX_4VPrefix || HasVEX_4VOp3Prefix|| HasVEXPrefix) { if (HasVEX_LPrefix && HasVEX_WPrefix) { if (HasOpSizePrefix) insnContext = IC_VEX_L_W_OPSIZE; + else if (Prefix == X86Local::XS || Prefix == X86Local::T8XS) + insnContext = IC_VEX_L_W_XS; + else if (Prefix == X86Local::XD || Prefix == X86Local::T8XD || + Prefix == X86Local::TAXD) + insnContext = IC_VEX_L_W_XD; else - llvm_unreachable("Don't support VEX.L and VEX.W together"); + insnContext = IC_VEX_L_W; } else if (HasOpSizePrefix && HasVEX_LPrefix) insnContext = IC_VEX_L_OPSIZE; else if (HasOpSizePrefix && HasVEX_WPrefix) @@ -461,6 +547,12 @@ RecognizableInstr::filter_ret RecognizableInstr::filter() const { Name == "VMOVQs64rr") return FILTER_WEAK; + // XACQUIRE and XRELEASE reuse REPNE and REP respectively. + // For now, just prefer the REP versions. + if (Name == "XACQUIRE_PREFIX" || + Name == "XRELEASE_PREFIX") + return FILTER_WEAK; + if (HasFROperands && Name.find("MOV") != Name.npos && ((Name.find("2") != Name.npos && Name.find("32") == Name.npos) || (Name.find("to") != Name.npos))) @@ -635,6 +727,9 @@ void RecognizableInstr::emitInstructionSpecifier(DisassemblerTables &tables) { "Unexpected number of operands for MRMDestMemFrm"); HANDLE_OPERAND(memory) + if (HasEVEX_K) + HANDLE_OPERAND(writemaskRegister) + if (HasVEX_4VPrefix) // FIXME: In AVX, the register below becomes the one encoded // in ModRMVEX and the one above the one in the VEX.VVVV field @@ -659,6 +754,9 @@ void RecognizableInstr::emitInstructionSpecifier(DisassemblerTables &tables) { HANDLE_OPERAND(roRegister) + if (HasEVEX_K) + HANDLE_OPERAND(writemaskRegister) + if (HasVEX_4VPrefix) // FIXME: In AVX, the register below becomes the one encoded // in ModRMVEX and the one above the one in the VEX.VVVV field @@ -692,6 +790,9 @@ void RecognizableInstr::emitInstructionSpecifier(DisassemblerTables &tables) { HANDLE_OPERAND(roRegister) + if (HasEVEX_K) + HANDLE_OPERAND(writemaskRegister) + if (HasVEX_4VPrefix) // FIXME: In AVX, the register below becomes the one encoded // in ModRMVEX and the one above the one in the VEX.VVVV field @@ -1073,17 +1174,22 @@ OperandType RecognizableInstr::typeFromString(const std::string &s, TYPE("i8imm", TYPE_IMM8) TYPE("GR8", TYPE_R8) TYPE("VR128", TYPE_XMM128) + TYPE("VR128X", TYPE_XMM128) TYPE("f128mem", TYPE_M128) TYPE("f256mem", TYPE_M256) + TYPE("f512mem", TYPE_M512) TYPE("FR64", TYPE_XMM64) + TYPE("FR64X", TYPE_XMM64) TYPE("f64mem", TYPE_M64FP) TYPE("sdmem", TYPE_M64FP) TYPE("FR32", TYPE_XMM32) + TYPE("FR32X", TYPE_XMM32) TYPE("f32mem", TYPE_M32FP) TYPE("ssmem", TYPE_M32FP) TYPE("RST", TYPE_ST) TYPE("i128mem", TYPE_M128) TYPE("i256mem", TYPE_M256) + TYPE("i512mem", TYPE_M512) TYPE("i64i32imm_pcrel", TYPE_REL64) TYPE("i16imm_pcrel", TYPE_REL16) TYPE("i32imm_pcrel", TYPE_REL32) @@ -1110,13 +1216,22 @@ OperandType RecognizableInstr::typeFromString(const std::string &s, TYPE("offset32", TYPE_MOFFS32) TYPE("offset64", TYPE_MOFFS64) TYPE("VR256", TYPE_XMM256) + TYPE("VR256X", TYPE_XMM256) + TYPE("VR512", TYPE_XMM512) + TYPE("VK8", TYPE_VK8) + TYPE("VK8WM", TYPE_VK8) + TYPE("VK16", TYPE_VK16) + TYPE("VK16WM", TYPE_VK16) TYPE("GR16_NOAX", TYPE_Rv) TYPE("GR32_NOAX", TYPE_Rv) TYPE("GR64_NOAX", TYPE_R64) TYPE("vx32mem", TYPE_M32) TYPE("vy32mem", TYPE_M32) + TYPE("vz32mem", TYPE_M32) TYPE("vx64mem", TYPE_M64) TYPE("vy64mem", TYPE_M64) + TYPE("vy64xmem", TYPE_M64) + TYPE("vz64mem", TYPE_M64) errs() << "Unhandled type string " << s << "\n"; llvm_unreachable("Unhandled type string"); } @@ -1143,10 +1258,15 @@ OperandEncoding RecognizableInstr::immediateEncodingFromString ENCODING("i8imm", ENCODING_IB) // This is not a typo. Instructions like BLENDVPD put // register IDs in 8-bit immediates nowadays. - ENCODING("VR256", ENCODING_IB) - ENCODING("VR128", ENCODING_IB) ENCODING("FR32", ENCODING_IB) ENCODING("FR64", ENCODING_IB) + ENCODING("VR128", ENCODING_IB) + ENCODING("VR256", ENCODING_IB) + ENCODING("FR32X", ENCODING_IB) + ENCODING("FR64X", ENCODING_IB) + ENCODING("VR128X", ENCODING_IB) + ENCODING("VR256X", ENCODING_IB) + ENCODING("VR512", ENCODING_IB) errs() << "Unhandled immediate encoding " << s << "\n"; llvm_unreachable("Unhandled immediate encoding"); } @@ -1159,10 +1279,17 @@ OperandEncoding RecognizableInstr::rmRegisterEncodingFromString ENCODING("GR64", ENCODING_RM) ENCODING("GR8", ENCODING_RM) ENCODING("VR128", ENCODING_RM) + ENCODING("VR128X", ENCODING_RM) ENCODING("FR64", ENCODING_RM) ENCODING("FR32", ENCODING_RM) + ENCODING("FR64X", ENCODING_RM) + ENCODING("FR32X", ENCODING_RM) ENCODING("VR64", ENCODING_RM) ENCODING("VR256", ENCODING_RM) + ENCODING("VR256X", ENCODING_RM) + ENCODING("VR512", ENCODING_RM) + ENCODING("VK8", ENCODING_RM) + ENCODING("VK16", ENCODING_RM) errs() << "Unhandled R/M register encoding " << s << "\n"; llvm_unreachable("Unhandled R/M register encoding"); } @@ -1182,6 +1309,15 @@ OperandEncoding RecognizableInstr::roRegisterEncodingFromString ENCODING("DEBUG_REG", ENCODING_REG) ENCODING("CONTROL_REG", ENCODING_REG) ENCODING("VR256", ENCODING_REG) + ENCODING("VR256X", ENCODING_REG) + ENCODING("VR128X", ENCODING_REG) + ENCODING("FR64X", ENCODING_REG) + ENCODING("FR32X", ENCODING_REG) + ENCODING("VR512", ENCODING_REG) + ENCODING("VK8", ENCODING_REG) + ENCODING("VK16", ENCODING_REG) + ENCODING("VK8WM", ENCODING_REG) + ENCODING("VK16WM", ENCODING_REG) errs() << "Unhandled reg/opcode register encoding " << s << "\n"; llvm_unreachable("Unhandled reg/opcode register encoding"); } @@ -1195,10 +1331,26 @@ OperandEncoding RecognizableInstr::vvvvRegisterEncodingFromString ENCODING("FR64", ENCODING_VVVV) ENCODING("VR128", ENCODING_VVVV) ENCODING("VR256", ENCODING_VVVV) + ENCODING("FR32X", ENCODING_VVVV) + ENCODING("FR64X", ENCODING_VVVV) + ENCODING("VR128X", ENCODING_VVVV) + ENCODING("VR256X", ENCODING_VVVV) + ENCODING("VR512", ENCODING_VVVV) + ENCODING("VK8", ENCODING_VVVV) + ENCODING("VK16", ENCODING_VVVV) errs() << "Unhandled VEX.vvvv register encoding " << s << "\n"; llvm_unreachable("Unhandled VEX.vvvv register encoding"); } +OperandEncoding RecognizableInstr::writemaskRegisterEncodingFromString + (const std::string &s, + bool hasOpSizePrefix) { + ENCODING("VK8WM", ENCODING_WRITEMASK) + ENCODING("VK16WM", ENCODING_WRITEMASK) + errs() << "Unhandled mask register encoding " << s << "\n"; + llvm_unreachable("Unhandled mask register encoding"); +} + OperandEncoding RecognizableInstr::memoryEncodingFromString (const std::string &s, bool hasOpSizePrefix) { @@ -1210,10 +1362,12 @@ OperandEncoding RecognizableInstr::memoryEncodingFromString ENCODING("sdmem", ENCODING_RM) ENCODING("f128mem", ENCODING_RM) ENCODING("f256mem", ENCODING_RM) + ENCODING("f512mem", ENCODING_RM) ENCODING("f64mem", ENCODING_RM) ENCODING("f32mem", ENCODING_RM) ENCODING("i128mem", ENCODING_RM) ENCODING("i256mem", ENCODING_RM) + ENCODING("i512mem", ENCODING_RM) ENCODING("f80mem", ENCODING_RM) ENCODING("lea32mem", ENCODING_RM) ENCODING("lea64_32mem", ENCODING_RM) @@ -1224,8 +1378,11 @@ OperandEncoding RecognizableInstr::memoryEncodingFromString ENCODING("opaque512mem", ENCODING_RM) ENCODING("vx32mem", ENCODING_RM) ENCODING("vy32mem", ENCODING_RM) + ENCODING("vz32mem", ENCODING_RM) ENCODING("vx64mem", ENCODING_RM) ENCODING("vy64mem", ENCODING_RM) + ENCODING("vy64xmem", ENCODING_RM) + ENCODING("vz64mem", ENCODING_RM) errs() << "Unhandled memory encoding " << s << "\n"; llvm_unreachable("Unhandled memory encoding"); } diff --git a/utils/TableGen/X86RecognizableInstr.h b/utils/TableGen/X86RecognizableInstr.h index 9ec36a3..7e1d362 100644 --- a/utils/TableGen/X86RecognizableInstr.h +++ b/utils/TableGen/X86RecognizableInstr.h @@ -66,6 +66,14 @@ private: bool HasMemOp4Prefix; /// The ignoreVEX_L field from the record bool IgnoresVEX_L; + /// The hasEVEXPrefix field from the record + bool HasEVEXPrefix; + /// The hasEVEX_L2Prefix field from the record + bool HasEVEX_L2Prefix; + /// The hasEVEX_K field from the record + bool HasEVEX_K; + /// The hasEVEX_B field from the record + bool HasEVEX_B; /// The hasLockPrefix field from the record bool HasLockPrefix; /// The isCodeGenOnly filed from the record @@ -176,6 +184,8 @@ private: bool hasOpSizePrefix); static OperandEncoding vvvvRegisterEncodingFromString(const std::string &s, bool HasOpSizePrefix); + static OperandEncoding writemaskRegisterEncodingFromString(const std::string &s, + bool HasOpSizePrefix); /// handleOperand - Converts a single operand from the LLVM table format to /// the emitted table format, handling any duplicate operands it encounters diff --git a/utils/buildit/build_llvm b/utils/buildit/build_llvm index c056b97..4ed25f2 100755 --- a/utils/buildit/build_llvm +++ b/utils/buildit/build_llvm @@ -103,7 +103,8 @@ COMMON_CONFIGURE_OPTS="\ --prefix=$DEST_DIR$DEST_ROOT \ --enable-assertions=$LLVM_ASSERTIONS \ --enable-optimized=$LLVM_OPTIMIZED \ - --disable-bindings" + --disable-bindings \ + --disable-zlib" COMMON_MAKEFLAGS="\ UNIVERSAL=1 \ diff --git a/utils/fpcmp/fpcmp.cpp b/utils/fpcmp/fpcmp.cpp index 5f6b5e8..bbe7276 100644 --- a/utils/fpcmp/fpcmp.cpp +++ b/utils/fpcmp/fpcmp.cpp @@ -33,9 +33,8 @@ int main(int argc, char **argv) { cl::ParseCommandLineOptions(argc, argv); std::string ErrorMsg; - int DF = DiffFilesWithTolerance(sys::PathWithStatus(File1), - sys::PathWithStatus(File2), - AbsTolerance, RelTolerance, &ErrorMsg); + int DF = DiffFilesWithTolerance(File1, File2, AbsTolerance, RelTolerance, + &ErrorMsg); if (!ErrorMsg.empty()) errs() << argv[0] << ": " << ErrorMsg << "\n"; return DF; diff --git a/utils/lit/lit/LitConfig.py b/utils/lit/lit/LitConfig.py index 9bcf20b..bd7a603 100644 --- a/utils/lit/lit/LitConfig.py +++ b/utils/lit/lit/LitConfig.py @@ -1,3 +1,11 @@ +from __future__ import absolute_import +import os + +import lit.Test +import lit.TestFormats +import lit.TestingConfig +import lit.Util + class LitConfig: """LitConfig - Configuration data for a 'lit' test runner instance, shared across all tests. @@ -9,17 +17,17 @@ class LitConfig: """ # Provide access to Test module. - import Test + Test = lit.Test # Provide access to built-in formats. - import TestFormats as formats + formats = lit.TestFormats # Provide access to built-in utility functions. - import Util as util + util = lit.Util def __init__(self, progname, path, quiet, useValgrind, valgrindLeakCheck, valgrindArgs, - noExecute, ignoreStdErr, debug, isWindows, + noExecute, debug, isWindows, params, config_prefix = None): # The name of the test runner. self.progname = progname @@ -30,7 +38,6 @@ class LitConfig: self.valgrindLeakCheck = bool(valgrindLeakCheck) self.valgrindUserArgs = list(valgrindArgs) self.noExecute = noExecute - self.ignoreStdErr = ignoreStdErr self.debug = debug self.isWindows = bool(isWindows) self.params = dict(params) @@ -61,21 +68,19 @@ class LitConfig: def load_config(self, config, path): """load_config(config, path) - Load a config object from an alternate path.""" - from TestingConfig import TestingConfig if self.debug: self.note('load_config from %r' % path) - return TestingConfig.frompath(path, config.parent, self, - mustExist = True, - config = config) + return lit.TestingConfig.TestingConfig.frompath( + path, config.parent, self, mustExist = True, config = config) def getBashPath(self): """getBashPath - Get the path to 'bash'""" - import os, Util + import os if self.bashPath is not None: return self.bashPath - self.bashPath = Util.which('bash', os.pathsep.join(self.path)) + self.bashPath = lit.Util.which('bash', os.pathsep.join(self.path)) if self.bashPath is None: # Check some known paths. for path in ('/bin/bash', '/usr/bin/bash', '/usr/local/bin/bash'): @@ -90,15 +95,14 @@ class LitConfig: return self.bashPath def getToolsPath(self, dir, paths, tools): - import os, Util if dir is not None and os.path.isabs(dir) and os.path.isdir(dir): - if not Util.checkToolsPath(dir, tools): + if not lit.Util.checkToolsPath(dir, tools): return None else: - dir = Util.whichTools(tools, paths) + dir = lit.Util.whichTools(tools, paths) # bash - self.bashPath = Util.which('bash', dir) + self.bashPath = lit.Util.which('bash', dir) if self.bashPath is None: self.note("Unable to find 'bash.exe'.") self.bashPath = '' @@ -115,8 +119,8 @@ class LitConfig: 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) + sys.stderr.write('%s: %s: %s: %s\n' % (self.progname, location, + kind, message)) def note(self, message): self._write_message('note', message) diff --git a/utils/lit/lit/ProgressBar.py b/utils/lit/lit/ProgressBar.py index 5c85a17..0454ba2 100644 --- a/utils/lit/lit/ProgressBar.py +++ b/utils/lit/lit/ProgressBar.py @@ -16,13 +16,13 @@ class TerminalController: output to the terminal: >>> term = TerminalController() - >>> print 'This is '+term.GREEN+'green'+term.NORMAL + >>> 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}') + >>> 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 @@ -34,7 +34,7 @@ class TerminalController: >>> term = TerminalController() >>> if term.CLEAR_SCREEN: - ... print 'This terminal supports clearning the 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. diff --git a/utils/lit/lit/ShCommands.py b/utils/lit/lit/ShCommands.py index 4550437..9ca9e8c 100644 --- a/utils/lit/lit/ShCommands.py +++ b/utils/lit/lit/ShCommands.py @@ -6,12 +6,12 @@ class Command: def __repr__(self): return 'Command(%r, %r)' % (self.args, self.redirects) - def __cmp__(self, other): + def __eq__(self, other): if not isinstance(other, Command): - return -1 + return False - return cmp((self.args, self.redirects), - (other.args, other.redirects)) + return ((self.args, self.redirects) == + (other.args, other.redirects)) def toShell(self, file): for arg in self.args: @@ -20,20 +20,20 @@ class Command: elif '"' not in arg and '$' not in arg: quoted = '"%s"' % arg else: - raise NotImplementedError,'Unable to quote %r' % arg - print >>file, quoted, + raise NotImplementedError('Unable to quote %r' % arg) + file.write(quoted) # For debugging / validation. import ShUtil dequoted = list(ShUtil.ShLexer(quoted).lex()) if dequoted != [arg]: - raise NotImplementedError,'Unable to quote %r' % 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]), + file.write("%s '%s'" % (r[0][0], r[1])) else: - print >>file, "%s%s '%s'" % (r[0][1], r[0][0], r[1]), + file.write("%s%s '%s'" % (r[0][1], r[0][0], r[1])) class Pipeline: def __init__(self, commands, negate=False, pipe_err=False): @@ -45,22 +45,22 @@ class Pipeline: return 'Pipeline(%r, %r, %r)' % (self.commands, self.negate, self.pipe_err) - def __cmp__(self, other): + def __eq__(self, other): if not isinstance(other, Pipeline): - return -1 + return False - return cmp((self.commands, self.negate, self.pipe_err), - (other.commands, other.negate, self.pipe_err)) + return ((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!' + raise ValueError('Inconsistent "pipefail" attribute!') if self.negate: - print >>file, '!', + file.write('! ') for cmd in self.commands: cmd.toShell(file) if cmd is not self.commands[-1]: - print >>file, '|\n ', + file.write('|\n ') class Seq: def __init__(self, lhs, op, rhs): @@ -72,14 +72,14 @@ class Seq: def __repr__(self): return 'Seq(%r, %r, %r)' % (self.lhs, self.op, self.rhs) - def __cmp__(self, other): + def __eq__(self, other): if not isinstance(other, Seq): - return -1 + return False - return cmp((self.lhs, self.op, self.rhs), - (other.lhs, other.op, other.rhs)) + return ((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 + file.write(' %s\n' % self.op) self.rhs.toShell(file, pipefail) diff --git a/utils/lit/lit/ShUtil.py b/utils/lit/lit/ShUtil.py index 50f7910..fb0689b 100644 --- a/utils/lit/lit/ShUtil.py +++ b/utils/lit/lit/ShUtil.py @@ -1,7 +1,8 @@ +from __future__ import absolute_import import itertools -import Util -from ShCommands import Command, Pipeline, Seq +import lit.Util +from lit.ShCommands import Command, Pipeline, Seq class ShLexer: def __init__(self, data, win32Escapes = False): @@ -74,8 +75,8 @@ class ShLexer: # 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) + lit.Util.warning( + "escape at end of quoted argument in: %r" % self.data) return str str += self.eat() else: @@ -92,8 +93,8 @@ class ShLexer: # 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) + lit.Util.warning( + "escape at end of quoted argument in: %r" % self.data) return str c = self.eat() if c == '"': # @@ -104,7 +105,7 @@ class ShLexer: str += '\\' + c else: str += c - Util.warning("missing quote character in %r" % self.data) + lit.Util.warning("missing quote character in %r" % self.data) return str def lex_arg_checked(self, c): @@ -116,9 +117,11 @@ class ShLexer: reference = self.lex_arg_slow(c) if res is not None: if res != reference: - raise ValueError,"Fast path failure: %r != %r" % (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) + raise ValueError("Fast path failure: %r != %r" % ( + self.pos, end)) return reference def lex_arg(self, c): @@ -166,28 +169,28 @@ class ShLexer: ### class ShParser: - def __init__(self, data, win32Escapes = False): + def __init__(self, data, win32Escapes = False, pipefail = False): self.data = data + self.pipefail = pipefail self.tokens = ShLexer(data, win32Escapes = win32Escapes).lex() def lex(self): - try: - return self.tokens.next() - except StopIteration: - return None + for item in self.tokens: + return item + return None def look(self): - next = self.lex() - if next is not None: - self.tokens = itertools.chain([next], self.tokens) - return next + token = self.lex() + if token is not None: + self.tokens = itertools.chain([token], self.tokens) + return token def parse_command(self): tok = self.lex() if not tok: - raise ValueError,"empty command!" + raise ValueError("empty command!") if isinstance(tok, tuple): - raise ValueError,"syntax error near unexpected token %r" % tok[0] + raise ValueError("syntax error near unexpected token %r" % tok[0]) args = [tok] redirects = [] @@ -212,7 +215,7 @@ class ShParser: op = self.lex() arg = self.lex() if not arg: - raise ValueError,"syntax error near token %r" % op[0] + raise ValueError("syntax error near token %r" % op[0]) redirects.append((op, arg)) return Command(args, redirects) @@ -224,7 +227,7 @@ class ShParser: while self.look() == ('|',): self.lex() commands.append(self.parse_command()) - return Pipeline(commands, negate) + return Pipeline(commands, negate, self.pipefail) def parse(self): lhs = self.parse_pipeline() @@ -234,7 +237,8 @@ class ShParser: assert isinstance(operator, tuple) and len(operator) == 1 if not self.look(): - raise ValueError, "missing argument to operator %r" % operator[0] + raise ValueError( + "missing argument to operator %r" % operator[0]) # FIXME: Operator precedence!! lhs = Seq(lhs, operator[0], self.parse_pipeline()) diff --git a/utils/lit/lit/TestFormats.py b/utils/lit/lit/TestFormats.py index 27085b9..9e0c7a0 100644 --- a/utils/lit/lit/TestFormats.py +++ b/utils/lit/lit/TestFormats.py @@ -1,9 +1,10 @@ +from __future__ import absolute_import import os import sys -import Test -import TestRunner -import Util +import lit.Test +import lit.TestRunner +import lit.Util kIsWindows = sys.platform in ['win32', 'cygwin'] @@ -27,8 +28,8 @@ class GoogleTest(object): localConfig: TestingConfig instance""" try: - lines = Util.capture([path, '--gtest_list_tests'], - env=localConfig.environment) + lines = lit.Util.capture([path, '--gtest_list_tests'], + env=localConfig.environment) if kIsWindows: lines = lines.replace('\r', '') lines = lines.split('\n') @@ -63,7 +64,7 @@ class GoogleTest(object): # Discover the tests in this executable. for testname in self.getGTestTests(execpath, litConfig, localConfig): testPath = path_in_suite + (basename, testname) - yield Test.Test(testSuite, testPath, localConfig) + yield lit.Test.Test(testSuite, testPath, localConfig) def getTestsInDirectory(self, testSuite, path_in_suite, litConfig, localConfig): @@ -100,15 +101,15 @@ class GoogleTest(object): cmd = litConfig.valgrindArgs + cmd if litConfig.noExecute: - return Test.PASS, '' + return lit.Test.PASS, '' - out, err, exitCode = TestRunner.executeCommand( + out, err, exitCode = lit.TestRunner.executeCommand( cmd, env=test.config.environment) if not exitCode: - return Test.PASS,'' + return lit.Test.PASS,'' - return Test.FAIL, out + err + return lit.Test.FAIL, out + err ### @@ -126,16 +127,16 @@ class FileBasedTest(object): 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) + yield lit.Test.Test(testSuite, path_in_suite + (filename,), + localConfig) class ShTest(FileBasedTest): def __init__(self, execute_external = False): self.execute_external = execute_external def execute(self, test, litConfig): - return TestRunner.executeShTest(test, litConfig, - self.execute_external) + return lit.TestRunner.executeShTest(test, litConfig, + self.execute_external) ### @@ -183,9 +184,9 @@ class OneCommandPerFileTest: suffix = path[len(dir):] if suffix.startswith(os.sep): suffix = suffix[1:] - test = Test.Test(testSuite, - path_in_suite + tuple(suffix.split(os.sep)), - localConfig) + test = lit.Test.Test( + testSuite, path_in_suite + tuple(suffix.split(os.sep)), + localConfig) # FIXME: Hack? test.source_path = path yield test @@ -195,7 +196,7 @@ class OneCommandPerFileTest: def execute(self, test, litConfig): if test.config.unsupported: - return (Test.UNSUPPORTED, 'Test is unsupported') + return (lit.Test.UNSUPPORTED, 'Test is unsupported') cmd = list(self.command) @@ -211,11 +212,11 @@ class OneCommandPerFileTest: else: cmd.append(test.getSourcePath()) - out, err, exitCode = TestRunner.executeCommand(cmd) + out, err, exitCode = lit.TestRunner.executeCommand(cmd) diags = out + err if not exitCode and not diags.strip(): - return Test.PASS,'' + return lit.Test.PASS,'' # Try to include some useful information. report = """Command: %s\n""" % ' '.join(["'%s'" % a @@ -225,4 +226,4 @@ class OneCommandPerFileTest: report += "--\n%s--\n""" % open(tmp.name).read() report += """Output:\n--\n%s--""" % diags - return Test.FAIL, report + return lit.Test.FAIL, report diff --git a/utils/lit/lit/TestRunner.py b/utils/lit/lit/TestRunner.py index 8417699..989a992 100644 --- a/utils/lit/lit/TestRunner.py +++ b/utils/lit/lit/TestRunner.py @@ -1,14 +1,16 @@ +from __future__ import absolute_import import os, signal, subprocess, sys -import StringIO - -import ShUtil -import Test -import Util - +import re import platform import tempfile +try: + from io import StringIO +except ImportError: + from StringIO import StringIO -import re +import lit.ShUtil as ShUtil +import lit.Test as Test +import lit.Util as Util class InternalShellError(Exception): def __init__(self, command, message): @@ -66,7 +68,7 @@ def executeShCmd(cmd, cfg, cwd, results): res = executeShCmd(cmd.rhs, cfg, cwd, results) return res - raise ValueError,'Unknown shell command: %r' % cmd.op + raise ValueError('Unknown shell command: %r' % cmd.op) assert isinstance(cmd, ShUtil.Pipeline) procs = [] @@ -245,7 +247,8 @@ def executeScriptInternal(test, litConfig, tmpBase, commands, cwd): cmds = [] for ln in commands: try: - cmds.append(ShUtil.ShParser(ln, litConfig.isWindows).parse()) + cmds.append(ShUtil.ShParser(ln, litConfig.isWindows, + test.config.pipefail).parse()) except: return (Test.FAIL, "shell parser error on: %r" % ln) @@ -256,7 +259,8 @@ def executeScriptInternal(test, litConfig, tmpBase, commands, cwd): results = [] try: exitCode = executeShCmd(cmd, test.config, cwd, results) - except InternalShellError,e: + except InternalShellError: + e = sys.exc_info()[1] exitCode = 127 results.append((e.command, '', e.message, exitCode)) @@ -284,6 +288,8 @@ def executeScript(test, litConfig, tmpBase, commands, cwd): if isWin32CMDEXE: f.write('\nif %ERRORLEVEL% NEQ 0 EXIT\n'.join(commands)) else: + if test.config.pipefail: + f.write('set -o pipefail;') f.write('{ ' + '; } &&\n{ '.join(commands) + '; }') f.write('\n') f.close() @@ -432,23 +438,22 @@ def parseIntegratedTestScript(test, normalize_slashes=False, return script,isXFail,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 + output = StringIO() + output.write(u"Script:\n") + output.write(u"--\n") + output.write(u'\n'.join(script)) + output.write(u"\n--\n") + output.write(u"Exit Code: %r\n\n" % exitCode) if out: - print >>output, "Command Output (stdout):" - print >>output, "--" - output.write(out) - print >>output, "--" + output.write(u"Command Output (stdout):\n") + output.write(u"--\n") + output.write(unicode(out)) + output.write(u"--\n") if err: - print >>output, "Command Output (stderr):" - print >>output, "--" - output.write(err) - print >>output, "--" + output.write(u"Command Output (stderr):\n") + output.write(u"--\n") + output.write(unicode(err)) + output.write(u"--\n") return (status, output.getvalue()) def executeShTest(test, litConfig, useExternalSh, diff --git a/utils/lit/lit/TestingConfig.py b/utils/lit/lit/TestingConfig.py index a1f79a3..f4ff89f 100644 --- a/utils/lit/lit/TestingConfig.py +++ b/utils/lit/lit/TestingConfig.py @@ -1,6 +1,8 @@ import os import sys +PY2 = sys.version_info[0] < 3 + class TestingConfig: """" TestingConfig - Information on the tests inside a suite. @@ -47,7 +49,8 @@ class TestingConfig: test_exec_root = None, test_source_root = None, excludes = [], - available_features = available_features) + available_features = available_features, + pipefail = True) if os.path.exists(path): # FIXME: Improve detection and error reporting of errors in the @@ -58,13 +61,18 @@ class TestingConfig: cfg_globals['lit'] = litConfig cfg_globals['__file__'] = path try: - exec f in cfg_globals + data = f.read() + if PY2: + exec("exec data in cfg_globals") + else: + exec(data, cfg_globals) if litConfig.debug: litConfig.note('... loaded config %r' % path) - except SystemExit,status: + except SystemExit: + e = sys.exc_info()[1] # We allow normal system exit inside a config file to just # return control without error. - if status.args: + if e.args: raise f.close() else: @@ -79,7 +87,7 @@ class TestingConfig: def __init__(self, parent, name, suffixes, test_format, environment, substitutions, unsupported, on_clone, test_exec_root, test_source_root, excludes, - available_features): + available_features, pipefail): self.parent = parent self.name = str(name) self.suffixes = set(suffixes) @@ -92,6 +100,7 @@ class TestingConfig: self.test_source_root = test_source_root self.excludes = set(excludes) self.available_features = set(available_features) + self.pipefail = pipefail def clone(self, path): # FIXME: Chain implementations? @@ -101,7 +110,8 @@ class TestingConfig: self.environment, self.substitutions, self.unsupported, self.on_clone, self.test_exec_root, self.test_source_root, - self.excludes, self.available_features) + self.excludes, self.available_features, + self.pipefail) if cfg.on_clone: cfg.on_clone(self, cfg, path) return cfg diff --git a/utils/lit/lit/Util.py b/utils/lit/lit/Util.py index f294809..6f09eed 100644 --- a/utils/lit/lit/Util.py +++ b/utils/lit/lit/Util.py @@ -34,7 +34,8 @@ def mkdir_p(path): try: os.mkdir(path) - except OSError,e: + except OSError: + e = sys.exc_info()[1] # Ignore EEXIST, which may occur during a race condition. if e.errno != errno.EEXIST: raise @@ -94,7 +95,7 @@ def whichTools(tools, paths): def printHistogram(items, title = 'Items'): import itertools, math - items.sort(key = lambda (_,v): v) + items.sort(key = lambda item: item[1]) maxValue = max([v for _,v in items]) @@ -115,27 +116,25 @@ def printHistogram(items, title = 'Items'): barW = 40 hr = '-' * (barW + 34) - print '\nSlowest %s:' % title - print hr + print('\nSlowest %s:' % title) + print(hr) for name,value in items[-20:]: - print '%.2fs: %s' % (value, name) - print '\n%s Times:' % title - print hr + 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), + print("[%s] :: [%s] :: [%s]" % ('Range'.center((pDigits+1)*2 + 3), 'Percentage'.center(barW), - 'Count'.center(cDigits*2 + 1)) - print hr + '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)) + print("[%*.*fs,%*.*fs) :: [%s%s] :: [%*d/%*d]" % ( + pDigits, pfDigits, i*barH, pDigits, pfDigits, (i+1)*barH, + '*'*w, ' '*(barW-w), cDigits, len(row), cDigits, len(items))) diff --git a/utils/lit/lit/__init__.py b/utils/lit/lit/__init__.py index 3e61bbd..b9f573d 100644 --- a/utils/lit/lit/__init__.py +++ b/utils/lit/lit/__init__.py @@ -1,6 +1,7 @@ """'lit' Testing Tool""" -from main import main +from __future__ import absolute_import +from .main import main __author__ = 'Daniel Dunbar' __email__ = 'daniel@zuster.org' diff --git a/utils/lit/lit/discovery.py b/utils/lit/lit/discovery.py index 64a9510..f76bd22 100644 --- a/utils/lit/lit/discovery.py +++ b/utils/lit/lit/discovery.py @@ -215,7 +215,7 @@ def find_tests_for_inputs(lit_config, inputs): # If there were any errors during test discovery, exit now. if lit_config.numErrors: - print >>sys.stderr, '%d errors, exiting.' % lit_config.numErrors + sys.stderr.write('%d errors, exiting.\n' % lit_config.numErrors) sys.exit(2) return tests @@ -233,7 +233,6 @@ def load_test_suite(inputs): valgrindLeakCheck = False, valgrindArgs = [], noExecute = False, - ignoreStdErr = False, debug = False, isWindows = (platform.system()=='Windows'), params = {}) @@ -242,4 +241,3 @@ def load_test_suite(inputs): # Return a unittest test suite which just runs the tests in order. return unittest.TestSuite([LitTestCase(test, litConfig) for test in tests]) - diff --git a/utils/lit/lit/main.py b/utils/lit/lit/main.py index de97a8e..acf6101 100755 --- a/utils/lit/lit/main.py +++ b/utils/lit/lit/main.py @@ -6,14 +6,13 @@ lit - LLVM Integrated Tester. See lit.pod for more information. """ +from __future__ import absolute_import import math, os, platform, random, re, sys, time, threading, traceback -import ProgressBar -import TestRunner -import Util - -import LitConfig -import Test +import lit.ProgressBar +import lit.LitConfig +import lit.Test +import lit.Util import lit.discovery @@ -59,14 +58,14 @@ class TestingProgressDisplay: if self.progressBar: self.progressBar.clear() - print '%s: %s (%d of %d)' % (test.result.name, test.getFullName(), - self.completed, self.numTests) + 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 + print("%s TEST '%s' FAILED %s" % ('*'*20, test.getFullName(), + '*'*20)) + print(test.output) + print("*" * 20) sys.stdout.flush() @@ -76,6 +75,12 @@ class TestProvider: self.iter = iter(tests) self.lock = threading.Lock() self.startTime = time.time() + self.canceled = False + + def cancel(self): + self.lock.acquire() + self.canceled = True + self.lock.release() def get(self): # Check if we have run out of time. @@ -85,9 +90,12 @@ class TestProvider: # Otherwise take the next test. self.lock.acquire() - try: - item = self.iter.next() - except StopIteration: + if self.canceled: + self.lock.release() + return None + for item in self.iter: + break + else: item = None self.lock.release() return item @@ -115,12 +123,12 @@ class Tester(threading.Thread): except KeyboardInterrupt: # This is a sad hack. Unfortunately subprocess goes # bonkers with ctrl-c and we start forking merrily. - print '\nCtrl-C detected, goodbye.' + print('\nCtrl-C detected, goodbye.') os.kill(0,9) except: if self.litConfig.debug: raise - result = Test.UNRESOLVED + result = lit.Test.UNRESOLVED output = 'Exception during script execution:\n' output += traceback.format_exc() output += '\n' @@ -232,6 +240,9 @@ def main(builtinParameters = {}): group.add_option("", "--show-suites", dest="showSuites", help="Show discovered test suites", action="store_true", default=False) + group.add_option("", "--show-tests", dest="showTests", + help="Show all discovered tests", + action="store_true", default=False) group.add_option("", "--repeat", dest="repeatTests", metavar="N", help="Repeat tests N times (for timing)", action="store", default=None, type=int) @@ -248,7 +259,7 @@ def main(builtinParameters = {}): # I haven't seen this bug occur with 2.5.2 and later, so only enable multiple # threads by default there. if sys.hexversion >= 0x2050200: - opts.numThreads = Util.detectCPUs() + opts.numThreads = lit.Util.detectCPUs() else: opts.numThreads = 1 @@ -264,36 +275,47 @@ def main(builtinParameters = {}): userParams[name] = val # Create the global config object. - litConfig = LitConfig.LitConfig(progname = os.path.basename(sys.argv[0]), - path = opts.path, - quiet = opts.quiet, - useValgrind = opts.useValgrind, - valgrindLeakCheck = opts.valgrindLeakCheck, - valgrindArgs = opts.valgrindArgs, - noExecute = opts.noExecute, - ignoreStdErr = False, - debug = opts.debug, - isWindows = (platform.system()=='Windows'), - params = userParams, - config_prefix = opts.configPrefix) + litConfig = lit.LitConfig.LitConfig( + progname = os.path.basename(sys.argv[0]), + path = opts.path, + quiet = opts.quiet, + useValgrind = opts.useValgrind, + valgrindLeakCheck = opts.valgrindLeakCheck, + valgrindArgs = opts.valgrindArgs, + noExecute = opts.noExecute, + debug = opts.debug, + isWindows = (platform.system()=='Windows'), + params = userParams, + config_prefix = opts.configPrefix) tests = lit.discovery.find_tests_for_inputs(litConfig, inputs) - if opts.showSuites: + if opts.showSuites or opts.showTests: + # Aggregate the tests by suite. suitesAndTests = {} for t in tests: if t.suite not in suitesAndTests: suitesAndTests[t.suite] = [] suitesAndTests[t.suite].append(t) - - print '-- Test Suites --' suitesAndTests = suitesAndTests.items() - suitesAndTests.sort(key = lambda (ts,_): ts.name) - for ts,ts_tests in suitesAndTests: - print ' %s - %d tests' %(ts.name, len(ts_tests)) - print ' Source Root: %s' % ts.source_root - print ' Exec Root : %s' % ts.exec_root - + suitesAndTests.sort(key = lambda item: item[0].name) + + # Show the suites, if requested. + if opts.showSuites: + print('-- Test Suites --') + for ts,ts_tests in suitesAndTests: + print(' %s - %d tests' %(ts.name, len(ts_tests))) + print(' Source Root: %s' % ts.source_root) + print(' Exec Root : %s' % ts.exec_root) + + # Show the tests, if requested. + if opts.showTests: + print('-- Available Tests --') + for ts,ts_tests in suitesAndTests: + ts_tests.sort(key = lambda test: test.path_in_suite) + for test in ts_tests: + print(' %s' % (test.getFullName(),)) + # Select and order the tests. numTotalTests = len(tests) @@ -335,27 +357,38 @@ def main(builtinParameters = {}): if not opts.quiet: if opts.succinct and opts.useProgressBar: try: - tc = ProgressBar.TerminalController() - progressBar = ProgressBar.ProgressBar(tc, header) + tc = lit.ProgressBar.TerminalController() + progressBar = lit.ProgressBar.ProgressBar(tc, header) except ValueError: - print header - progressBar = ProgressBar.SimpleProgressBar('Testing: ') + print(header) + progressBar = lit.ProgressBar.SimpleProgressBar('Testing: ') else: - print header + print(header) startTime = time.time() display = TestingProgressDisplay(opts, len(tests), progressBar) provider = TestProvider(tests, opts.maxTime) + + try: + import win32api + except ImportError: + pass + else: + def console_ctrl_handler(type): + provider.cancel() + return True + win32api.SetConsoleCtrlHandler(console_ctrl_handler, True) + runTests(opts.numThreads, litConfig, provider, display) display.finish() if not opts.quiet: - print 'Testing Time: %.2fs'%(time.time() - startTime) + 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) + t.setResult(lit.Test.UNRESOLVED, '', 0.0) # List test results organized by kind. hasFailures = False @@ -368,16 +401,16 @@ def main(builtinParameters = {}): hasFailures = True # FIXME: Show unresolved and (optionally) unsupported tests. - for title,code in (('Unexpected Passing Tests', Test.XPASS), - ('Failing Tests', Test.FAIL)): + for title,code in (('Unexpected Passing Tests', lit.Test.XPASS), + ('Failing Tests', lit.Test.FAIL)): elts = byCode.get(code) if not elts: continue - print '*'*20 - print '%s (%d):' % (title, len(elts)) + print('*'*20) + print('%s (%d):' % (title, len(elts))) for t in elts: - print ' %s' % t.getFullName() - print + print(' %s' % t.getFullName()) + sys.stdout.write('\n') if opts.timeTests: # Collate, in case we repeated tests. @@ -387,30 +420,30 @@ def main(builtinParameters = {}): times[key] = times.get(key, 0.) + t.elapsed byTime = list(times.items()) - byTime.sort(key = lambda (name,elapsed): elapsed) + byTime.sort(key = lambda item: item[1]) if byTime: - Util.printHistogram(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),): + lit.Util.printHistogram(byTime, title='Tests') + + for name,code in (('Expected Passes ', lit.Test.PASS), + ('Expected Failures ', lit.Test.XFAIL), + ('Unsupported Tests ', lit.Test.UNSUPPORTED), + ('Unresolved Tests ', lit.Test.UNRESOLVED), + ('Unexpected Passes ', lit.Test.XPASS), + ('Unexpected Failures', lit.Test.FAIL),): if opts.quiet and not code.isFailure: continue N = len(byCode.get(code,[])) if N: - print ' %s: %d' % (name,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.stderr.write('\n%d error(s), exiting.\n' % litConfig.numErrors) sys.exit(2) # Warn about warnings. if litConfig.numWarnings: - print >>sys.stderr, '\n%d warning(s) in tests.' % litConfig.numWarnings + sys.stderr.write('\n%d warning(s) in tests.\n' % litConfig.numWarnings) if hasFailures: sys.exit(1) diff --git a/utils/lit/tests/discovery.py b/utils/lit/tests/discovery.py index 56d9dd0..be98c4b 100644 --- a/utils/lit/tests/discovery.py +++ b/utils/lit/tests/discovery.py @@ -1,7 +1,8 @@ # Check the basic discovery process, including a sub-suite. # # RUN: %{lit} %{inputs}/discovery \ -# RUN: -j 1 --debug --no-execute --show-suites -v > %t.out 2> %t.err +# RUN: -j 1 --debug --show-tests --show-suites --max-tests 0 \ +# RUN: -v > %t.out 2> %t.err # RUN: FileCheck --check-prefix=CHECK-BASIC-OUT < %t.out %s # RUN: FileCheck --check-prefix=CHECK-BASIC-ERR < %t.err %s # @@ -17,12 +18,13 @@ # CHECK-BASIC-OUT: Source Root: {{.*/discovery$}} # CHECK-BASIC-OUT: Exec Root : {{.*/discovery$}} # -# CHECK-BASIC-OUT: -- Testing: 5 tests, 1 threads -- -# CHECK-BASIC-OUT: PASS: sub-suite :: test-one -# CHECK-BASIC-OUT: PASS: sub-suite :: test-two -# CHECK-BASIC-OUT: PASS: top-level-suite :: subdir/test-three -# CHECK-BASIC-OUT: PASS: top-level-suite :: test-one -# CHECK-BASIC-OUT: PASS: top-level-suite :: test-two +# CHECK-BASIC-OUT: -- Available Tests -- +# CHECK-BASIC-OUT: sub-suite :: test-one +# CHECK-BASIC-OUT: sub-suite :: test-two +# CHECK-BASIC-OUT: top-level-suite :: subdir/test-three +# CHECK-BASIC-OUT: top-level-suite :: test-one +# CHECK-BASIC-OUT: top-level-suite :: test-two +# CHECK-BASIC-OUT: -- Testing: 0 # Check discovery when exact test names are given. @@ -30,18 +32,20 @@ # RUN: %{lit} \ # RUN: %{inputs}/discovery/subdir/test-three.py \ # RUN: %{inputs}/discovery/subsuite/test-one.txt \ -# RUN: -j 1 --no-execute --show-suites -v > %t.out +# RUN: -j 1 --show-tests --show-suites --max-tests 0 -v > %t.out # RUN: FileCheck --check-prefix=CHECK-EXACT-TEST < %t.out %s # -# CHECK-EXACT-TEST: -- Testing: 2 tests, 1 threads -- -# CHECK-EXACT-TEST: PASS: sub-suite :: test-one -# CHECK-EXACT-TEST: PASS: top-level-suite :: subdir/test-three +# CHECK-EXACT-TEST: -- Available Tests -- +# CHECK-EXACT-TEST: sub-suite :: test-one +# CHECK-EXACT-TEST: top-level-suite :: subdir/test-three +# CHECK-EXACT-TEST: -- Testing: 0 # Check discovery when using an exec path. # # RUN: %{lit} %{inputs}/exec-discovery \ -# RUN: -j 1 --debug --no-execute --show-suites -v > %t.out 2> %t.err +# RUN: -j 1 --debug --show-tests --show-suites --max-tests 0 \ +# RUN: -v > %t.out 2> %t.err # RUN: FileCheck --check-prefix=CHECK-ASEXEC-OUT < %t.out %s # RUN: FileCheck --check-prefix=CHECK-ASEXEC-ERR < %t.err %s # @@ -60,13 +64,13 @@ # CHECK-ASEXEC-OUT: Source Root: {{.*/discovery$}} # CHECK-ASEXEC-OUT: Exec Root : {{.*/exec-discovery$}} # -# CHECK-ASEXEC-OUT: -- Testing: 5 tests, 1 threads -- -# CHECK-ASEXEC-OUT: PASS: sub-suite :: test-one -# CHECK-ASEXEC-OUT: PASS: sub-suite :: test-two -# CHECK-ASEXEC-OUT: PASS: top-level-suite :: subdir/test-three -# CHECK-ASEXEC-OUT: PASS: top-level-suite :: test-one -# CHECK-ASEXEC-OUT: PASS: top-level-suite :: test-two - +# CHECK-ASEXEC-OUT: -- Available Tests -- +# CHECK-ASEXEC-OUT: sub-suite :: test-one +# CHECK-ASEXEC-OUT: sub-suite :: test-two +# CHECK-ASEXEC-OUT: top-level-suite :: subdir/test-three +# CHECK-ASEXEC-OUT: top-level-suite :: test-one +# CHECK-ASEXEC-OUT: top-level-suite :: test-two +# CHECK-ASEXEC-OUT: -- Testing: 0 # Check discovery when exact test names are given. # @@ -74,11 +78,12 @@ # # RUN: %{lit} \ # RUN: %{inputs}/exec-discovery/subdir/test-three.py \ -# RUN: -j 1 --no-execute --show-suites -v > %t.out +# RUN: -j 1 --show-tests --show-suites --max-tests 0 -v > %t.out # RUN: FileCheck --check-prefix=CHECK-ASEXEC-EXACT-TEST < %t.out %s # -# CHECK-ASEXEC-EXACT-TEST: -- Testing: 1 tests, 1 threads -- -# CHECK-ASEXEC-EXACT-TEST: PASS: top-level-suite :: subdir/test-three +# CHECK-ASEXEC-EXACT-TEST: -- Available Tests -- +# CHECK-ASEXEC-EXACT-TEST: top-level-suite :: subdir/test-three +# CHECK-ASEXEC-EXACT-TEST: -- Testing: 0 # Check that we don't recurse infinitely when loading an site specific test @@ -86,11 +91,12 @@ # # RUN: %{lit} \ # RUN: %{inputs}/exec-discovery-in-tree/obj/ \ -# RUN: -j 1 --no-execute --show-suites -v > %t.out +# RUN: -j 1 --show-tests --show-suites --max-tests 0 -v > %t.out # RUN: FileCheck --check-prefix=CHECK-ASEXEC-INTREE < %t.out %s # # CHECK-ASEXEC-INTREE: exec-discovery-in-tree-suite - 1 tests # CHECK-ASEXEC-INTREE-NEXT: Source Root: {{.*/exec-discovery-in-tree$}} # CHECK-ASEXEC-INTREE-NEXT: Exec Root : {{.*/exec-discovery-in-tree/obj$}} -# CHECK-ASEXEC-INTREE-NEXT: -- Testing: 1 tests, 1 threads -- -# CHECK-ASEXEC-INTREE-NEXT: PASS: exec-discovery-in-tree-suite :: test-one +# CHECK-ASEXEC-INTREE-NEXT: -- Available Tests -- +# CHECK-ASEXEC-INTREE-NEXT: exec-discovery-in-tree-suite :: test-one +# CHECK-ASEXEC-INTREE: -- Testing: 0 diff --git a/utils/not/not.cpp b/utils/not/not.cpp index 9a924b5..ebd1618 100644 --- a/utils/not/not.cpp +++ b/utils/not/not.cpp @@ -13,15 +13,33 @@ using namespace llvm; int main(int argc, const char **argv) { - sys::Path Program = sys::Program::FindProgramByName(argv[1]); + bool ExpectCrash = false; + + ++argv; + --argc; + + if (argc > 0 && StringRef(argv[0]) == "--crash") { + ++argv; + --argc; + ExpectCrash = true; + } + + if (argc == 0) + return 1; + + std::string Program = sys::FindProgramByName(argv[0]); std::string ErrMsg; - int Result = sys::Program::ExecuteAndWait(Program, argv + 1, 0, 0, 0, 0, - &ErrMsg); + int Result = sys::ExecuteAndWait(Program, argv, 0, 0, 0, 0, &ErrMsg); if (Result < 0) { errs() << "Error: " << ErrMsg << "\n"; + if (ExpectCrash) + return 0; return 1; } + if (ExpectCrash) + return 1; + return Result == 0; } diff --git a/utils/release/test-release.sh b/utils/release/test-release.sh index 104945b..6c17b46 100755 --- a/utils/release/test-release.sh +++ b/utils/release/test-release.sh @@ -480,7 +480,7 @@ for Flavor in $Flavors ; do build_dragonegg 2 $Flavor $llvmCore_de_phase2_installdir $dragonegg_phase2_objdir ######################################################################## - # Phase 3: Build llvmCore with newly built clang from phase 2. + # Phase 3: Build llvmCore with newly built dragonegg from phase 2. c_compiler="$gcc_compiler -fplugin=$dragonegg_phase2_objdir/dragonegg.so" cxx_compiler="$gxx_compiler -fplugin=$dragonegg_phase2_objdir/dragonegg.so" echo "# Phase 3: Building llvmCore with dragonegg" diff --git a/utils/vim/llvm.vim b/utils/vim/llvm.vim index 830476f..6c87cff 100644 --- a/utils/vim/llvm.vim +++ b/utils/vim/llvm.vim @@ -50,12 +50,13 @@ syn keyword llvmKeyword msp430_intrcc naked nest noalias nocapture syn keyword llvmKeyword noimplicitfloat noinline nonlazybind noredzone noreturn syn keyword llvmKeyword nounwind optsize personality private protected syn keyword llvmKeyword ptx_device ptx_kernel readnone readonly release -syn keyword llvmKeyword returns_twice section seq_cst sideeffect signext -syn keyword llvmKeyword singlethread spir_func spir_kernel sret ssp sspreq -syn keyword llvmKeyword sspstrong tail target thread_local to triple -syn keyword llvmKeyword unnamed_addr unordered uwtable volatile weak weak_odr -syn keyword llvmKeyword x86_fastcallcc x86_stdcallcc x86_thiscallcc zeroext -syn keyword llvmKeyword sanitize_thread sanitize_memory +syn keyword llvmKeyword returns_twice sanitize_thread sanitize_memory +syn keyword llvmKeyword section seq_cst sideeffect signext singlethread +syn keyword llvmKeyword spir_func spir_kernel sret ssp sspreq sspstrong +syn keyword llvmKeyword tail target thread_local to triple unnamed_addr +syn keyword llvmKeyword unordered uwtable volatile weak weak_odr +syn keyword llvmKeyword x86_fastcallcc x86_stdcallcc x86_thiscallcc x86_64_sysvcc +syn keyword llvmKeyword x86_64_win64cc zeroext " Obsolete keywords. syn keyword llvmError getresult begin end |