diff options
author | dim <dim@FreeBSD.org> | 2011-10-20 21:10:27 +0000 |
---|---|---|
committer | dim <dim@FreeBSD.org> | 2011-10-20 21:10:27 +0000 |
commit | 7b3392326c40c3c20697816acae597ba7b3144eb (patch) | |
tree | 2cbcf22585e99f8a87d12d5ff94f392c0d266819 /utils | |
parent | 1176aa52646fe641a4243a246aa7f960c708a274 (diff) | |
download | FreeBSD-src-7b3392326c40c3c20697816acae597ba7b3144eb.zip FreeBSD-src-7b3392326c40c3c20697816acae597ba7b3144eb.tar.gz |
Vendor import of llvm release_30 branch r142614:
http://llvm.org/svn/llvm-project/llvm/branches/release_30@142614
Diffstat (limited to 'utils')
117 files changed, 5190 insertions, 15704 deletions
diff --git a/utils/TableGen/ARMDecoderEmitter.cpp b/utils/TableGen/ARMDecoderEmitter.cpp index 8a5dc8b..145b96d 100644 --- a/utils/TableGen/ARMDecoderEmitter.cpp +++ b/utils/TableGen/ARMDecoderEmitter.cpp @@ -18,10 +18,10 @@ #include "ARMDecoderEmitter.h" #include "CodeGenTarget.h" -#include "Record.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/TableGen/Record.h" #include <vector> #include <map> @@ -41,7 +41,7 @@ using namespace llvm; ENTRY(ARM_FORMAT_BRFRM, 2) \ ENTRY(ARM_FORMAT_BRMISCFRM, 3) \ ENTRY(ARM_FORMAT_DPFRM, 4) \ - ENTRY(ARM_FORMAT_DPSOREGFRM, 5) \ + ENTRY(ARM_FORMAT_DPSOREGREGFRM, 5) \ ENTRY(ARM_FORMAT_LDFRM, 6) \ ENTRY(ARM_FORMAT_STFRM, 7) \ ENTRY(ARM_FORMAT_LDMISCFRM, 8) \ @@ -77,7 +77,8 @@ using namespace llvm; ENTRY(ARM_FORMAT_N3RegVecSh, 38) \ ENTRY(ARM_FORMAT_NVecExtract, 39) \ ENTRY(ARM_FORMAT_NVecMulScalar, 40) \ - ENTRY(ARM_FORMAT_NVTBL, 41) + ENTRY(ARM_FORMAT_NVTBL, 41) \ + ENTRY(ARM_FORMAT_DPSOREGIMMFRM, 42) // ARM instruction format specifies the encoding used by the instruction. #define ENTRY(n, v) n = v, @@ -1614,15 +1615,6 @@ ARMDEBackend::populateInstruction(const CodeGenInstruction &CGI, if (!thumbInstruction(Form)) return false; - // A8.6.189 STM / STMIA / STMEA -- Encoding T1 - // There's only STMIA_UPD for Thumb1. - if (Name == "tSTMIA") - return false; - - // On Darwin R9 is call-clobbered. Ignore the non-Darwin counterparts. - if (Name == "tBL" || Name == "tBLXi" || Name == "tBLXr") - return false; - // A8.6.25 BX. Use the generic tBX_Rm, ignore tBX_RET and tBX_RET_vararg. if (Name == "tBX_RET" || Name == "tBX_RET_vararg") return false; @@ -1654,14 +1646,12 @@ ARMDEBackend::populateInstruction(const CodeGenInstruction &CGI, // Resolve conflicts: // - // tBfar conflicts with tBLr9 // t2LDMIA_RET conflict with t2LDM (ditto) // tMOVCCi conflicts with tMOVi8 // tMOVCCr conflicts with tMOVgpr2gpr // tLDRcp conflicts with tLDRspi // t2MOVCCi16 conflicts with tMOVi16 - if (Name == "tBfar" || - Name == "t2LDMIA_RET" || + if (Name == "t2LDMIA_RET" || Name == "tMOVCCi" || Name == "tMOVCCr" || Name == "tLDRcp" || Name == "t2MOVCCi16") diff --git a/utils/TableGen/ARMDecoderEmitter.h b/utils/TableGen/ARMDecoderEmitter.h index 1faeb91..486f899 100644 --- a/utils/TableGen/ARMDecoderEmitter.h +++ b/utils/TableGen/ARMDecoderEmitter.h @@ -15,9 +15,8 @@ #ifndef ARMDECODEREMITTER_H #define ARMDECODEREMITTER_H -#include "TableGenBackend.h" - #include "llvm/Support/DataTypes.h" +#include "llvm/TableGen/TableGenBackend.h" namespace llvm { diff --git a/utils/TableGen/AsmMatcherEmitter.cpp b/utils/TableGen/AsmMatcherEmitter.cpp index a6a4fec..8b86c23 100644 --- a/utils/TableGen/AsmMatcherEmitter.cpp +++ b/utils/TableGen/AsmMatcherEmitter.cpp @@ -98,8 +98,6 @@ #include "AsmMatcherEmitter.h" #include "CodeGenTarget.h" -#include "Error.h" -#include "Record.h" #include "StringMatcher.h" #include "llvm/ADT/OwningPtr.h" #include "llvm/ADT/PointerUnion.h" @@ -109,6 +107,8 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" #include <map> #include <set> using namespace llvm; @@ -914,17 +914,17 @@ void AsmMatcherInfo:: BuildRegisterClasses(SmallPtrSet<Record*, 16> &SingletonRegisters) { const std::vector<CodeGenRegister*> &Registers = Target.getRegBank().getRegisters(); - const std::vector<CodeGenRegisterClass> &RegClassList = - Target.getRegisterClasses(); + ArrayRef<CodeGenRegisterClass*> RegClassList = + Target.getRegBank().getRegClasses(); // The register sets used for matching. std::set< std::set<Record*> > RegisterSets; // Gather the defined sets. - for (std::vector<CodeGenRegisterClass>::const_iterator it = + for (ArrayRef<CodeGenRegisterClass*>::const_iterator it = RegClassList.begin(), ie = RegClassList.end(); it != ie; ++it) - RegisterSets.insert(std::set<Record*>(it->getOrder().begin(), - it->getOrder().end())); + RegisterSets.insert(std::set<Record*>( + (*it)->getOrder().begin(), (*it)->getOrder().end())); // Add any required singleton sets. for (SmallPtrSet<Record*, 16>::iterator it = SingletonRegisters.begin(), @@ -996,18 +996,23 @@ BuildRegisterClasses(SmallPtrSet<Record*, 16> &SingletonRegisters) { } // Name the register classes which correspond to a user defined RegisterClass. - for (std::vector<CodeGenRegisterClass>::const_iterator + for (ArrayRef<CodeGenRegisterClass*>::const_iterator it = RegClassList.begin(), ie = RegClassList.end(); it != ie; ++it) { - ClassInfo *CI = RegisterSetClasses[std::set<Record*>(it->getOrder().begin(), - it->getOrder().end())]; + const CodeGenRegisterClass &RC = **it; + // Def will be NULL for non-user defined register classes. + Record *Def = RC.getDef(); + if (!Def) + continue; + ClassInfo *CI = RegisterSetClasses[std::set<Record*>(RC.getOrder().begin(), + RC.getOrder().end())]; if (CI->ValueName.empty()) { - CI->ClassName = it->getName(); - CI->Name = "MCK_" + it->getName(); - CI->ValueName = it->getName(); + CI->ClassName = RC.getName(); + CI->Name = "MCK_" + RC.getName(); + CI->ValueName = RC.getName(); } else - CI->ValueName = CI->ValueName + "," + it->getName(); + CI->ValueName = CI->ValueName + "," + RC.getName(); - RegisterClassClasses.insert(std::make_pair(it->TheDef, CI)); + RegisterClassClasses.insert(std::make_pair(Def, CI)); } // Populate the map for individual registers. @@ -2172,21 +2177,14 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { OS << "\n#ifdef GET_ASSEMBLER_HEADER\n"; OS << "#undef GET_ASSEMBLER_HEADER\n"; OS << " // This should be included into the middle of the declaration of\n"; - OS << " // your subclasses implementation of TargetAsmParser.\n"; + OS << " // your subclasses implementation of MCTargetAsmParser.\n"; OS << " unsigned ComputeAvailableFeatures(uint64_t FeatureBits) const;\n"; - OS << " enum MatchResultTy {\n"; - OS << " Match_ConversionFail,\n"; - OS << " Match_InvalidOperand,\n"; - OS << " Match_MissingFeature,\n"; - OS << " Match_MnemonicFail,\n"; - OS << " Match_Success\n"; - OS << " };\n"; OS << " bool ConvertToMCInst(unsigned Kind, MCInst &Inst, " << "unsigned Opcode,\n" << " const SmallVectorImpl<MCParsedAsmOperand*> " << "&Operands);\n"; OS << " bool MnemonicIsValid(StringRef Mnemonic);\n"; - OS << " MatchResultTy MatchInstructionImpl(\n"; + OS << " unsigned MatchInstructionImpl(\n"; OS << " const SmallVectorImpl<MCParsedAsmOperand*> &Operands,\n"; OS << " MCInst &Inst, unsigned &ErrorInfo);\n"; @@ -2328,7 +2326,7 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { OS << "}\n\n"; // Finally, build the match function. - OS << Target.getName() << ClassName << "::MatchResultTy " + OS << "unsigned " << Target.getName() << ClassName << "::\n" << "MatchInstructionImpl(const SmallVectorImpl<MCParsedAsmOperand*>" << " &Operands,\n"; @@ -2355,7 +2353,9 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { OS << " }\n\n"; OS << " // Some state to try to produce better error messages.\n"; - OS << " bool HadMatchOtherThanFeatures = false;\n\n"; + OS << " bool HadMatchOtherThanFeatures = false;\n"; + OS << " bool HadMatchOtherThanPredicate = false;\n"; + OS << " unsigned RetCode = Match_InvalidOperand;\n"; OS << " // Set ErrorInfo to the operand that mismatches if it is\n"; OS << " // wrong for all instances of the instruction.\n"; OS << " ErrorInfo = ~0U;\n"; @@ -2411,6 +2411,18 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { OS << " return Match_ConversionFail;\n"; OS << "\n"; + // Verify the instruction with the target-specific match predicate function. + OS << " // We have a potential match. Check the target predicate to\n" + << " // handle any context sensitive constraints.\n" + << " unsigned MatchResult;\n" + << " if ((MatchResult = checkTargetMatchPredicate(Inst)) !=" + << " Match_Success) {\n" + << " Inst.clear();\n" + << " RetCode = MatchResult;\n" + << " HadMatchOtherThanPredicate = true;\n" + << " continue;\n" + << " }\n\n"; + // Call the post-processing function, if used. std::string InsnCleanupFn = AsmParser->getValueAsString("AsmParserInstCleanup"); @@ -2421,8 +2433,9 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { OS << " }\n\n"; OS << " // Okay, we had no match. Try to return a useful error code.\n"; - OS << " if (HadMatchOtherThanFeatures) return Match_MissingFeature;\n"; - OS << " return Match_InvalidOperand;\n"; + OS << " if (HadMatchOtherThanPredicate || !HadMatchOtherThanFeatures)"; + OS << " return RetCode;\n"; + OS << " return Match_MissingFeature;\n"; OS << "}\n\n"; if (Info.OperandMatchInfo.size()) diff --git a/utils/TableGen/AsmMatcherEmitter.h b/utils/TableGen/AsmMatcherEmitter.h index c13adf3..e04ac10 100644 --- a/utils/TableGen/AsmMatcherEmitter.h +++ b/utils/TableGen/AsmMatcherEmitter.h @@ -15,7 +15,7 @@ #ifndef ASMMATCHER_EMITTER_H #define ASMMATCHER_EMITTER_H -#include "TableGenBackend.h" +#include "llvm/TableGen/TableGenBackend.h" #include <cassert> namespace llvm { diff --git a/utils/TableGen/AsmWriterEmitter.cpp b/utils/TableGen/AsmWriterEmitter.cpp index f44f050..3123e11 100644 --- a/utils/TableGen/AsmWriterEmitter.cpp +++ b/utils/TableGen/AsmWriterEmitter.cpp @@ -14,13 +14,13 @@ #include "AsmWriterEmitter.h" #include "AsmWriterInst.h" -#include "Error.h" #include "CodeGenTarget.h" -#include "Record.h" #include "StringToOffsetTable.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/MathExtras.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" #include <algorithm> using namespace llvm; @@ -703,8 +703,8 @@ void AsmWriterEmitter::EmitRegIsInRegClass(raw_ostream &O) { CodeGenTarget Target(Records); // Enumerate the register classes. - const std::vector<CodeGenRegisterClass> &RegisterClasses = - Target.getRegisterClasses(); + ArrayRef<CodeGenRegisterClass*> RegisterClasses = + Target.getRegBank().getRegClasses(); O << "namespace { // Register classes\n"; O << " enum RegClass {\n"; @@ -712,7 +712,7 @@ void AsmWriterEmitter::EmitRegIsInRegClass(raw_ostream &O) { // Emit the register enum value for each RegisterClass. for (unsigned I = 0, E = RegisterClasses.size(); I != E; ++I) { if (I != 0) O << ",\n"; - O << " RC_" << RegisterClasses[I].TheDef->getName(); + O << " RC_" << RegisterClasses[I]->getName(); } O << "\n };\n"; @@ -729,10 +729,10 @@ void AsmWriterEmitter::EmitRegIsInRegClass(raw_ostream &O) { O << " default: break;\n"; for (unsigned I = 0, E = RegisterClasses.size(); I != E; ++I) { - const CodeGenRegisterClass &RC = RegisterClasses[I]; + const CodeGenRegisterClass &RC = *RegisterClasses[I]; // Give the register class a legal C name if it's anonymous. - std::string Name = RC.TheDef->getName(); + std::string Name = RC.getName(); O << " case RC_" << Name << ":\n"; // Emit the register list now. diff --git a/utils/TableGen/AsmWriterEmitter.h b/utils/TableGen/AsmWriterEmitter.h index 84c925b..731e31c 100644 --- a/utils/TableGen/AsmWriterEmitter.h +++ b/utils/TableGen/AsmWriterEmitter.h @@ -15,7 +15,7 @@ #ifndef ASMWRITER_EMITTER_H #define ASMWRITER_EMITTER_H -#include "TableGenBackend.h" +#include "llvm/TableGen/TableGenBackend.h" #include <map> #include <vector> #include <cassert> diff --git a/utils/TableGen/AsmWriterInst.cpp b/utils/TableGen/AsmWriterInst.cpp index fdf447f..350a2cc 100644 --- a/utils/TableGen/AsmWriterInst.cpp +++ b/utils/TableGen/AsmWriterInst.cpp @@ -13,8 +13,8 @@ #include "AsmWriterInst.h" #include "CodeGenTarget.h" -#include "Record.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/TableGen/Record.h" using namespace llvm; diff --git a/utils/TableGen/CMakeLists.txt b/utils/TableGen/CMakeLists.txt index 4d71259..02ebd67 100644 --- a/utils/TableGen/CMakeLists.txt +++ b/utils/TableGen/CMakeLists.txt @@ -1,18 +1,12 @@ set(LLVM_REQUIRES_EH 1) set(LLVM_REQUIRES_RTTI 1) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${LLVM_TOOLS_BINARY_DIR}) - -add_llvm_utility(tblgen +add_tablegen(llvm-tblgen LLVM ARMDecoderEmitter.cpp AsmMatcherEmitter.cpp AsmWriterEmitter.cpp AsmWriterInst.cpp CallingConvEmitter.cpp - ClangASTNodesEmitter.cpp - ClangAttrEmitter.cpp - ClangDiagnosticsEmitter.cpp - ClangSACheckersEmitter.cpp CodeEmitterGen.cpp CodeGenDAGPatterns.cpp CodeGenInstruction.cpp @@ -25,36 +19,18 @@ add_llvm_utility(tblgen DAGISelMatcher.cpp DisassemblerEmitter.cpp EDEmitter.cpp - Error.cpp FastISelEmitter.cpp FixedLenDecoderEmitter.cpp InstrEnumEmitter.cpp InstrInfoEmitter.cpp IntrinsicEmitter.cpp - LLVMCConfigurationEmitter.cpp - NeonEmitter.cpp - OptParserEmitter.cpp PseudoLoweringEmitter.cpp - Record.cpp RegisterInfoEmitter.cpp SetTheory.cpp StringMatcher.cpp SubtargetEmitter.cpp - TGLexer.cpp - TGParser.cpp TGValueTypes.cpp TableGen.cpp - TableGenBackend.cpp X86DisassemblerTables.cpp X86RecognizableInstr.cpp ) - -target_link_libraries(tblgen LLVMSupport) -if( MINGW ) - target_link_libraries(tblgen imagehlp psapi) -endif( MINGW ) -if( LLVM_ENABLE_THREADS AND HAVE_LIBPTHREAD AND NOT BEOS ) - target_link_libraries(tblgen pthread) -endif() - -install(TARGETS tblgen RUNTIME DESTINATION bin) diff --git a/utils/TableGen/CallingConvEmitter.cpp b/utils/TableGen/CallingConvEmitter.cpp index c51afd8..fcdaa08 100644 --- a/utils/TableGen/CallingConvEmitter.cpp +++ b/utils/TableGen/CallingConvEmitter.cpp @@ -13,8 +13,8 @@ //===----------------------------------------------------------------------===// #include "CallingConvEmitter.h" -#include "Record.h" #include "CodeGenTarget.h" +#include "llvm/TableGen/Record.h" using namespace llvm; void CallingConvEmitter::run(raw_ostream &O) { diff --git a/utils/TableGen/CallingConvEmitter.h b/utils/TableGen/CallingConvEmitter.h index 431c33b..7bddd6c 100644 --- a/utils/TableGen/CallingConvEmitter.h +++ b/utils/TableGen/CallingConvEmitter.h @@ -15,7 +15,7 @@ #ifndef CALLINGCONV_EMITTER_H #define CALLINGCONV_EMITTER_H -#include "TableGenBackend.h" +#include "llvm/TableGen/TableGenBackend.h" #include <cassert> namespace llvm { diff --git a/utils/TableGen/ClangASTNodesEmitter.cpp b/utils/TableGen/ClangASTNodesEmitter.cpp deleted file mode 100644 index d9d5a3c..0000000 --- a/utils/TableGen/ClangASTNodesEmitter.cpp +++ /dev/null @@ -1,168 +0,0 @@ -//=== ClangASTNodesEmitter.cpp - Generate Clang AST node tables -*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// These tablegen backends emit Clang AST node tables -// -//===----------------------------------------------------------------------===// - -#include "ClangASTNodesEmitter.h" -#include <set> -using namespace llvm; - -//===----------------------------------------------------------------------===// -// Statement Node Tables (.inc file) generation. -//===----------------------------------------------------------------------===// - -// Returns the first and last non-abstract subrecords -// Called recursively to ensure that nodes remain contiguous -std::pair<Record *, Record *> ClangASTNodesEmitter::EmitNode( - const ChildMap &Tree, - raw_ostream &OS, - Record *Base) { - std::string BaseName = macroName(Base->getName()); - - ChildIterator i = Tree.lower_bound(Base), e = Tree.upper_bound(Base); - - Record *First = 0, *Last = 0; - // This might be the pseudo-node for Stmt; don't assume it has an Abstract - // bit - if (Base->getValue("Abstract") && !Base->getValueAsBit("Abstract")) - First = Last = Base; - - for (; i != e; ++i) { - Record *R = i->second; - bool Abstract = R->getValueAsBit("Abstract"); - std::string NodeName = macroName(R->getName()); - - OS << "#ifndef " << NodeName << "\n"; - OS << "# define " << NodeName << "(Type, Base) " - << BaseName << "(Type, Base)\n"; - OS << "#endif\n"; - - if (Abstract) - OS << "ABSTRACT_" << macroName(Root.getName()) << "(" << NodeName << "(" - << R->getName() << ", " << baseName(*Base) << "))\n"; - else - OS << NodeName << "(" << R->getName() << ", " - << baseName(*Base) << ")\n"; - - if (Tree.find(R) != Tree.end()) { - const std::pair<Record *, Record *> &Result - = EmitNode(Tree, OS, R); - if (!First && Result.first) - First = Result.first; - if (Result.second) - Last = Result.second; - } else { - if (!Abstract) { - Last = R; - - if (!First) - First = R; - } - } - - OS << "#undef " << NodeName << "\n\n"; - } - - if (First) { - assert (Last && "Got a first node but not a last node for a range!"); - if (Base == &Root) - OS << "LAST_" << macroName(Root.getName()) << "_RANGE("; - else - OS << macroName(Root.getName()) << "_RANGE("; - OS << Base->getName() << ", " << First->getName() << ", " - << Last->getName() << ")\n\n"; - } - - return std::make_pair(First, Last); -} - -void ClangASTNodesEmitter::run(raw_ostream &OS) { - // Write the preamble - OS << "#ifndef ABSTRACT_" << macroName(Root.getName()) << "\n"; - OS << "# define ABSTRACT_" << macroName(Root.getName()) << "(Type) Type\n"; - OS << "#endif\n"; - - OS << "#ifndef " << macroName(Root.getName()) << "_RANGE\n"; - OS << "# define " - << macroName(Root.getName()) << "_RANGE(Base, First, Last)\n"; - OS << "#endif\n\n"; - - OS << "#ifndef LAST_" << macroName(Root.getName()) << "_RANGE\n"; - OS << "# define LAST_" - << macroName(Root.getName()) << "_RANGE(Base, First, Last) " - << macroName(Root.getName()) << "_RANGE(Base, First, Last)\n"; - OS << "#endif\n\n"; - - // Emit statements - const std::vector<Record*> Stmts - = Records.getAllDerivedDefinitions(Root.getName()); - - ChildMap Tree; - - for (unsigned i = 0, e = Stmts.size(); i != e; ++i) { - Record *R = Stmts[i]; - - if (R->getValue("Base")) - Tree.insert(std::make_pair(R->getValueAsDef("Base"), R)); - else - Tree.insert(std::make_pair(&Root, R)); - } - - EmitNode(Tree, OS, &Root); - - OS << "#undef " << macroName(Root.getName()) << "\n"; - OS << "#undef " << macroName(Root.getName()) << "_RANGE\n"; - OS << "#undef LAST_" << macroName(Root.getName()) << "_RANGE\n"; - OS << "#undef ABSTRACT_" << macroName(Root.getName()) << "\n"; -} - -void ClangDeclContextEmitter::run(raw_ostream &OS) { - // FIXME: Find a .td file format to allow for this to be represented better. - - OS << "#ifndef DECL_CONTEXT\n"; - OS << "# define DECL_CONTEXT(DECL)\n"; - OS << "#endif\n"; - - OS << "#ifndef DECL_CONTEXT_BASE\n"; - OS << "# define DECL_CONTEXT_BASE(DECL) DECL_CONTEXT(DECL)\n"; - OS << "#endif\n"; - - typedef std::set<Record*> RecordSet; - typedef std::vector<Record*> RecordVector; - - RecordVector DeclContextsVector - = Records.getAllDerivedDefinitions("DeclContext"); - RecordVector Decls = Records.getAllDerivedDefinitions("Decl"); - RecordSet DeclContexts (DeclContextsVector.begin(), DeclContextsVector.end()); - - for (RecordVector::iterator i = Decls.begin(), e = Decls.end(); i != e; ++i) { - Record *R = *i; - - if (R->getValue("Base")) { - Record *B = R->getValueAsDef("Base"); - if (DeclContexts.find(B) != DeclContexts.end()) { - OS << "DECL_CONTEXT_BASE(" << B->getName() << ")\n"; - DeclContexts.erase(B); - } - } - } - - // To keep identical order, RecordVector may be used - // instead of RecordSet. - for (RecordVector::iterator - i = DeclContextsVector.begin(), e = DeclContextsVector.end(); - i != e; ++i) - if (DeclContexts.find(*i) != DeclContexts.end()) - OS << "DECL_CONTEXT(" << (*i)->getName() << ")\n"; - - OS << "#undef DECL_CONTEXT\n"; - OS << "#undef DECL_CONTEXT_BASE\n"; -} diff --git a/utils/TableGen/ClangASTNodesEmitter.h b/utils/TableGen/ClangASTNodesEmitter.h deleted file mode 100644 index 712333b..0000000 --- a/utils/TableGen/ClangASTNodesEmitter.h +++ /dev/null @@ -1,84 +0,0 @@ -//===- ClangASTNodesEmitter.h - Generate Clang AST node tables -*- C++ -*--===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// These tablegen backends emit Clang AST node tables -// -//===----------------------------------------------------------------------===// - -#ifndef CLANGAST_EMITTER_H -#define CLANGAST_EMITTER_H - -#include "TableGenBackend.h" -#include "Record.h" -#include <string> -#include <cctype> -#include <map> - -namespace llvm { - -/// ClangASTNodesEmitter - The top-level class emits .inc files containing -/// declarations of Clang statements. -/// -class ClangASTNodesEmitter : public TableGenBackend { - // A map from a node to each of its derived nodes. - typedef std::multimap<Record*, Record*> ChildMap; - typedef ChildMap::const_iterator ChildIterator; - - RecordKeeper &Records; - Record Root; - const std::string &BaseSuffix; - - // Create a macro-ized version of a name - static std::string macroName(std::string S) { - for (unsigned i = 0; i < S.size(); ++i) - S[i] = std::toupper(S[i]); - - return S; - } - - // Return the name to be printed in the base field. Normally this is - // the record's name plus the base suffix, but if it is the root node and - // the suffix is non-empty, it's just the suffix. - std::string baseName(Record &R) { - if (&R == &Root && !BaseSuffix.empty()) - return BaseSuffix; - - return R.getName() + BaseSuffix; - } - - std::pair<Record *, Record *> EmitNode (const ChildMap &Tree, raw_ostream& OS, - Record *Base); -public: - explicit ClangASTNodesEmitter(RecordKeeper &R, const std::string &N, - const std::string &S) - : Records(R), Root(N, SMLoc(), R), BaseSuffix(S) - {} - - // run - Output the .inc file contents - void run(raw_ostream &OS); -}; - -/// ClangDeclContextEmitter - Emits an addendum to a .inc file to enumerate the -/// clang declaration contexts. -/// -class ClangDeclContextEmitter : public TableGenBackend { - RecordKeeper &Records; - -public: - explicit ClangDeclContextEmitter(RecordKeeper &R) - : Records(R) - {} - - // run - Output the .inc file contents - void run(raw_ostream &OS); -}; - -} // End llvm namespace - -#endif diff --git a/utils/TableGen/ClangAttrEmitter.cpp b/utils/TableGen/ClangAttrEmitter.cpp deleted file mode 100644 index 26bd878..0000000 --- a/utils/TableGen/ClangAttrEmitter.cpp +++ /dev/null @@ -1,756 +0,0 @@ -//===- ClangAttrEmitter.cpp - Generate Clang attribute handling =-*- C++ -*--=// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// These tablegen backends emit Clang attribute processing code -// -//===----------------------------------------------------------------------===// - -#include "ClangAttrEmitter.h" -#include "Record.h" -#include "llvm/ADT/StringSwitch.h" -#include <algorithm> -#include <cctype> - -using namespace llvm; - -static const std::vector<StringRef> -getValueAsListOfStrings(Record &R, StringRef FieldName) { - ListInit *List = R.getValueAsListInit(FieldName); - assert (List && "Got a null ListInit"); - - std::vector<StringRef> Strings; - Strings.reserve(List->getSize()); - - for (ListInit::iterator i = List->begin(), e = List->end(); i != e; ++i) { - assert(*i && "Got a null element in a ListInit"); - if (StringInit *S = dynamic_cast<StringInit *>(*i)) - Strings.push_back(S->getValue()); - else if (CodeInit *C = dynamic_cast<CodeInit *>(*i)) - Strings.push_back(C->getValue()); - else - assert(false && "Got a non-string, non-code element in a ListInit"); - } - - return Strings; -} - -std::string ReadPCHRecord(StringRef type) { - return StringSwitch<std::string>(type) - .EndsWith("Decl *", "cast_or_null<" + std::string(type, 0, type.size()-1) + - ">(GetDecl(Record[Idx++]))") - .Case("QualType", "GetType(Record[Idx++])") - .Case("Expr *", "ReadSubExpr()") - .Case("IdentifierInfo *", "GetIdentifierInfo(Record, Idx)") - .Default("Record[Idx++]"); -} - -// Assumes that the way to get the value is SA->getname() -std::string WritePCHRecord(StringRef type, StringRef name) { - return StringSwitch<std::string>(type) - .EndsWith("Decl *", "AddDeclRef(" + std::string(name) + - ", Record);\n") - .Case("QualType", "AddTypeRef(" + std::string(name) + ", Record);\n") - .Case("Expr *", "AddStmt(" + std::string(name) + ");\n") - .Case("IdentifierInfo *", - "AddIdentifierRef(" + std::string(name) + ", Record);\n") - .Default("Record.push_back(" + std::string(name) + ");\n"); -} - -namespace { - class Argument { - std::string lowerName, upperName; - StringRef attrName; - - public: - Argument(Record &Arg, StringRef Attr) - : lowerName(Arg.getValueAsString("Name")), upperName(lowerName), - attrName(Attr) { - if (!lowerName.empty()) { - lowerName[0] = std::tolower(lowerName[0]); - upperName[0] = std::toupper(upperName[0]); - } - } - virtual ~Argument() {} - - StringRef getLowerName() const { return lowerName; } - StringRef getUpperName() const { return upperName; } - StringRef getAttrName() const { return attrName; } - - // These functions print the argument contents formatted in different ways. - virtual void writeAccessors(raw_ostream &OS) const = 0; - virtual void writeAccessorDefinitions(raw_ostream &OS) const {} - virtual void writeCloneArgs(raw_ostream &OS) const = 0; - virtual void writeCtorBody(raw_ostream &OS) const {} - virtual void writeCtorInitializers(raw_ostream &OS) const = 0; - virtual void writeCtorParameters(raw_ostream &OS) const = 0; - virtual void writeDeclarations(raw_ostream &OS) const = 0; - virtual void writePCHReadArgs(raw_ostream &OS) const = 0; - virtual void writePCHReadDecls(raw_ostream &OS) const = 0; - virtual void writePCHWrite(raw_ostream &OS) const = 0; - }; - - class SimpleArgument : public Argument { - std::string type; - - public: - SimpleArgument(Record &Arg, StringRef Attr, std::string T) - : Argument(Arg, Attr), type(T) - {} - - void writeAccessors(raw_ostream &OS) const { - OS << " " << type << " get" << getUpperName() << "() const {\n"; - OS << " return " << getLowerName() << ";\n"; - OS << " }"; - } - void writeCloneArgs(raw_ostream &OS) const { - OS << getLowerName(); - } - void writeCtorInitializers(raw_ostream &OS) const { - OS << getLowerName() << "(" << getUpperName() << ")"; - } - void writeCtorParameters(raw_ostream &OS) const { - OS << type << " " << getUpperName(); - } - void writeDeclarations(raw_ostream &OS) const { - OS << type << " " << getLowerName() << ";"; - } - void writePCHReadDecls(raw_ostream &OS) const { - std::string read = ReadPCHRecord(type); - OS << " " << type << " " << getLowerName() << " = " << read << ";\n"; - } - void writePCHReadArgs(raw_ostream &OS) const { - OS << getLowerName(); - } - void writePCHWrite(raw_ostream &OS) const { - OS << " " << WritePCHRecord(type, "SA->get" + - std::string(getUpperName()) + "()"); - } - }; - - class StringArgument : public Argument { - public: - StringArgument(Record &Arg, StringRef Attr) - : Argument(Arg, Attr) - {} - - void writeAccessors(raw_ostream &OS) const { - OS << " llvm::StringRef get" << getUpperName() << "() const {\n"; - OS << " return llvm::StringRef(" << getLowerName() << ", " - << getLowerName() << "Length);\n"; - OS << " }\n"; - OS << " unsigned get" << getUpperName() << "Length() const {\n"; - OS << " return " << getLowerName() << "Length;\n"; - OS << " }\n"; - OS << " void set" << getUpperName() - << "(ASTContext &C, llvm::StringRef S) {\n"; - OS << " " << getLowerName() << "Length = S.size();\n"; - OS << " this->" << getLowerName() << " = new (C, 1) char [" - << getLowerName() << "Length];\n"; - OS << " std::memcpy(this->" << getLowerName() << ", S.data(), " - << getLowerName() << "Length);\n"; - OS << " }"; - } - void writeCloneArgs(raw_ostream &OS) const { - OS << "get" << getUpperName() << "()"; - } - void writeCtorBody(raw_ostream &OS) const { - OS << " std::memcpy(" << getLowerName() << ", " << getUpperName() - << ".data(), " << getLowerName() << "Length);"; - } - void writeCtorInitializers(raw_ostream &OS) const { - OS << getLowerName() << "Length(" << getUpperName() << ".size())," - << getLowerName() << "(new (Ctx, 1) char[" << getLowerName() - << "Length])"; - } - void writeCtorParameters(raw_ostream &OS) const { - OS << "llvm::StringRef " << getUpperName(); - } - void writeDeclarations(raw_ostream &OS) const { - OS << "unsigned " << getLowerName() << "Length;\n"; - OS << "char *" << getLowerName() << ";"; - } - void writePCHReadDecls(raw_ostream &OS) const { - OS << " std::string " << getLowerName() - << "= ReadString(Record, Idx);\n"; - } - void writePCHReadArgs(raw_ostream &OS) const { - OS << getLowerName(); - } - void writePCHWrite(raw_ostream &OS) const { - OS << " AddString(SA->get" << getUpperName() << "(), Record);\n"; - } - }; - - class AlignedArgument : public Argument { - public: - AlignedArgument(Record &Arg, StringRef Attr) - : Argument(Arg, Attr) - {} - - void writeAccessors(raw_ostream &OS) const { - OS << " bool is" << getUpperName() << "Dependent() const;\n"; - - OS << " unsigned get" << getUpperName() << "(ASTContext &Ctx) const;\n"; - - OS << " bool is" << getUpperName() << "Expr() const {\n"; - OS << " return is" << getLowerName() << "Expr;\n"; - OS << " }\n"; - - OS << " Expr *get" << getUpperName() << "Expr() const {\n"; - OS << " assert(is" << getLowerName() << "Expr);\n"; - OS << " return " << getLowerName() << "Expr;\n"; - OS << " }\n"; - - OS << " TypeSourceInfo *get" << getUpperName() << "Type() const {\n"; - OS << " assert(!is" << getLowerName() << "Expr);\n"; - OS << " return " << getLowerName() << "Type;\n"; - OS << " }"; - } - void writeAccessorDefinitions(raw_ostream &OS) const { - OS << "bool " << getAttrName() << "Attr::is" << getUpperName() - << "Dependent() const {\n"; - OS << " if (is" << getLowerName() << "Expr)\n"; - OS << " return " << getLowerName() << "Expr && (" << getLowerName() - << "Expr->isValueDependent() || " << getLowerName() - << "Expr->isTypeDependent());\n"; - OS << " else\n"; - OS << " return " << getLowerName() - << "Type->getType()->isDependentType();\n"; - OS << "}\n"; - - // FIXME: Do not do the calculation here - // FIXME: Handle types correctly - // A null pointer means maximum alignment - // FIXME: Load the platform-specific maximum alignment, rather than - // 16, the x86 max. - OS << "unsigned " << getAttrName() << "Attr::get" << getUpperName() - << "(ASTContext &Ctx) const {\n"; - OS << " assert(!is" << getUpperName() << "Dependent());\n"; - OS << " if (is" << getLowerName() << "Expr)\n"; - OS << " return (" << getLowerName() << "Expr ? " << getLowerName() - << "Expr->EvaluateAsInt(Ctx).getZExtValue() : 16)" - << "* Ctx.getCharWidth();\n"; - OS << " else\n"; - OS << " return 0; // FIXME\n"; - OS << "}\n"; - } - void writeCloneArgs(raw_ostream &OS) const { - OS << "is" << getLowerName() << "Expr, is" << getLowerName() - << "Expr ? static_cast<void*>(" << getLowerName() - << "Expr) : " << getLowerName() - << "Type"; - } - void writeCtorBody(raw_ostream &OS) const { - OS << " if (is" << getLowerName() << "Expr)\n"; - OS << " " << getLowerName() << "Expr = reinterpret_cast<Expr *>(" - << getUpperName() << ");\n"; - OS << " else\n"; - OS << " " << getLowerName() - << "Type = reinterpret_cast<TypeSourceInfo *>(" << getUpperName() - << ");"; - } - void writeCtorInitializers(raw_ostream &OS) const { - OS << "is" << getLowerName() << "Expr(Is" << getUpperName() << "Expr)"; - } - void writeCtorParameters(raw_ostream &OS) const { - OS << "bool Is" << getUpperName() << "Expr, void *" << getUpperName(); - } - void writeDeclarations(raw_ostream &OS) const { - OS << "bool is" << getLowerName() << "Expr;\n"; - OS << "union {\n"; - OS << "Expr *" << getLowerName() << "Expr;\n"; - OS << "TypeSourceInfo *" << getLowerName() << "Type;\n"; - OS << "};"; - } - void writePCHReadArgs(raw_ostream &OS) const { - OS << "is" << getLowerName() << "Expr, " << getLowerName() << "Ptr"; - } - void writePCHReadDecls(raw_ostream &OS) const { - OS << " bool is" << getLowerName() << "Expr = Record[Idx++];\n"; - OS << " void *" << getLowerName() << "Ptr;\n"; - OS << " if (is" << getLowerName() << "Expr)\n"; - OS << " " << getLowerName() << "Ptr = ReadExpr(F);\n"; - OS << " else\n"; - OS << " " << getLowerName() - << "Ptr = GetTypeSourceInfo(F, Record, Idx);\n"; - } - void writePCHWrite(raw_ostream &OS) const { - OS << " Record.push_back(SA->is" << getUpperName() << "Expr());\n"; - OS << " if (SA->is" << getUpperName() << "Expr())\n"; - OS << " AddStmt(SA->get" << getUpperName() << "Expr());\n"; - OS << " else\n"; - OS << " AddTypeSourceInfo(SA->get" << getUpperName() - << "Type(), Record);\n"; - } - }; - - class VariadicArgument : public Argument { - std::string type; - - public: - VariadicArgument(Record &Arg, StringRef Attr, std::string T) - : Argument(Arg, Attr), type(T) - {} - - std::string getType() const { return type; } - - void writeAccessors(raw_ostream &OS) const { - OS << " typedef " << type << "* " << getLowerName() << "_iterator;\n"; - OS << " " << getLowerName() << "_iterator " << getLowerName() - << "_begin() const {\n"; - OS << " return " << getLowerName() << ";\n"; - OS << " }\n"; - OS << " " << getLowerName() << "_iterator " << getLowerName() - << "_end() const {\n"; - OS << " return " << getLowerName() << " + " << getLowerName() - << "Size;\n"; - OS << " }\n"; - OS << " unsigned " << getLowerName() << "_size() const {\n" - << " return " << getLowerName() << "Size;\n;"; - OS << " }"; - } - void writeCloneArgs(raw_ostream &OS) const { - OS << getLowerName() << ", " << getLowerName() << "Size"; - } - void writeCtorBody(raw_ostream &OS) const { - // FIXME: memcpy is not safe on non-trivial types. - OS << " std::memcpy(" << getLowerName() << ", " << getUpperName() - << ", " << getLowerName() << "Size * sizeof(" << getType() << "));\n"; - } - void writeCtorInitializers(raw_ostream &OS) const { - OS << getLowerName() << "Size(" << getUpperName() << "Size), " - << getLowerName() << "(new (Ctx, 16) " << getType() << "[" - << getLowerName() << "Size])"; - } - void writeCtorParameters(raw_ostream &OS) const { - OS << getType() << " *" << getUpperName() << ", unsigned " - << getUpperName() << "Size"; - } - void writeDeclarations(raw_ostream &OS) const { - OS << " unsigned " << getLowerName() << "Size;\n"; - OS << " " << getType() << " *" << getLowerName() << ";"; - } - void writePCHReadDecls(raw_ostream &OS) const { - OS << " unsigned " << getLowerName() << "Size = Record[Idx++];\n"; - OS << " llvm::SmallVector<" << type << ", 4> " << getLowerName() - << ";\n"; - OS << " " << getLowerName() << ".reserve(" << getLowerName() - << "Size);\n"; - OS << " for (unsigned i = " << getLowerName() << "Size; i; --i)\n"; - - std::string read = ReadPCHRecord(type); - OS << " " << getLowerName() << ".push_back(" << read << ");\n"; - } - void writePCHReadArgs(raw_ostream &OS) const { - OS << getLowerName() << ".data(), " << getLowerName() << "Size"; - } - void writePCHWrite(raw_ostream &OS) const{ - OS << " Record.push_back(SA->" << getLowerName() << "_size());\n"; - OS << " for (" << getAttrName() << "Attr::" << getLowerName() - << "_iterator i = SA->" << getLowerName() << "_begin(), e = SA->" - << getLowerName() << "_end(); i != e; ++i)\n"; - OS << " " << WritePCHRecord(type, "(*i)"); - } - }; - - class EnumArgument : public Argument { - std::string type; - std::vector<StringRef> values, enums; - public: - EnumArgument(Record &Arg, StringRef Attr) - : Argument(Arg, Attr), type(Arg.getValueAsString("Type")), - values(getValueAsListOfStrings(Arg, "Values")), - enums(getValueAsListOfStrings(Arg, "Enums")) - {} - - void writeAccessors(raw_ostream &OS) const { - OS << " " << type << " get" << getUpperName() << "() const {\n"; - OS << " return " << getLowerName() << ";\n"; - OS << " }"; - } - void writeCloneArgs(raw_ostream &OS) const { - OS << getLowerName(); - } - void writeCtorInitializers(raw_ostream &OS) const { - OS << getLowerName() << "(" << getUpperName() << ")"; - } - void writeCtorParameters(raw_ostream &OS) const { - OS << type << " " << getUpperName(); - } - void writeDeclarations(raw_ostream &OS) const { - // Calculate the various enum values - std::vector<StringRef> uniques(enums); - std::sort(uniques.begin(), uniques.end()); - uniques.erase(std::unique(uniques.begin(), uniques.end()), - uniques.end()); - // FIXME: Emit a proper error - assert(!uniques.empty()); - - std::vector<StringRef>::iterator i = uniques.begin(), - e = uniques.end(); - // The last one needs to not have a comma. - --e; - - OS << "public:\n"; - OS << " enum " << type << " {\n"; - for (; i != e; ++i) - OS << " " << *i << ",\n"; - OS << " " << *e << "\n"; - OS << " };\n"; - OS << "private:\n"; - OS << " " << type << " " << getLowerName() << ";"; - } - void writePCHReadDecls(raw_ostream &OS) const { - OS << " " << getAttrName() << "Attr::" << type << " " << getLowerName() - << "(static_cast<" << getAttrName() << "Attr::" << type - << ">(Record[Idx++]));\n"; - } - void writePCHReadArgs(raw_ostream &OS) const { - OS << getLowerName(); - } - void writePCHWrite(raw_ostream &OS) const { - OS << "Record.push_back(SA->get" << getUpperName() << "());\n"; - } - }; - - class VersionArgument : public Argument { - public: - VersionArgument(Record &Arg, StringRef Attr) - : Argument(Arg, Attr) - {} - - void writeAccessors(raw_ostream &OS) const { - OS << " VersionTuple get" << getUpperName() << "() const {\n"; - OS << " return " << getLowerName() << ";\n"; - OS << " }\n"; - OS << " void set" << getUpperName() - << "(ASTContext &C, VersionTuple V) {\n"; - OS << " " << getLowerName() << " = V;\n"; - OS << " }"; - } - void writeCloneArgs(raw_ostream &OS) const { - OS << "get" << getUpperName() << "()"; - } - void writeCtorBody(raw_ostream &OS) const { - } - void writeCtorInitializers(raw_ostream &OS) const { - OS << getLowerName() << "(" << getUpperName() << ")"; - } - void writeCtorParameters(raw_ostream &OS) const { - OS << "VersionTuple " << getUpperName(); - } - void writeDeclarations(raw_ostream &OS) const { - OS << "VersionTuple " << getLowerName() << ";\n"; - } - void writePCHReadDecls(raw_ostream &OS) const { - OS << " VersionTuple " << getLowerName() - << "= ReadVersionTuple(Record, Idx);\n"; - } - void writePCHReadArgs(raw_ostream &OS) const { - OS << getLowerName(); - } - void writePCHWrite(raw_ostream &OS) const { - OS << " AddVersionTuple(SA->get" << getUpperName() << "(), Record);\n"; - } - }; -} - -static Argument *createArgument(Record &Arg, StringRef Attr, - Record *Search = 0) { - if (!Search) - Search = &Arg; - - Argument *Ptr = 0; - llvm::StringRef ArgName = Search->getName(); - - if (ArgName == "AlignedArgument") Ptr = new AlignedArgument(Arg, Attr); - else if (ArgName == "EnumArgument") Ptr = new EnumArgument(Arg, Attr); - else if (ArgName == "ExprArgument") Ptr = new SimpleArgument(Arg, Attr, - "Expr *"); - else if (ArgName == "FunctionArgument") - Ptr = new SimpleArgument(Arg, Attr, "FunctionDecl *"); - else if (ArgName == "IdentifierArgument") - Ptr = new SimpleArgument(Arg, Attr, "IdentifierInfo *"); - else if (ArgName == "BoolArgument") Ptr = new SimpleArgument(Arg, Attr, - "bool"); - else if (ArgName == "IntArgument") Ptr = new SimpleArgument(Arg, Attr, "int"); - else if (ArgName == "StringArgument") Ptr = new StringArgument(Arg, Attr); - else if (ArgName == "TypeArgument") - Ptr = new SimpleArgument(Arg, Attr, "QualType"); - else if (ArgName == "UnsignedArgument") - Ptr = new SimpleArgument(Arg, Attr, "unsigned"); - else if (ArgName == "VariadicUnsignedArgument") - Ptr = new VariadicArgument(Arg, Attr, "unsigned"); - else if (ArgName == "VersionArgument") - Ptr = new VersionArgument(Arg, Attr); - - if (!Ptr) { - std::vector<Record*> Bases = Search->getSuperClasses(); - for (std::vector<Record*>::iterator i = Bases.begin(), e = Bases.end(); - i != e; ++i) { - Ptr = createArgument(Arg, Attr, *i); - if (Ptr) - break; - } - } - return Ptr; -} - -void ClangAttrClassEmitter::run(raw_ostream &OS) { - OS << "// This file is generated by TableGen. Do not edit.\n\n"; - OS << "#ifndef LLVM_CLANG_ATTR_CLASSES_INC\n"; - OS << "#define LLVM_CLANG_ATTR_CLASSES_INC\n\n"; - - std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr"); - - for (std::vector<Record*>::iterator i = Attrs.begin(), e = Attrs.end(); - i != e; ++i) { - Record &R = **i; - const std::string &SuperName = R.getSuperClasses().back()->getName(); - - OS << "class " << R.getName() << "Attr : public " << SuperName << " {\n"; - - std::vector<Record*> ArgRecords = R.getValueAsListOfDefs("Args"); - std::vector<Argument*> Args; - std::vector<Argument*>::iterator ai, ae; - Args.reserve(ArgRecords.size()); - - for (std::vector<Record*>::iterator ri = ArgRecords.begin(), - re = ArgRecords.end(); - ri != re; ++ri) { - Record &ArgRecord = **ri; - Argument *Arg = createArgument(ArgRecord, R.getName()); - assert(Arg); - Args.push_back(Arg); - - Arg->writeDeclarations(OS); - OS << "\n\n"; - } - - ae = Args.end(); - - OS << "\n public:\n"; - OS << " " << R.getName() << "Attr(SourceLocation L, ASTContext &Ctx\n"; - - for (ai = Args.begin(); ai != ae; ++ai) { - OS << " , "; - (*ai)->writeCtorParameters(OS); - OS << "\n"; - } - - OS << " )\n"; - OS << " : " << SuperName << "(attr::" << R.getName() << ", L)\n"; - - for (ai = Args.begin(); ai != ae; ++ai) { - OS << " , "; - (*ai)->writeCtorInitializers(OS); - OS << "\n"; - } - - OS << " {\n"; - - for (ai = Args.begin(); ai != ae; ++ai) { - (*ai)->writeCtorBody(OS); - OS << "\n"; - } - OS << " }\n\n"; - - OS << " virtual " << R.getName() << "Attr *clone (ASTContext &C) const;\n"; - - for (ai = Args.begin(); ai != ae; ++ai) { - (*ai)->writeAccessors(OS); - OS << "\n\n"; - } - - OS << R.getValueAsCode("AdditionalMembers"); - OS << "\n\n"; - - OS << " static bool classof(const Attr *A) { return A->getKind() == " - << "attr::" << R.getName() << "; }\n"; - OS << " static bool classof(const " << R.getName() - << "Attr *) { return true; }\n"; - OS << "};\n\n"; - } - - OS << "#endif\n"; -} - -void ClangAttrImplEmitter::run(raw_ostream &OS) { - OS << "// This file is generated by TableGen. Do not edit.\n\n"; - - std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr"); - std::vector<Record*>::iterator i = Attrs.begin(), e = Attrs.end(), ri, re; - std::vector<Argument*>::iterator ai, ae; - - for (; i != e; ++i) { - Record &R = **i; - std::vector<Record*> ArgRecords = R.getValueAsListOfDefs("Args"); - std::vector<Argument*> Args; - for (ri = ArgRecords.begin(), re = ArgRecords.end(); ri != re; ++ri) - Args.push_back(createArgument(**ri, R.getName())); - - for (ai = Args.begin(), ae = Args.end(); ai != ae; ++ai) - (*ai)->writeAccessorDefinitions(OS); - - OS << R.getName() << "Attr *" << R.getName() - << "Attr::clone(ASTContext &C) const {\n"; - OS << " return new (C) " << R.getName() << "Attr(getLocation(), C"; - for (ai = Args.begin(); ai != ae; ++ai) { - OS << ", "; - (*ai)->writeCloneArgs(OS); - } - OS << ");\n}\n\n"; - } -} - -static void EmitAttrList(raw_ostream &OS, StringRef Class, - const std::vector<Record*> &AttrList) { - std::vector<Record*>::const_iterator i = AttrList.begin(), e = AttrList.end(); - - if (i != e) { - // Move the end iterator back to emit the last attribute. - for(--e; i != e; ++i) - OS << Class << "(" << (*i)->getName() << ")\n"; - - OS << "LAST_" << Class << "(" << (*i)->getName() << ")\n\n"; - } -} - -void ClangAttrListEmitter::run(raw_ostream &OS) { - OS << "// This file is generated by TableGen. Do not edit.\n\n"; - - OS << "#ifndef LAST_ATTR\n"; - OS << "#define LAST_ATTR(NAME) ATTR(NAME)\n"; - OS << "#endif\n\n"; - - OS << "#ifndef INHERITABLE_ATTR\n"; - OS << "#define INHERITABLE_ATTR(NAME) ATTR(NAME)\n"; - OS << "#endif\n\n"; - - OS << "#ifndef LAST_INHERITABLE_ATTR\n"; - OS << "#define LAST_INHERITABLE_ATTR(NAME) INHERITABLE_ATTR(NAME)\n"; - OS << "#endif\n\n"; - - OS << "#ifndef INHERITABLE_PARAM_ATTR\n"; - OS << "#define INHERITABLE_PARAM_ATTR(NAME) ATTR(NAME)\n"; - OS << "#endif\n\n"; - - OS << "#ifndef LAST_INHERITABLE_PARAM_ATTR\n"; - OS << "#define LAST_INHERITABLE_PARAM_ATTR(NAME)" - " INHERITABLE_PARAM_ATTR(NAME)\n"; - OS << "#endif\n\n"; - - Record *InhClass = Records.getClass("InheritableAttr"); - Record *InhParamClass = Records.getClass("InheritableParamAttr"); - std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr"), - NonInhAttrs, InhAttrs, InhParamAttrs; - for (std::vector<Record*>::iterator i = Attrs.begin(), e = Attrs.end(); - i != e; ++i) { - if ((*i)->isSubClassOf(InhParamClass)) - InhParamAttrs.push_back(*i); - else if ((*i)->isSubClassOf(InhClass)) - InhAttrs.push_back(*i); - else - NonInhAttrs.push_back(*i); - } - - EmitAttrList(OS, "INHERITABLE_PARAM_ATTR", InhParamAttrs); - EmitAttrList(OS, "INHERITABLE_ATTR", InhAttrs); - EmitAttrList(OS, "ATTR", NonInhAttrs); - - OS << "#undef LAST_ATTR\n"; - OS << "#undef INHERITABLE_ATTR\n"; - OS << "#undef LAST_INHERITABLE_ATTR\n"; - OS << "#undef LAST_INHERITABLE_PARAM_ATTR\n"; - OS << "#undef ATTR\n"; -} - -void ClangAttrPCHReadEmitter::run(raw_ostream &OS) { - OS << "// This file is generated by TableGen. Do not edit.\n\n"; - - Record *InhClass = Records.getClass("InheritableAttr"); - std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr"), - ArgRecords; - std::vector<Record*>::iterator i = Attrs.begin(), e = Attrs.end(), ai, ae; - std::vector<Argument*> Args; - std::vector<Argument*>::iterator ri, re; - - OS << " switch (Kind) {\n"; - OS << " default:\n"; - OS << " assert(0 && \"Unknown attribute!\");\n"; - OS << " break;\n"; - for (; i != e; ++i) { - Record &R = **i; - OS << " case attr::" << R.getName() << ": {\n"; - if (R.isSubClassOf(InhClass)) - OS << " bool isInherited = Record[Idx++];\n"; - ArgRecords = R.getValueAsListOfDefs("Args"); - Args.clear(); - for (ai = ArgRecords.begin(), ae = ArgRecords.end(); ai != ae; ++ai) { - Argument *A = createArgument(**ai, R.getName()); - Args.push_back(A); - A->writePCHReadDecls(OS); - } - OS << " New = new (*Context) " << R.getName() << "Attr(Loc, *Context"; - for (ri = Args.begin(), re = Args.end(); ri != re; ++ri) { - OS << ", "; - (*ri)->writePCHReadArgs(OS); - } - OS << ");\n"; - if (R.isSubClassOf(InhClass)) - OS << " cast<InheritableAttr>(New)->setInherited(isInherited);\n"; - OS << " break;\n"; - OS << " }\n"; - } - OS << " }\n"; -} - -void ClangAttrPCHWriteEmitter::run(raw_ostream &OS) { - Record *InhClass = Records.getClass("InheritableAttr"); - std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr"), Args; - std::vector<Record*>::iterator i = Attrs.begin(), e = Attrs.end(), ai, ae; - - OS << " switch (A->getKind()) {\n"; - OS << " default:\n"; - OS << " llvm_unreachable(\"Unknown attribute kind!\");\n"; - OS << " break;\n"; - for (; i != e; ++i) { - Record &R = **i; - OS << " case attr::" << R.getName() << ": {\n"; - Args = R.getValueAsListOfDefs("Args"); - if (R.isSubClassOf(InhClass) || !Args.empty()) - OS << " const " << R.getName() << "Attr *SA = cast<" << R.getName() - << "Attr>(A);\n"; - if (R.isSubClassOf(InhClass)) - OS << " Record.push_back(SA->isInherited());\n"; - for (ai = Args.begin(), ae = Args.end(); ai != ae; ++ai) - createArgument(**ai, R.getName())->writePCHWrite(OS); - OS << " break;\n"; - OS << " }\n"; - } - OS << " }\n"; -} - -void ClangAttrSpellingListEmitter::run(raw_ostream &OS) { - OS << "// This file is generated by TableGen. Do not edit.\n\n"; - - std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr"); - - for (std::vector<Record*>::iterator I = Attrs.begin(), E = Attrs.end(); I != E; ++I) { - Record &Attr = **I; - - std::vector<StringRef> Spellings = getValueAsListOfStrings(Attr, "Spellings"); - - for (std::vector<StringRef>::const_iterator I = Spellings.begin(), E = Spellings.end(); I != E; ++I) { - StringRef Spelling = *I; - OS << ".Case(\"" << Spelling << "\", true)\n"; - } - } - -} diff --git a/utils/TableGen/ClangAttrEmitter.h b/utils/TableGen/ClangAttrEmitter.h deleted file mode 100644 index af87009..0000000 --- a/utils/TableGen/ClangAttrEmitter.h +++ /dev/null @@ -1,101 +0,0 @@ -//===- ClangAttrEmitter.h - Generate Clang attribute handling =-*- C++ -*--===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// These tablegen backends emit Clang attribute processing code -// -//===----------------------------------------------------------------------===// - -#ifndef CLANGATTR_EMITTER_H -#define CLANGATTR_EMITTER_H - -#include "TableGenBackend.h" - -namespace llvm { - -/// ClangAttrClassEmitter - class emits the class defintions for attributes for -/// clang. -class ClangAttrClassEmitter : public TableGenBackend { - RecordKeeper &Records; - - public: - explicit ClangAttrClassEmitter(RecordKeeper &R) - : Records(R) - {} - - void run(raw_ostream &OS); -}; - -/// ClangAttrImplEmitter - class emits the class method defintions for -/// attributes for clang. -class ClangAttrImplEmitter : public TableGenBackend { - RecordKeeper &Records; - - public: - explicit ClangAttrImplEmitter(RecordKeeper &R) - : Records(R) - {} - - void run(raw_ostream &OS); -}; - -/// ClangAttrListEmitter - class emits the enumeration list for attributes for -/// clang. -class ClangAttrListEmitter : public TableGenBackend { - RecordKeeper &Records; - - public: - explicit ClangAttrListEmitter(RecordKeeper &R) - : Records(R) - {} - - void run(raw_ostream &OS); -}; - -/// ClangAttrPCHReadEmitter - class emits the code to read an attribute from -/// a clang precompiled header. -class ClangAttrPCHReadEmitter : public TableGenBackend { - RecordKeeper &Records; - -public: - explicit ClangAttrPCHReadEmitter(RecordKeeper &R) - : Records(R) - {} - - void run(raw_ostream &OS); -}; - -/// ClangAttrPCHWriteEmitter - class emits the code to read an attribute from -/// a clang precompiled header. -class ClangAttrPCHWriteEmitter : public TableGenBackend { - RecordKeeper &Records; - -public: - explicit ClangAttrPCHWriteEmitter(RecordKeeper &R) - : Records(R) - {} - - void run(raw_ostream &OS); -}; - -/// ClangAttrSpellingListEmitter - class emits the list of spellings for attributes for -/// clang. -class ClangAttrSpellingListEmitter : public TableGenBackend { - RecordKeeper &Records; - - public: - explicit ClangAttrSpellingListEmitter(RecordKeeper &R) - : Records(R) - {} - - void run(raw_ostream &OS); -}; - -} - -#endif diff --git a/utils/TableGen/ClangDiagnosticsEmitter.cpp b/utils/TableGen/ClangDiagnosticsEmitter.cpp deleted file mode 100644 index 0a48e75..0000000 --- a/utils/TableGen/ClangDiagnosticsEmitter.cpp +++ /dev/null @@ -1,362 +0,0 @@ -//=- ClangDiagnosticsEmitter.cpp - Generate Clang diagnostics tables -*- C++ -*- -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// These tablegen backends emit Clang diagnostics tables. -// -//===----------------------------------------------------------------------===// - -#include "ClangDiagnosticsEmitter.h" -#include "Record.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/Compiler.h" -#include "llvm/ADT/DenseSet.h" -#include "llvm/ADT/StringExtras.h" -#include "llvm/ADT/StringMap.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/ADT/VectorExtras.h" -#include <map> -#include <algorithm> -#include <functional> -using namespace llvm; - -//===----------------------------------------------------------------------===// -// Diagnostic category computation code. -//===----------------------------------------------------------------------===// - -namespace { -class DiagGroupParentMap { - RecordKeeper &Records; - std::map<const Record*, std::vector<Record*> > Mapping; -public: - DiagGroupParentMap(RecordKeeper &records) : Records(records) { - std::vector<Record*> DiagGroups - = Records.getAllDerivedDefinitions("DiagGroup"); - for (unsigned i = 0, e = DiagGroups.size(); i != e; ++i) { - std::vector<Record*> SubGroups = - DiagGroups[i]->getValueAsListOfDefs("SubGroups"); - for (unsigned j = 0, e = SubGroups.size(); j != e; ++j) - Mapping[SubGroups[j]].push_back(DiagGroups[i]); - } - } - - const std::vector<Record*> &getParents(const Record *Group) { - return Mapping[Group]; - } -}; -} // end anonymous namespace. - - -static std::string -getCategoryFromDiagGroup(const Record *Group, - DiagGroupParentMap &DiagGroupParents) { - // If the DiagGroup has a category, return it. - std::string CatName = Group->getValueAsString("CategoryName"); - if (!CatName.empty()) return CatName; - - // The diag group may the subgroup of one or more other diagnostic groups, - // check these for a category as well. - const std::vector<Record*> &Parents = DiagGroupParents.getParents(Group); - for (unsigned i = 0, e = Parents.size(); i != e; ++i) { - CatName = getCategoryFromDiagGroup(Parents[i], DiagGroupParents); - if (!CatName.empty()) return CatName; - } - return ""; -} - -/// getDiagnosticCategory - Return the category that the specified diagnostic -/// lives in. -static std::string getDiagnosticCategory(const Record *R, - DiagGroupParentMap &DiagGroupParents) { - // If the diagnostic is in a group, and that group has a category, use it. - if (DefInit *Group = dynamic_cast<DefInit*>(R->getValueInit("Group"))) { - // Check the diagnostic's diag group for a category. - std::string CatName = getCategoryFromDiagGroup(Group->getDef(), - DiagGroupParents); - if (!CatName.empty()) return CatName; - } - - // If the diagnostic itself has a category, get it. - return R->getValueAsString("CategoryName"); -} - -namespace { - class DiagCategoryIDMap { - RecordKeeper &Records; - StringMap<unsigned> CategoryIDs; - std::vector<std::string> CategoryStrings; - public: - DiagCategoryIDMap(RecordKeeper &records) : Records(records) { - DiagGroupParentMap ParentInfo(Records); - - // The zero'th category is "". - CategoryStrings.push_back(""); - CategoryIDs[""] = 0; - - std::vector<Record*> Diags = - Records.getAllDerivedDefinitions("Diagnostic"); - for (unsigned i = 0, e = Diags.size(); i != e; ++i) { - std::string Category = getDiagnosticCategory(Diags[i], ParentInfo); - if (Category.empty()) continue; // Skip diags with no category. - - unsigned &ID = CategoryIDs[Category]; - if (ID != 0) continue; // Already seen. - - ID = CategoryStrings.size(); - CategoryStrings.push_back(Category); - } - } - - unsigned getID(StringRef CategoryString) { - return CategoryIDs[CategoryString]; - } - - typedef std::vector<std::string>::iterator iterator; - iterator begin() { return CategoryStrings.begin(); } - iterator end() { return CategoryStrings.end(); } - }; -} // end anonymous namespace. - - -//===----------------------------------------------------------------------===// -// Warning Tables (.inc file) generation. -//===----------------------------------------------------------------------===// - -void ClangDiagsDefsEmitter::run(raw_ostream &OS) { - // Write the #if guard - if (!Component.empty()) { - std::string ComponentName = UppercaseString(Component); - OS << "#ifdef " << ComponentName << "START\n"; - OS << "__" << ComponentName << "START = DIAG_START_" << ComponentName - << ",\n"; - OS << "#undef " << ComponentName << "START\n"; - OS << "#endif\n\n"; - } - - const std::vector<Record*> &Diags = - Records.getAllDerivedDefinitions("Diagnostic"); - - DiagCategoryIDMap CategoryIDs(Records); - DiagGroupParentMap DGParentMap(Records); - - for (unsigned i = 0, e = Diags.size(); i != e; ++i) { - const Record &R = *Diags[i]; - // Filter by component. - if (!Component.empty() && Component != R.getValueAsString("Component")) - continue; - - OS << "DIAG(" << R.getName() << ", "; - OS << R.getValueAsDef("Class")->getName(); - OS << ", diag::" << R.getValueAsDef("DefaultMapping")->getName(); - - // Description string. - OS << ", \""; - OS.write_escaped(R.getValueAsString("Text")) << '"'; - - // Warning associated with the diagnostic. - if (DefInit *DI = dynamic_cast<DefInit*>(R.getValueInit("Group"))) { - OS << ", \""; - OS.write_escaped(DI->getDef()->getValueAsString("GroupName")) << '"'; - } else { - OS << ", \"\""; - } - - // SFINAE bit - if (R.getValueAsBit("SFINAE")) - OS << ", true"; - else - OS << ", false"; - - // Access control bit - if (R.getValueAsBit("AccessControl")) - OS << ", true"; - else - OS << ", false"; - - // Category number. - OS << ", " << CategoryIDs.getID(getDiagnosticCategory(&R, DGParentMap)); - - // Brief - OS << ", \""; - OS.write_escaped(R.getValueAsString("Brief")) << '"'; - - // Explanation - OS << ", \""; - OS.write_escaped(R.getValueAsString("Explanation")) << '"'; - OS << ")\n"; - } -} - -//===----------------------------------------------------------------------===// -// Warning Group Tables generation -//===----------------------------------------------------------------------===// - -static std::string getDiagCategoryEnum(llvm::StringRef name) { - if (name.empty()) - return "DiagCat_None"; - llvm::SmallString<256> enumName = llvm::StringRef("DiagCat_"); - for (llvm::StringRef::iterator I = name.begin(), E = name.end(); I != E; ++I) - enumName += isalnum(*I) ? *I : '_'; - return enumName.str(); -} - -namespace { -struct GroupInfo { - std::vector<const Record*> DiagsInGroup; - std::vector<std::string> SubGroups; - unsigned IDNo; -}; -} // end anonymous namespace. - -void ClangDiagGroupsEmitter::run(raw_ostream &OS) { - // Compute a mapping from a DiagGroup to all of its parents. - DiagGroupParentMap DGParentMap(Records); - - // Invert the 1-[0/1] mapping of diags to group into a one to many mapping of - // groups to diags in the group. - std::map<std::string, GroupInfo> DiagsInGroup; - - std::vector<Record*> Diags = - Records.getAllDerivedDefinitions("Diagnostic"); - for (unsigned i = 0, e = Diags.size(); i != e; ++i) { - const Record *R = Diags[i]; - DefInit *DI = dynamic_cast<DefInit*>(R->getValueInit("Group")); - if (DI == 0) continue; - std::string GroupName = DI->getDef()->getValueAsString("GroupName"); - DiagsInGroup[GroupName].DiagsInGroup.push_back(R); - } - - // Add all DiagGroup's to the DiagsInGroup list to make sure we pick up empty - // groups (these are warnings that GCC supports that clang never produces). - std::vector<Record*> DiagGroups - = Records.getAllDerivedDefinitions("DiagGroup"); - for (unsigned i = 0, e = DiagGroups.size(); i != e; ++i) { - Record *Group = DiagGroups[i]; - GroupInfo &GI = DiagsInGroup[Group->getValueAsString("GroupName")]; - - std::vector<Record*> SubGroups = Group->getValueAsListOfDefs("SubGroups"); - for (unsigned j = 0, e = SubGroups.size(); j != e; ++j) - GI.SubGroups.push_back(SubGroups[j]->getValueAsString("GroupName")); - } - - // Assign unique ID numbers to the groups. - unsigned IDNo = 0; - for (std::map<std::string, GroupInfo>::iterator - I = DiagsInGroup.begin(), E = DiagsInGroup.end(); I != E; ++I, ++IDNo) - I->second.IDNo = IDNo; - - // Walk through the groups emitting an array for each diagnostic of the diags - // that are mapped to. - OS << "\n#ifdef GET_DIAG_ARRAYS\n"; - unsigned MaxLen = 0; - for (std::map<std::string, GroupInfo>::iterator - I = DiagsInGroup.begin(), E = DiagsInGroup.end(); I != E; ++I) { - MaxLen = std::max(MaxLen, (unsigned)I->first.size()); - - std::vector<const Record*> &V = I->second.DiagsInGroup; - if (!V.empty()) { - OS << "static const short DiagArray" << I->second.IDNo << "[] = { "; - for (unsigned i = 0, e = V.size(); i != e; ++i) - OS << "diag::" << V[i]->getName() << ", "; - OS << "-1 };\n"; - } - - const std::vector<std::string> &SubGroups = I->second.SubGroups; - if (!SubGroups.empty()) { - OS << "static const short DiagSubGroup" << I->second.IDNo << "[] = { "; - for (unsigned i = 0, e = SubGroups.size(); i != e; ++i) { - std::map<std::string, GroupInfo>::iterator RI = - DiagsInGroup.find(SubGroups[i]); - assert(RI != DiagsInGroup.end() && "Referenced without existing?"); - OS << RI->second.IDNo << ", "; - } - OS << "-1 };\n"; - } - } - OS << "#endif // GET_DIAG_ARRAYS\n\n"; - - // Emit the table now. - OS << "\n#ifdef GET_DIAG_TABLE\n"; - for (std::map<std::string, GroupInfo>::iterator - I = DiagsInGroup.begin(), E = DiagsInGroup.end(); I != E; ++I) { - // Group option string. - OS << " { "; - OS << I->first.size() << ", "; - OS << "\""; - OS.write_escaped(I->first) << "\"," - << std::string(MaxLen-I->first.size()+1, ' '); - - // Diagnostics in the group. - if (I->second.DiagsInGroup.empty()) - OS << "0, "; - else - OS << "DiagArray" << I->second.IDNo << ", "; - - // Subgroups. - if (I->second.SubGroups.empty()) - OS << 0; - else - OS << "DiagSubGroup" << I->second.IDNo; - OS << " },\n"; - } - OS << "#endif // GET_DIAG_TABLE\n\n"; - - // Emit the category table next. - DiagCategoryIDMap CategoriesByID(Records); - OS << "\n#ifdef GET_CATEGORY_TABLE\n"; - for (DiagCategoryIDMap::iterator I = CategoriesByID.begin(), - E = CategoriesByID.end(); I != E; ++I) - OS << "CATEGORY(\"" << *I << "\", " << getDiagCategoryEnum(*I) << ")\n"; - OS << "#endif // GET_CATEGORY_TABLE\n\n"; -} - -//===----------------------------------------------------------------------===// -// Diagnostic name index generation -//===----------------------------------------------------------------------===// - -namespace { -struct RecordIndexElement -{ - RecordIndexElement() {} - explicit RecordIndexElement(Record const &R): - Name(R.getName()) {} - - std::string Name; -}; - -struct RecordIndexElementSorter : - public std::binary_function<RecordIndexElement, RecordIndexElement, bool> { - - bool operator()(RecordIndexElement const &Lhs, - RecordIndexElement const &Rhs) const { - return Lhs.Name < Rhs.Name; - } - -}; - -} // end anonymous namespace. - -void ClangDiagsIndexNameEmitter::run(raw_ostream &OS) { - const std::vector<Record*> &Diags = - Records.getAllDerivedDefinitions("Diagnostic"); - - std::vector<RecordIndexElement> Index; - Index.reserve(Diags.size()); - for (unsigned i = 0, e = Diags.size(); i != e; ++i) { - const Record &R = *(Diags[i]); - Index.push_back(RecordIndexElement(R)); - } - - std::sort(Index.begin(), Index.end(), RecordIndexElementSorter()); - - for (unsigned i = 0, e = Index.size(); i != e; ++i) { - const RecordIndexElement &R = Index[i]; - - OS << "DIAG_NAME_INDEX(" << R.Name << ")\n"; - } -} diff --git a/utils/TableGen/ClangDiagnosticsEmitter.h b/utils/TableGen/ClangDiagnosticsEmitter.h deleted file mode 100644 index 1e4c8b7..0000000 --- a/utils/TableGen/ClangDiagnosticsEmitter.h +++ /dev/null @@ -1,54 +0,0 @@ -//===- ClangDiagnosticsEmitter.h - Generate Clang diagnostics tables -*- C++ -*- -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// These tablegen backends emit Clang diagnostics tables. -// -//===----------------------------------------------------------------------===// - -#ifndef CLANGDIAGS_EMITTER_H -#define CLANGDIAGS_EMITTER_H - -#include "TableGenBackend.h" - -namespace llvm { - -/// ClangDiagsDefsEmitter - The top-level class emits .def files containing -/// declarations of Clang diagnostics. -/// -class ClangDiagsDefsEmitter : public TableGenBackend { - RecordKeeper &Records; - const std::string& Component; -public: - explicit ClangDiagsDefsEmitter(RecordKeeper &R, const std::string& component) - : Records(R), Component(component) {} - - // run - Output the .def file contents - void run(raw_ostream &OS); -}; - -class ClangDiagGroupsEmitter : public TableGenBackend { - RecordKeeper &Records; -public: - explicit ClangDiagGroupsEmitter(RecordKeeper &R) : Records(R) {} - - void run(raw_ostream &OS); -}; - -class ClangDiagsIndexNameEmitter : public TableGenBackend { - RecordKeeper &Records; -public: - explicit ClangDiagsIndexNameEmitter(RecordKeeper &R) : Records(R) {} - - void run(raw_ostream &OS); -}; - - -} // End llvm namespace - -#endif diff --git a/utils/TableGen/ClangSACheckersEmitter.cpp b/utils/TableGen/ClangSACheckersEmitter.cpp deleted file mode 100644 index 97739c6..0000000 --- a/utils/TableGen/ClangSACheckersEmitter.cpp +++ /dev/null @@ -1,319 +0,0 @@ -//=- ClangSACheckersEmitter.cpp - Generate Clang SA checkers tables -*- C++ -*- -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This tablegen backend emits Clang Static Analyzer checkers tables. -// -//===----------------------------------------------------------------------===// - -#include "ClangSACheckersEmitter.h" -#include "Record.h" -#include "llvm/ADT/DenseSet.h" -#include <map> -#include <string> -using namespace llvm; - -//===----------------------------------------------------------------------===// -// Static Analyzer Checkers Tables generation -//===----------------------------------------------------------------------===// - -/// \brief True if it is specified hidden or a parent package is specified -/// as hidden, otherwise false. -static bool isHidden(const Record &R) { - if (R.getValueAsBit("Hidden")) - return true; - // Not declared as hidden, check the parent package if it is hidden. - if (DefInit *DI = dynamic_cast<DefInit*>(R.getValueInit("ParentPackage"))) - return isHidden(*DI->getDef()); - - return false; -} - -static bool isCheckerNamed(const Record *R) { - return !R->getValueAsString("CheckerName").empty(); -} - -static std::string getPackageFullName(const Record *R); - -static std::string getParentPackageFullName(const Record *R) { - std::string name; - if (DefInit *DI = dynamic_cast<DefInit*>(R->getValueInit("ParentPackage"))) - name = getPackageFullName(DI->getDef()); - return name; -} - -static std::string getPackageFullName(const Record *R) { - std::string name = getParentPackageFullName(R); - if (!name.empty()) name += "."; - return name + R->getValueAsString("PackageName"); -} - -static std::string getCheckerFullName(const Record *R) { - std::string name = getParentPackageFullName(R); - if (isCheckerNamed(R)) { - if (!name.empty()) name += "."; - name += R->getValueAsString("CheckerName"); - } - return name; -} - -static std::string getStringValue(const Record &R, StringRef field) { - if (StringInit * - SI = dynamic_cast<StringInit*>(R.getValueInit(field))) - return SI->getValue(); - return std::string(); -} - -namespace { -struct GroupInfo { - llvm::DenseSet<const Record*> Checkers; - llvm::DenseSet<const Record *> SubGroups; - bool Hidden; - unsigned Index; - - GroupInfo() : Hidden(false) { } -}; -} - -static void addPackageToCheckerGroup(const Record *package, const Record *group, - llvm::DenseMap<const Record *, GroupInfo *> &recordGroupMap) { - llvm::DenseSet<const Record *> &checkers = recordGroupMap[package]->Checkers; - for (llvm::DenseSet<const Record *>::iterator - I = checkers.begin(), E = checkers.end(); I != E; ++I) - recordGroupMap[group]->Checkers.insert(*I); - - llvm::DenseSet<const Record *> &subGroups = recordGroupMap[package]->SubGroups; - for (llvm::DenseSet<const Record *>::iterator - I = subGroups.begin(), E = subGroups.end(); I != E; ++I) - addPackageToCheckerGroup(*I, group, recordGroupMap); -} - -void ClangSACheckersEmitter::run(raw_ostream &OS) { - std::vector<Record*> checkers = Records.getAllDerivedDefinitions("Checker"); - llvm::DenseMap<const Record *, unsigned> checkerRecIndexMap; - for (unsigned i = 0, e = checkers.size(); i != e; ++i) - checkerRecIndexMap[checkers[i]] = i; - - // Invert the mapping of checkers to package/group into a one to many - // mapping of packages/groups to checkers. - std::map<std::string, GroupInfo> groupInfoByName; - llvm::DenseMap<const Record *, GroupInfo *> recordGroupMap; - - std::vector<Record*> packages = Records.getAllDerivedDefinitions("Package"); - for (unsigned i = 0, e = packages.size(); i != e; ++i) { - Record *R = packages[i]; - std::string fullName = getPackageFullName(R); - if (!fullName.empty()) { - GroupInfo &info = groupInfoByName[fullName]; - info.Hidden = isHidden(*R); - recordGroupMap[R] = &info; - } - } - - std::vector<Record*> - checkerGroups = Records.getAllDerivedDefinitions("CheckerGroup"); - for (unsigned i = 0, e = checkerGroups.size(); i != e; ++i) { - Record *R = checkerGroups[i]; - std::string name = R->getValueAsString("GroupName"); - if (!name.empty()) { - GroupInfo &info = groupInfoByName[name]; - recordGroupMap[R] = &info; - } - } - - for (unsigned i = 0, e = checkers.size(); i != e; ++i) { - Record *R = checkers[i]; - Record *package = 0; - if (DefInit * - DI = dynamic_cast<DefInit*>(R->getValueInit("ParentPackage"))) - package = DI->getDef(); - if (!isCheckerNamed(R) && !package) - throw "Checker '" + R->getName() + "' is neither named, nor in a package!"; - - if (isCheckerNamed(R)) { - // Create a pseudo-group to hold this checker. - std::string fullName = getCheckerFullName(R); - GroupInfo &info = groupInfoByName[fullName]; - info.Hidden = R->getValueAsBit("Hidden"); - recordGroupMap[R] = &info; - info.Checkers.insert(R); - } else { - recordGroupMap[package]->Checkers.insert(R); - } - - Record *currR = isCheckerNamed(R) ? R : package; - // Insert the checker and its parent packages into the subgroups set of - // the corresponding parent package. - while (DefInit *DI - = dynamic_cast<DefInit*>(currR->getValueInit("ParentPackage"))) { - Record *parentPackage = DI->getDef(); - recordGroupMap[parentPackage]->SubGroups.insert(currR); - currR = parentPackage; - } - // Insert the checker into the set of its group. - if (DefInit *DI = dynamic_cast<DefInit*>(R->getValueInit("Group"))) - recordGroupMap[DI->getDef()]->Checkers.insert(R); - } - - // If a package is in group, add all its checkers and its sub-packages - // checkers into the group. - for (unsigned i = 0, e = packages.size(); i != e; ++i) - if (DefInit *DI = dynamic_cast<DefInit*>(packages[i]->getValueInit("Group"))) - addPackageToCheckerGroup(packages[i], DI->getDef(), recordGroupMap); - - typedef std::map<std::string, const Record *> SortedRecords; - typedef llvm::DenseMap<const Record *, unsigned> RecToSortIndex; - - SortedRecords sortedGroups; - RecToSortIndex groupToSortIndex; - OS << "\n#ifdef GET_GROUPS\n"; - { - for (unsigned i = 0, e = checkerGroups.size(); i != e; ++i) - sortedGroups[checkerGroups[i]->getValueAsString("GroupName")] - = checkerGroups[i]; - - unsigned sortIndex = 0; - for (SortedRecords::iterator - I = sortedGroups.begin(), E = sortedGroups.end(); I != E; ++I) { - const Record *R = I->second; - - OS << "GROUP(" << "\""; - OS.write_escaped(R->getValueAsString("GroupName")) << "\""; - OS << ")\n"; - - groupToSortIndex[R] = sortIndex++; - } - } - OS << "#endif // GET_GROUPS\n\n"; - - OS << "\n#ifdef GET_PACKAGES\n"; - { - SortedRecords sortedPackages; - for (unsigned i = 0, e = packages.size(); i != e; ++i) - sortedPackages[getPackageFullName(packages[i])] = packages[i]; - - for (SortedRecords::iterator - I = sortedPackages.begin(), E = sortedPackages.end(); I != E; ++I) { - const Record &R = *I->second; - - OS << "PACKAGE(" << "\""; - OS.write_escaped(getPackageFullName(&R)) << "\", "; - // Group index - if (DefInit *DI = dynamic_cast<DefInit*>(R.getValueInit("Group"))) - OS << groupToSortIndex[DI->getDef()] << ", "; - else - OS << "-1, "; - // Hidden bit - if (isHidden(R)) - OS << "true"; - else - OS << "false"; - OS << ")\n"; - } - } - OS << "#endif // GET_PACKAGES\n\n"; - - OS << "\n#ifdef GET_CHECKERS\n"; - for (unsigned i = 0, e = checkers.size(); i != e; ++i) { - const Record &R = *checkers[i]; - - OS << "CHECKER(" << "\""; - std::string name; - if (isCheckerNamed(&R)) - name = getCheckerFullName(&R); - OS.write_escaped(name) << "\", "; - OS << R.getName() << ", "; - OS << getStringValue(R, "DescFile") << ", "; - OS << "\""; - OS.write_escaped(getStringValue(R, "HelpText")) << "\", "; - // Group index - if (DefInit *DI = dynamic_cast<DefInit*>(R.getValueInit("Group"))) - OS << groupToSortIndex[DI->getDef()] << ", "; - else - OS << "-1, "; - // Hidden bit - if (isHidden(R)) - OS << "true"; - else - OS << "false"; - OS << ")\n"; - } - OS << "#endif // GET_CHECKERS\n\n"; - - unsigned index = 0; - for (std::map<std::string, GroupInfo>::iterator - I = groupInfoByName.begin(), E = groupInfoByName.end(); I != E; ++I) - I->second.Index = index++; - - // Walk through the packages/groups/checkers emitting an array for each - // set of checkers and an array for each set of subpackages. - - OS << "\n#ifdef GET_MEMBER_ARRAYS\n"; - unsigned maxLen = 0; - for (std::map<std::string, GroupInfo>::iterator - I = groupInfoByName.begin(), E = groupInfoByName.end(); I != E; ++I) { - maxLen = std::max(maxLen, (unsigned)I->first.size()); - - llvm::DenseSet<const Record *> &checkers = I->second.Checkers; - if (!checkers.empty()) { - OS << "static const short CheckerArray" << I->second.Index << "[] = { "; - // Make the output order deterministic. - std::map<int, const Record *> sorted; - for (llvm::DenseSet<const Record *>::iterator - I = checkers.begin(), E = checkers.end(); I != E; ++I) - sorted[(*I)->getID()] = *I; - - for (std::map<int, const Record *>::iterator - I = sorted.begin(), E = sorted.end(); I != E; ++I) - OS << checkerRecIndexMap[I->second] << ", "; - OS << "-1 };\n"; - } - - llvm::DenseSet<const Record *> &subGroups = I->second.SubGroups; - if (!subGroups.empty()) { - OS << "static const short SubPackageArray" << I->second.Index << "[] = { "; - // Make the output order deterministic. - std::map<int, const Record *> sorted; - for (llvm::DenseSet<const Record *>::iterator - I = subGroups.begin(), E = subGroups.end(); I != E; ++I) - sorted[(*I)->getID()] = *I; - - for (std::map<int, const Record *>::iterator - I = sorted.begin(), E = sorted.end(); I != E; ++I) { - OS << recordGroupMap[I->second]->Index << ", "; - } - OS << "-1 };\n"; - } - } - OS << "#endif // GET_MEMBER_ARRAYS\n\n"; - - OS << "\n#ifdef GET_CHECKNAME_TABLE\n"; - for (std::map<std::string, GroupInfo>::iterator - I = groupInfoByName.begin(), E = groupInfoByName.end(); I != E; ++I) { - // Group option string. - OS << " { \""; - OS.write_escaped(I->first) << "\"," - << std::string(maxLen-I->first.size()+1, ' '); - - if (I->second.Checkers.empty()) - OS << "0, "; - else - OS << "CheckerArray" << I->second.Index << ", "; - - // Subgroups. - if (I->second.SubGroups.empty()) - OS << "0, "; - else - OS << "SubPackageArray" << I->second.Index << ", "; - - OS << (I->second.Hidden ? "true" : "false"); - - OS << " },\n"; - } - OS << "#endif // GET_CHECKNAME_TABLE\n\n"; -} diff --git a/utils/TableGen/ClangSACheckersEmitter.h b/utils/TableGen/ClangSACheckersEmitter.h deleted file mode 100644 index 6bd1635..0000000 --- a/utils/TableGen/ClangSACheckersEmitter.h +++ /dev/null @@ -1,31 +0,0 @@ -//===- ClangSACheckersEmitter.h - Generate Clang SA checkers tables -*- C++ -*- -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This tablegen backend emits Clang Static Analyzer checkers tables. -// -//===----------------------------------------------------------------------===// - -#ifndef CLANGSACHECKERS_EMITTER_H -#define CLANGSACHECKERS_EMITTER_H - -#include "TableGenBackend.h" - -namespace llvm { - -class ClangSACheckersEmitter : public TableGenBackend { - RecordKeeper &Records; -public: - explicit ClangSACheckersEmitter(RecordKeeper &R) : Records(R) {} - - void run(raw_ostream &OS); -}; - -} // End llvm namespace - -#endif diff --git a/utils/TableGen/CodeEmitterGen.cpp b/utils/TableGen/CodeEmitterGen.cpp index d828dfc..c5a1526 100644 --- a/utils/TableGen/CodeEmitterGen.cpp +++ b/utils/TableGen/CodeEmitterGen.cpp @@ -15,7 +15,7 @@ #include "CodeEmitterGen.h" #include "CodeGenTarget.h" -#include "Record.h" +#include "llvm/TableGen/Record.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" @@ -41,19 +41,23 @@ void CodeEmitterGen::reverseBits(std::vector<Record*> &Insts) { BitsInit *BI = R->getValueAsBitsInit("Inst"); unsigned numBits = BI->getNumBits(); - BitsInit *NewBI = new BitsInit(numBits); + + SmallVector<Init *, 16> NewBits(numBits); + for (unsigned bit = 0, end = numBits / 2; bit != end; ++bit) { unsigned bitSwapIdx = numBits - bit - 1; Init *OrigBit = BI->getBit(bit); Init *BitSwap = BI->getBit(bitSwapIdx); - NewBI->setBit(bit, BitSwap); - NewBI->setBit(bitSwapIdx, OrigBit); + NewBits[bit] = BitSwap; + NewBits[bitSwapIdx] = OrigBit; } if (numBits % 2) { unsigned middle = (numBits + 1) / 2; - NewBI->setBit(middle, BI->getBit(middle)); + NewBits[middle] = BI->getBit(middle); } + BitsInit *NewBI = BitsInit::get(NewBits); + // Update the bits in reversed order so that emitInstrOpBits will get the // correct endianness. R->getValue("Inst")->setValue(NewBI); diff --git a/utils/TableGen/CodeEmitterGen.h b/utils/TableGen/CodeEmitterGen.h index a874d97..7f6ee2a 100644 --- a/utils/TableGen/CodeEmitterGen.h +++ b/utils/TableGen/CodeEmitterGen.h @@ -14,7 +14,7 @@ #ifndef CODEMITTERGEN_H #define CODEMITTERGEN_H -#include "TableGenBackend.h" +#include "llvm/TableGen/TableGenBackend.h" #include <vector> #include <string> diff --git a/utils/TableGen/CodeGenDAGPatterns.cpp b/utils/TableGen/CodeGenDAGPatterns.cpp index 072893f..dbf1662 100644 --- a/utils/TableGen/CodeGenDAGPatterns.cpp +++ b/utils/TableGen/CodeGenDAGPatterns.cpp @@ -13,8 +13,8 @@ //===----------------------------------------------------------------------===// #include "CodeGenDAGPatterns.h" -#include "Error.h" -#include "Record.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Debug.h" @@ -1747,9 +1747,10 @@ TreePatternNode *TreePattern::ParseTreePattern(Init *TheInit, StringRef OpName){ // TreePatternNode of its own. For example: /// (foo GPR, imm) -> (foo GPR, (imm)) if (R->isSubClassOf("SDNode") || R->isSubClassOf("PatFrag")) - return ParseTreePattern(new DagInit(DI, "", - std::vector<std::pair<Init*, std::string> >()), - OpName); + return ParseTreePattern( + DagInit::get(DI, "", + std::vector<std::pair<Init*, std::string> >()), + OpName); // Input argument? TreePatternNode *Res = new TreePatternNode(DI, 1); @@ -1771,7 +1772,7 @@ TreePatternNode *TreePattern::ParseTreePattern(Init *TheInit, StringRef OpName){ if (BitsInit *BI = dynamic_cast<BitsInit*>(TheInit)) { // Turn this into an IntInit. - Init *II = BI->convertInitializerTo(new IntRecTy()); + Init *II = BI->convertInitializerTo(IntRecTy::get()); if (II == 0 || !dynamic_cast<IntInit*>(II)) error("Bits value must be constants!"); return ParseTreePattern(II, OpName); @@ -1860,7 +1861,7 @@ TreePatternNode *TreePattern::ParseTreePattern(Init *TheInit, StringRef OpName){ else // Otherwise, no chain. Operator = getDAGPatterns().get_intrinsic_wo_chain_sdnode(); - TreePatternNode *IIDNode = new TreePatternNode(new IntInit(IID), 1); + TreePatternNode *IIDNode = new TreePatternNode(IntInit::get(IID), 1); Children.insert(Children.begin(), IIDNode); } @@ -2180,7 +2181,7 @@ void CodeGenDAGPatterns::ParseDefaultOperands() { // Find some SDNode. assert(!SDNodes.empty() && "No SDNodes parsed?"); - Init *SomeSDNode = new DefInit(SDNodes.begin()->first); + Init *SomeSDNode = DefInit::get(SDNodes.begin()->first); for (unsigned iter = 0; iter != 2; ++iter) { for (unsigned i = 0, e = DefaultOps[iter].size(); i != e; ++i) { @@ -2192,7 +2193,7 @@ void CodeGenDAGPatterns::ParseDefaultOperands() { for (unsigned op = 0, e = DefaultInfo->getNumArgs(); op != e; ++op) Ops.push_back(std::make_pair(DefaultInfo->getArg(op), DefaultInfo->getArgName(op))); - DagInit *DI = new DagInit(SomeSDNode, "", Ops); + DagInit *DI = DagInit::get(SomeSDNode, "", Ops); // Create a TreePattern to parse this. TreePattern P(DefaultOps[iter][i], DI, false, *this); @@ -2828,6 +2829,12 @@ void CodeGenDAGPatterns::InferInstructionFlags() { InstInfo.isBitcast = IsBitcast; InstInfo.hasSideEffects = HasSideEffects; InstInfo.Operands.isVariadic = IsVariadic; + + // Sanity checks. + if (InstInfo.isReMaterializable && InstInfo.hasSideEffects) + throw TGError(InstInfo.TheDef->getLoc(), "The instruction " + + InstInfo.TheDef->getName() + + " is rematerializable AND has unmodeled side effects?"); } } diff --git a/utils/TableGen/CodeGenInstruction.cpp b/utils/TableGen/CodeGenInstruction.cpp index a52ce86..53d499f 100644 --- a/utils/TableGen/CodeGenInstruction.cpp +++ b/utils/TableGen/CodeGenInstruction.cpp @@ -13,8 +13,8 @@ #include "CodeGenInstruction.h" #include "CodeGenTarget.h" -#include "Error.h" -#include "Record.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/STLExtras.h" @@ -267,8 +267,9 @@ static void ParseConstraints(const std::string &CStr, CGIOperandList &Ops) { void CGIOperandList::ProcessDisableEncoding(std::string DisableEncoding) { while (1) { - std::string OpName; - tie(OpName, DisableEncoding) = getToken(DisableEncoding, " ,\t"); + std::pair<StringRef, StringRef> P = getToken(DisableEncoding, " ,\t"); + std::string OpName = P.first; + DisableEncoding = P.second; if (OpName.empty()) break; // Figure out which operand this is. @@ -308,6 +309,7 @@ CodeGenInstruction::CodeGenInstruction(Record *R) : TheDef(R), Operands(R) { isReMaterializable = R->getValueAsBit("isReMaterializable"); hasDelaySlot = R->getValueAsBit("hasDelaySlot"); usesCustomInserter = R->getValueAsBit("usesCustomInserter"); + hasPostISelHook = R->getValueAsBit("hasPostISelHook"); hasCtrlDep = R->getValueAsBit("hasCtrlDep"); isNotDuplicable = R->getValueAsBit("isNotDuplicable"); hasSideEffects = R->getValueAsBit("hasSideEffects"); @@ -423,6 +425,13 @@ bool CodeGenInstAlias::tryAliasOpMatch(DagInit *Result, unsigned AliasOpNo, // Handle explicit registers. if (ADI && ADI->getDef()->isSubClassOf("Register")) { + if (InstOpRec->isSubClassOf("OptionalDefOperand")) { + DagInit *DI = InstOpRec->getValueAsDag("MIOperandInfo"); + // The operand info should only have a single (register) entry. We + // want the register class of it. + InstOpRec = dynamic_cast<DefInit*>(DI->getArg(0))->getDef(); + } + if (InstOpRec->isSubClassOf("RegisterOperand")) InstOpRec = InstOpRec->getValueAsDef("RegClass"); @@ -431,8 +440,8 @@ bool CodeGenInstAlias::tryAliasOpMatch(DagInit *Result, unsigned AliasOpNo, if (!T.getRegisterClass(InstOpRec) .contains(T.getRegBank().getReg(ADI->getDef()))) - throw TGError(Loc, "fixed register " +ADI->getDef()->getName() - + " is not a member of the " + InstOpRec->getName() + + throw TGError(Loc, "fixed register " + ADI->getDef()->getName() + + " is not a member of the " + InstOpRec->getName() + " register class!"); if (!Result->getArgName(AliasOpNo).empty()) diff --git a/utils/TableGen/CodeGenInstruction.h b/utils/TableGen/CodeGenInstruction.h index 8d7669a..468277a 100644 --- a/utils/TableGen/CodeGenInstruction.h +++ b/utils/TableGen/CodeGenInstruction.h @@ -233,6 +233,7 @@ namespace llvm { bool isReMaterializable; bool hasDelaySlot; bool usesCustomInserter; + bool hasPostISelHook; bool hasCtrlDep; bool isNotDuplicable; bool hasSideEffects; diff --git a/utils/TableGen/CodeGenRegisters.cpp b/utils/TableGen/CodeGenRegisters.cpp index 1acf3a8..8de4615 100644 --- a/utils/TableGen/CodeGenRegisters.cpp +++ b/utils/TableGen/CodeGenRegisters.cpp @@ -14,8 +14,9 @@ #include "CodeGenRegisters.h" #include "CodeGenTarget.h" -#include "Error.h" +#include "llvm/TableGen/Error.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" using namespace llvm; @@ -182,8 +183,8 @@ struct TupleExpander : SetTheory::Expander { // Precompute some types. Record *RegisterCl = Def->getRecords().getClass("Register"); - RecTy *RegisterRecTy = new RecordRecTy(RegisterCl); - StringInit *BlankName = new StringInit(""); + RecTy *RegisterRecTy = RecordRecTy::get(RegisterCl); + StringInit *BlankName = StringInit::get(""); // Zip them up. for (unsigned n = 0; n != Length; ++n) { @@ -195,7 +196,7 @@ struct TupleExpander : SetTheory::Expander { Record *Reg = Lists[i][n]; if (i) Name += '_'; Name += Reg->getName(); - Tuple.push_back(new DefInit(Reg)); + Tuple.push_back(DefInit::get(Reg)); CostPerUse = std::max(CostPerUse, unsigned(Reg->getValueAsInt("CostPerUse"))); } @@ -216,7 +217,7 @@ struct TupleExpander : SetTheory::Expander { // Replace the sub-register list with Tuple. if (RV.getName() == "SubRegs") - RV.setValue(new ListInit(Tuple, RegisterRecTy)); + RV.setValue(ListInit::get(Tuple, RegisterRecTy)); // Provide a blank AsmName. MC hacks are required anyway. if (RV.getName() == "AsmName") @@ -224,7 +225,7 @@ struct TupleExpander : SetTheory::Expander { // CostPerUse is aggregated from all Tuple members. if (RV.getName() == "CostPerUse") - RV.setValue(new IntInit(CostPerUse)); + RV.setValue(IntInit::get(CostPerUse)); // Copy fields from the RegisterTuples def. if (RV.getName() == "SubRegIndices" || @@ -255,7 +256,7 @@ struct TupleExpander : SetTheory::Expander { //===----------------------------------------------------------------------===// CodeGenRegisterClass::CodeGenRegisterClass(CodeGenRegBank &RegBank, Record *R) - : TheDef(R) { + : TheDef(R), Name(R->getName()), EnumValue(-1) { // Rename anonymous register classes. if (R->getName().size() > 9 && R->getName()[9] == '.') { static unsigned AnonCounter = 0; @@ -272,18 +273,22 @@ CodeGenRegisterClass::CodeGenRegisterClass(CodeGenRegBank &RegBank, Record *R) } assert(!VTs.empty() && "RegisterClass must contain at least one ValueType!"); + // Allocation order 0 is the full set. AltOrders provides others. + const SetTheory::RecVec *Elements = RegBank.getSets().expand(R); + ListInit *AltOrders = R->getValueAsListInit("AltOrders"); + Orders.resize(1 + AltOrders->size()); + // Default allocation order always contains all registers. - Elements = RegBank.getSets().expand(R); - for (unsigned i = 0, e = Elements->size(); i != e; ++i) + for (unsigned i = 0, e = Elements->size(); i != e; ++i) { + Orders[0].push_back((*Elements)[i]); Members.insert(RegBank.getReg((*Elements)[i])); + } // Alternative allocation orders may be subsets. - ListInit *Alts = R->getValueAsListInit("AltOrders"); - AltOrders.resize(Alts->size()); SetTheory::RecSet Order; - for (unsigned i = 0, e = Alts->size(); i != e; ++i) { - RegBank.getSets().evaluate(Alts->getElement(i), Order); - AltOrders[i].append(Order.begin(), Order.end()); + for (unsigned i = 0, e = AltOrders->size(); i != e; ++i) { + RegBank.getSets().evaluate(AltOrders->getElement(i), Order); + Orders[1 + i].append(Order.begin(), Order.end()); // Verify that all altorder members are regclass members. while (!Order.empty()) { CodeGenRegister *Reg = RegBank.getReg(Order.back()); @@ -328,10 +333,71 @@ CodeGenRegisterClass::CodeGenRegisterClass(CodeGenRegBank &RegBank, Record *R) AltOrderSelect = R->getValueAsCode("AltOrderSelect"); } +// Create an inferred register class that was missing from the .td files. +// Most properties will be inherited from the closest super-class after the +// class structure has been computed. +CodeGenRegisterClass::CodeGenRegisterClass(StringRef Name, Key Props) + : Members(*Props.Members), + TheDef(0), + Name(Name), + EnumValue(-1), + SpillSize(Props.SpillSize), + SpillAlignment(Props.SpillAlignment), + CopyCost(0), + Allocatable(true) { +} + +// Compute inherited propertied for a synthesized register class. +void CodeGenRegisterClass::inheritProperties(CodeGenRegBank &RegBank) { + assert(!getDef() && "Only synthesized classes can inherit properties"); + assert(!SuperClasses.empty() && "Synthesized class without super class"); + + // The last super-class is the smallest one. + CodeGenRegisterClass &Super = *SuperClasses.back(); + + // Most properties are copied directly. + // Exceptions are members, size, and alignment + Namespace = Super.Namespace; + VTs = Super.VTs; + CopyCost = Super.CopyCost; + Allocatable = Super.Allocatable; + AltOrderSelect = Super.AltOrderSelect; + + // Copy all allocation orders, filter out foreign registers from the larger + // super-class. + Orders.resize(Super.Orders.size()); + for (unsigned i = 0, ie = Super.Orders.size(); i != ie; ++i) + for (unsigned j = 0, je = Super.Orders[i].size(); j != je; ++j) + if (contains(RegBank.getReg(Super.Orders[i][j]))) + Orders[i].push_back(Super.Orders[i][j]); +} + bool CodeGenRegisterClass::contains(const CodeGenRegister *Reg) const { return Members.count(Reg); } +namespace llvm { + raw_ostream &operator<<(raw_ostream &OS, const CodeGenRegisterClass::Key &K) { + OS << "{ S=" << K.SpillSize << ", A=" << K.SpillAlignment; + for (CodeGenRegister::Set::const_iterator I = K.Members->begin(), + E = K.Members->end(); I != E; ++I) + OS << ", " << (*I)->getName(); + return OS << " }"; + } +} + +// This is a simple lexicographical order that can be used to search for sets. +// It is not the same as the topological order provided by TopoOrderRC. +bool CodeGenRegisterClass::Key:: +operator<(const CodeGenRegisterClass::Key &B) const { + assert(Members && B.Members); + if (*Members != *B.Members) + return *Members < *B.Members; + if (SpillSize != B.SpillSize) + return SpillSize < B.SpillSize; + return SpillAlignment < B.SpillAlignment; +} + // Returns true if RC is a strict subclass. // RC is a sub-class of this class if it is a valid replacement for any // instruction operand where a register of this classis required. It must @@ -341,16 +407,101 @@ bool CodeGenRegisterClass::contains(const CodeGenRegister *Reg) const { // 2. The RC spill size must not be smaller than our spill size. // 3. RC spill alignment must be compatible with ours. // -bool CodeGenRegisterClass::hasSubClass(const CodeGenRegisterClass *RC) const { - return SpillAlignment && RC->SpillAlignment % SpillAlignment == 0 && - SpillSize <= RC->SpillSize && - std::includes(Members.begin(), Members.end(), - RC->Members.begin(), RC->Members.end(), +static bool testSubClass(const CodeGenRegisterClass *A, + const CodeGenRegisterClass *B) { + return A->SpillAlignment && B->SpillAlignment % A->SpillAlignment == 0 && + A->SpillSize <= B->SpillSize && + std::includes(A->getMembers().begin(), A->getMembers().end(), + B->getMembers().begin(), B->getMembers().end(), CodeGenRegister::Less()); } -const std::string &CodeGenRegisterClass::getName() const { - return TheDef->getName(); +/// Sorting predicate for register classes. This provides a topological +/// ordering that arranges all register classes before their sub-classes. +/// +/// Register classes with the same registers, spill size, and alignment form a +/// clique. They will be ordered alphabetically. +/// +static int TopoOrderRC(const void *PA, const void *PB) { + const CodeGenRegisterClass *A = *(const CodeGenRegisterClass* const*)PA; + const CodeGenRegisterClass *B = *(const CodeGenRegisterClass* const*)PB; + if (A == B) + return 0; + + // Order by descending set size. Note that the classes' allocation order may + // not have been computed yet. The Members set is always vaild. + if (A->getMembers().size() > B->getMembers().size()) + return -1; + if (A->getMembers().size() < B->getMembers().size()) + return 1; + + // Order by ascending spill size. + if (A->SpillSize < B->SpillSize) + return -1; + if (A->SpillSize > B->SpillSize) + return 1; + + // Order by ascending spill alignment. + if (A->SpillAlignment < B->SpillAlignment) + return -1; + if (A->SpillAlignment > B->SpillAlignment) + return 1; + + // Finally order by name as a tie breaker. + return A->getName() < B->getName(); +} + +std::string CodeGenRegisterClass::getQualifiedName() const { + if (Namespace.empty()) + return getName(); + else + return Namespace + "::" + getName(); +} + +// Compute sub-classes of all register classes. +// Assume the classes are ordered topologically. +void CodeGenRegisterClass::computeSubClasses(CodeGenRegBank &RegBank) { + ArrayRef<CodeGenRegisterClass*> RegClasses = RegBank.getRegClasses(); + + // Visit backwards so sub-classes are seen first. + for (unsigned rci = RegClasses.size(); rci; --rci) { + CodeGenRegisterClass &RC = *RegClasses[rci - 1]; + RC.SubClasses.resize(RegClasses.size()); + RC.SubClasses.set(RC.EnumValue); + + // Normally, all subclasses have IDs >= rci, unless RC is part of a clique. + for (unsigned s = rci; s != RegClasses.size(); ++s) { + if (RC.SubClasses.test(s)) + continue; + CodeGenRegisterClass *SubRC = RegClasses[s]; + if (!testSubClass(&RC, SubRC)) + continue; + // SubRC is a sub-class. Grap all its sub-classes so we won't have to + // check them again. + RC.SubClasses |= SubRC->SubClasses; + } + + // Sweep up missed clique members. They will be immediately preceeding RC. + for (unsigned s = rci - 1; s && testSubClass(&RC, RegClasses[s - 1]); --s) + RC.SubClasses.set(s - 1); + } + + // Compute the SuperClasses lists from the SubClasses vectors. + for (unsigned rci = 0; rci != RegClasses.size(); ++rci) { + const BitVector &SC = RegClasses[rci]->getSubClasses(); + for (int s = SC.find_first(); s >= 0; s = SC.find_next(s)) { + if (unsigned(s) == rci) + continue; + RegClasses[s]->SuperClasses.push_back(RegClasses[rci]); + } + } + + // With the class hierarchy in place, let synthesized register classes inherit + // properties from their closest super-class. The iteration order here can + // propagate properties down multiple levels. + for (unsigned rci = 0; rci != RegClasses.size(); ++rci) + if (!RegClasses[rci]->getDef()) + RegClasses[rci]->inheritProperties(RegBank); } //===----------------------------------------------------------------------===// @@ -385,14 +536,29 @@ CodeGenRegBank::CodeGenRegBank(RecordKeeper &Records) : Records(Records) { getReg((*TupRegs)[j]); } + // Precompute all sub-register maps now all the registers are known. + // This will create Composite entries for all inferred sub-register indices. + for (unsigned i = 0, e = Registers.size(); i != e; ++i) + Registers[i]->getSubRegs(*this); + // Read in register class definitions. std::vector<Record*> RCs = Records.getAllDerivedDefinitions("RegisterClass"); if (RCs.empty()) throw std::string("No 'RegisterClass' subclasses defined!"); + // Allocate user-defined register classes. RegClasses.reserve(RCs.size()); for (unsigned i = 0, e = RCs.size(); i != e; ++i) - RegClasses.push_back(CodeGenRegisterClass(*this, RCs[i])); + addToMaps(new CodeGenRegisterClass(*this, RCs[i])); + + // Infer missing classes to create a full algebra. + computeInferredRegisterClasses(); + + // Order register classes topologically and assign enum values. + array_pod_sort(RegClasses.begin(), RegClasses.end(), TopoOrderRC); + for (unsigned i = 0, e = RegClasses.size(); i != e; ++i) + RegClasses[i]->EnumValue = i; + CodeGenRegisterClass::computeSubClasses(*this); } CodeGenRegister *CodeGenRegBank::getReg(Record *Def) { @@ -404,11 +570,19 @@ CodeGenRegister *CodeGenRegBank::getReg(Record *Def) { return Reg; } -CodeGenRegisterClass *CodeGenRegBank::getRegClass(Record *Def) { - if (Def2RC.empty()) - for (unsigned i = 0, e = RegClasses.size(); i != e; ++i) - Def2RC[RegClasses[i].TheDef] = &RegClasses[i]; +void CodeGenRegBank::addToMaps(CodeGenRegisterClass *RC) { + RegClasses.push_back(RC); + if (Record *Def = RC->getDef()) + Def2RC.insert(std::make_pair(Def, RC)); + + // Duplicate classes are rejected by insert(). + // That's OK, we only care about the properties handled by CGRC::Key. + CodeGenRegisterClass::Key K(*RC); + Key2RC.insert(std::make_pair(K, RC)); +} + +CodeGenRegisterClass *CodeGenRegBank::getRegClass(Record *Def) { if (CodeGenRegisterClass *RC = Def2RC[Def]) return RC; @@ -425,7 +599,6 @@ Record *CodeGenRegBank::getCompositeSubRegIndex(Record *A, Record *B, // None exists, synthesize one. std::string Name = A->getName() + "_then_" + B->getName(); Comp = new Record(Name, SMLoc(), Records); - Records.addDef(Comp); SubRegIndices.push_back(Comp); return Comp; } @@ -438,11 +611,6 @@ unsigned CodeGenRegBank::getSubRegIndexNo(Record *idx) { } void CodeGenRegBank::computeComposites() { - // Precompute all sub-register maps. This will create Composite entries for - // all inferred sub-register indices. - for (unsigned i = 0, e = Registers.size(); i != e; ++i) - Registers[i]->getSubRegs(*this); - for (unsigned i = 0, e = Registers.size(); i != e; ++i) { CodeGenRegister *Reg1 = Registers[i]; const CodeGenRegister::SubRegMap &SRM1 = Reg1->getSubRegs(); @@ -571,6 +739,64 @@ void CodeGenRegBank::computeDerivedInfo() { computeComposites(); } +// Infer missing register classes. +// +// For every register class RC, make sure that the set of registers in RC with +// a given SubIxx sub-register form a register class. +void CodeGenRegBank::computeInferredRegisterClasses() { + // When this function is called, the register classes have not been sorted + // and assigned EnumValues yet. That means getSubClasses(), + // getSuperClasses(), and hasSubClass() functions are defunct. + + // Map SubRegIndex to register set. + typedef std::map<Record*, CodeGenRegister::Set, LessRecord> SubReg2SetMap; + + // Visit all register classes, including the ones being added by the loop. + for (unsigned rci = 0; rci != RegClasses.size(); ++rci) { + CodeGenRegisterClass &RC = *RegClasses[rci]; + + // Compute the set of registers supporting each SubRegIndex. + SubReg2SetMap SRSets; + for (CodeGenRegister::Set::const_iterator RI = RC.getMembers().begin(), + RE = RC.getMembers().end(); RI != RE; ++RI) { + const CodeGenRegister::SubRegMap &SRM = (*RI)->getSubRegs(); + for (CodeGenRegister::SubRegMap::const_iterator I = SRM.begin(), + E = SRM.end(); I != E; ++I) + SRSets[I->first].insert(*RI); + } + + // Find matching classes for all SRSets entries. Iterate in SubRegIndex + // numerical order to visit synthetic indices last. + for (unsigned sri = 0, sre = SubRegIndices.size(); sri != sre; ++sri) { + Record *SubIdx = SubRegIndices[sri]; + SubReg2SetMap::const_iterator I = SRSets.find(SubIdx); + // Unsupported SubRegIndex. Skip it. + if (I == SRSets.end()) + continue; + // In most cases, all RC registers support the SubRegIndex. + if (I->second.size() == RC.getMembers().size()) { + RC.setSubClassWithSubReg(SubIdx, &RC); + continue; + } + + // This is a real subset. See if we have a matching class. + CodeGenRegisterClass::Key K(&I->second, RC.SpillSize, RC.SpillAlignment); + RCKeyMap::const_iterator FoundI = Key2RC.find(K); + if (FoundI != Key2RC.end()) { + RC.setSubClassWithSubReg(SubIdx, FoundI->second); + continue; + } + + // Class doesn't exist. + CodeGenRegisterClass *NewRC = + new CodeGenRegisterClass(RC.getName() + "_with_" + + I->first->getName(), K); + addToMaps(NewRC); + RC.setSubClassWithSubReg(SubIdx, NewRC); + } + } +} + /// getRegisterClassForRegister - Find the register class that contains the /// specified physical register. If the register is not in a register class, /// return null. If the register is in multiple classes, and the classes have a @@ -579,10 +805,10 @@ void CodeGenRegBank::computeDerivedInfo() { const CodeGenRegisterClass* CodeGenRegBank::getRegClassForRegister(Record *R) { const CodeGenRegister *Reg = getReg(R); - const std::vector<CodeGenRegisterClass> &RCs = getRegClasses(); + ArrayRef<CodeGenRegisterClass*> RCs = getRegClasses(); const CodeGenRegisterClass *FoundRC = 0; for (unsigned i = 0, e = RCs.size(); i != e; ++i) { - const CodeGenRegisterClass &RC = RCs[i]; + const CodeGenRegisterClass &RC = *RCs[i]; if (!RC.contains(Reg)) continue; diff --git a/utils/TableGen/CodeGenRegisters.h b/utils/TableGen/CodeGenRegisters.h index 5edbf47..4fc34b0 100644 --- a/utils/TableGen/CodeGenRegisters.h +++ b/utils/TableGen/CodeGenRegisters.h @@ -15,10 +15,11 @@ #ifndef CODEGEN_REGISTERS_H #define CODEGEN_REGISTERS_H -#include "Record.h" #include "SetTheory.h" +#include "llvm/TableGen/Record.h" #include "llvm/CodeGen/ValueTypes.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitVector.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SetVector.h" #include <cstdlib> @@ -69,6 +70,7 @@ namespace llvm { struct Less { bool operator()(const CodeGenRegister *A, const CodeGenRegister *B) const { + assert(A && B); return A->EnumValue < B->EnumValue; } }; @@ -85,10 +87,25 @@ namespace llvm { class CodeGenRegisterClass { CodeGenRegister::Set Members; - const std::vector<Record*> *Elements; - std::vector<SmallVector<Record*, 16> > AltOrders; - public: + // Allocation orders. Order[0] always contains all registers in Members. + std::vector<SmallVector<Record*, 16> > Orders; + // Bit mask of sub-classes including this, indexed by their EnumValue. + BitVector SubClasses; + // List of super-classes, topologocally ordered to have the larger classes + // first. This is the same as sorting by EnumValue. + SmallVector<CodeGenRegisterClass*, 4> SuperClasses; Record *TheDef; + std::string Name; + + // For a synthesized class, inherit missing properties from the nearest + // super-class. + void inheritProperties(CodeGenRegBank&); + + // Map SubRegIndex -> sub-class + DenseMap<Record*, CodeGenRegisterClass*> SubClassWithSubReg; + + public: + unsigned EnumValue; std::string Namespace; std::vector<MVT::SimpleValueType> VTs; unsigned SpillSize; @@ -99,7 +116,12 @@ namespace llvm { DenseMap<Record*,Record*> SubRegClasses; std::string AltOrderSelect; - const std::string &getName() const; + // Return the Record that defined this class, or NULL if the class was + // created by TableGen. + Record *getDef() const { return TheDef; } + + const std::string &getName() const { return Name; } + std::string getQualifiedName() const; const std::vector<MVT::SimpleValueType> &getValueTypes() const {return VTs;} unsigned getNumValueTypes() const { return VTs.size(); } @@ -122,22 +144,77 @@ namespace llvm { // 2. The RC spill size must not be smaller than our spill size. // 3. RC spill alignment must be compatible with ours. // - bool hasSubClass(const CodeGenRegisterClass *RC) const; + bool hasSubClass(const CodeGenRegisterClass *RC) const { + return SubClasses.test(RC->EnumValue); + } + + // getSubClassWithSubReg - Returns the largest sub-class where all + // registers have a SubIdx sub-register. + CodeGenRegisterClass *getSubClassWithSubReg(Record *SubIdx) const { + return SubClassWithSubReg.lookup(SubIdx); + } + + void setSubClassWithSubReg(Record *SubIdx, CodeGenRegisterClass *SubRC) { + SubClassWithSubReg[SubIdx] = SubRC; + } + + // getSubClasses - Returns a constant BitVector of subclasses indexed by + // EnumValue. + // The SubClasses vector includs an entry for this class. + const BitVector &getSubClasses() const { return SubClasses; } + + // getSuperClasses - Returns a list of super classes ordered by EnumValue. + // The array does not include an entry for this class. + ArrayRef<CodeGenRegisterClass*> getSuperClasses() const { + return SuperClasses; + } // Returns an ordered list of class members. // The order of registers is the same as in the .td file. // No = 0 is the default allocation order, No = 1 is the first alternative. ArrayRef<Record*> getOrder(unsigned No = 0) const { - if (No == 0) - return *Elements; - else - return AltOrders[No - 1]; + return Orders[No]; } // Return the total number of allocation orders available. - unsigned getNumOrders() const { return 1 + AltOrders.size(); } + unsigned getNumOrders() const { return Orders.size(); } + + // Get the set of registers. This set contains the same registers as + // getOrder(0). + const CodeGenRegister::Set &getMembers() const { return Members; } CodeGenRegisterClass(CodeGenRegBank&, Record *R); + + // A key representing the parts of a register class used for forming + // sub-classes. Note the ordering provided by this key is not the same as + // the topological order used for the EnumValues. + struct Key { + const CodeGenRegister::Set *Members; + unsigned SpillSize; + unsigned SpillAlignment; + + Key(const Key &O) + : Members(O.Members), + SpillSize(O.SpillSize), + SpillAlignment(O.SpillAlignment) {} + + Key(const CodeGenRegister::Set *M, unsigned S = 0, unsigned A = 0) + : Members(M), SpillSize(S), SpillAlignment(A) {} + + Key(const CodeGenRegisterClass &RC) + : Members(&RC.getMembers()), + SpillSize(RC.SpillSize), + SpillAlignment(RC.SpillAlignment) {} + + // Lexicographical order of (Members, SpillSize, SpillAlignment). + bool operator<(const Key&) const; + }; + + // Create a non-user defined register class. + CodeGenRegisterClass(StringRef Name, Key Props); + + // Called by CodeGenRegBank::CodeGenRegBank(). + static void computeSubClasses(CodeGenRegBank&); }; // CodeGenRegBank - Represent a target's registers and the relations between @@ -151,8 +228,17 @@ namespace llvm { std::vector<CodeGenRegister*> Registers; DenseMap<Record*, CodeGenRegister*> Def2Reg; - std::vector<CodeGenRegisterClass> RegClasses; + // Register classes. + std::vector<CodeGenRegisterClass*> RegClasses; DenseMap<Record*, CodeGenRegisterClass*> Def2RC; + typedef std::map<CodeGenRegisterClass::Key, CodeGenRegisterClass*> RCKeyMap; + RCKeyMap Key2RC; + + // Add RC to *2RC maps. + void addToMaps(CodeGenRegisterClass*); + + // Infer missing register classes. + void computeInferredRegisterClasses(); // Composite SubRegIndex instances. // Map (SubRegIndex, SubRegIndex) -> SubRegIndex. @@ -184,7 +270,7 @@ namespace llvm { // Find a register from its Record def. CodeGenRegister *getReg(Record*); - const std::vector<CodeGenRegisterClass> &getRegClasses() { + ArrayRef<CodeGenRegisterClass*> getRegClasses() const { return RegClasses; } diff --git a/utils/TableGen/CodeGenTarget.cpp b/utils/TableGen/CodeGenTarget.cpp index 929791c..4a7bad7 100644 --- a/utils/TableGen/CodeGenTarget.cpp +++ b/utils/TableGen/CodeGenTarget.cpp @@ -16,7 +16,7 @@ #include "CodeGenTarget.h" #include "CodeGenIntrinsics.h" -#include "Record.h" +#include "llvm/TableGen/Record.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/CommandLine.h" @@ -184,9 +184,9 @@ std::vector<MVT::SimpleValueType> CodeGenTarget:: getRegisterVTs(Record *R) const { const CodeGenRegister *Reg = getRegBank().getReg(R); std::vector<MVT::SimpleValueType> Result; - const std::vector<CodeGenRegisterClass> &RCs = getRegisterClasses(); + ArrayRef<CodeGenRegisterClass*> RCs = getRegBank().getRegClasses(); for (unsigned i = 0, e = RCs.size(); i != e; ++i) { - const CodeGenRegisterClass &RC = RCs[i]; + const CodeGenRegisterClass &RC = *RCs[i]; if (RC.contains(Reg)) { const std::vector<MVT::SimpleValueType> &InVTs = RC.getValueTypes(); Result.insert(Result.end(), InVTs.begin(), InVTs.end()); @@ -201,10 +201,10 @@ getRegisterVTs(Record *R) const { void CodeGenTarget::ReadLegalValueTypes() const { - const std::vector<CodeGenRegisterClass> &RCs = getRegisterClasses(); + ArrayRef<CodeGenRegisterClass*> RCs = getRegBank().getRegClasses(); for (unsigned i = 0, e = RCs.size(); i != e; ++i) - for (unsigned ri = 0, re = RCs[i].VTs.size(); ri != re; ++ri) - LegalValueTypes.push_back(RCs[i].VTs[ri]); + for (unsigned ri = 0, re = RCs[i]->VTs.size(); ri != re; ++ri) + LegalValueTypes.push_back(RCs[i]->VTs[ri]); // Remove duplicates. std::sort(LegalValueTypes.begin(), LegalValueTypes.end()); diff --git a/utils/TableGen/CodeGenTarget.h b/utils/TableGen/CodeGenTarget.h index 143daed..730216c 100644 --- a/utils/TableGen/CodeGenTarget.h +++ b/utils/TableGen/CodeGenTarget.h @@ -19,7 +19,7 @@ #include "CodeGenRegisters.h" #include "CodeGenInstruction.h" -#include "Record.h" +#include "llvm/TableGen/Record.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> @@ -107,10 +107,6 @@ public: return RegAltNameIndices; } - const std::vector<CodeGenRegisterClass> &getRegisterClasses() const { - return getRegBank().getRegClasses(); - } - const CodeGenRegisterClass &getRegisterClass(Record *R) const { return *getRegBank().getRegClass(R); } diff --git a/utils/TableGen/DAGISelEmitter.cpp b/utils/TableGen/DAGISelEmitter.cpp index d66ae96..7db9003 100644 --- a/utils/TableGen/DAGISelEmitter.cpp +++ b/utils/TableGen/DAGISelEmitter.cpp @@ -13,7 +13,7 @@ #include "DAGISelEmitter.h" #include "DAGISelMatcher.h" -#include "Record.h" +#include "llvm/TableGen/Record.h" #include "llvm/Support/Debug.h" using namespace llvm; diff --git a/utils/TableGen/DAGISelEmitter.h b/utils/TableGen/DAGISelEmitter.h index 35ab550..9c9fe42 100644 --- a/utils/TableGen/DAGISelEmitter.h +++ b/utils/TableGen/DAGISelEmitter.h @@ -14,7 +14,7 @@ #ifndef DAGISEL_EMITTER_H #define DAGISEL_EMITTER_H -#include "TableGenBackend.h" +#include "llvm/TableGen/TableGenBackend.h" #include "CodeGenDAGPatterns.h" namespace llvm { diff --git a/utils/TableGen/DAGISelMatcher.cpp b/utils/TableGen/DAGISelMatcher.cpp index b12e101..1367e8d 100644 --- a/utils/TableGen/DAGISelMatcher.cpp +++ b/utils/TableGen/DAGISelMatcher.cpp @@ -10,7 +10,7 @@ #include "DAGISelMatcher.h" #include "CodeGenDAGPatterns.h" #include "CodeGenTarget.h" -#include "Record.h" +#include "llvm/TableGen/Record.h" #include "llvm/Support/raw_ostream.h" #include "llvm/ADT/StringExtras.h" using namespace llvm; diff --git a/utils/TableGen/DAGISelMatcherEmitter.cpp b/utils/TableGen/DAGISelMatcherEmitter.cpp index acb0135..3b65b2a 100644 --- a/utils/TableGen/DAGISelMatcherEmitter.cpp +++ b/utils/TableGen/DAGISelMatcherEmitter.cpp @@ -13,7 +13,7 @@ #include "DAGISelMatcher.h" #include "CodeGenDAGPatterns.h" -#include "Record.h" +#include "llvm/TableGen/Record.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringMap.h" diff --git a/utils/TableGen/DAGISelMatcherGen.cpp b/utils/TableGen/DAGISelMatcherGen.cpp index c5897c7..49ad956 100644 --- a/utils/TableGen/DAGISelMatcherGen.cpp +++ b/utils/TableGen/DAGISelMatcherGen.cpp @@ -10,7 +10,7 @@ #include "DAGISelMatcher.h" #include "CodeGenDAGPatterns.h" #include "CodeGenRegisters.h" -#include "Record.h" +#include "llvm/TableGen/Record.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" @@ -26,10 +26,10 @@ static MVT::SimpleValueType getRegisterValueType(Record *R, bool FoundRC = false; MVT::SimpleValueType VT = MVT::Other; const CodeGenRegister *Reg = T.getRegBank().getReg(R); - const std::vector<CodeGenRegisterClass> &RCs = T.getRegisterClasses(); + ArrayRef<CodeGenRegisterClass*> RCs = T.getRegBank().getRegClasses(); for (unsigned rc = 0, e = RCs.size(); rc != e; ++rc) { - const CodeGenRegisterClass &RC = RCs[rc]; + const CodeGenRegisterClass &RC = *RCs[rc]; if (!RC.contains(Reg)) continue; diff --git a/utils/TableGen/DisassemblerEmitter.cpp b/utils/TableGen/DisassemblerEmitter.cpp index 07313d1..ff314e9 100644 --- a/utils/TableGen/DisassemblerEmitter.cpp +++ b/utils/TableGen/DisassemblerEmitter.cpp @@ -9,12 +9,12 @@ #include "DisassemblerEmitter.h" #include "CodeGenTarget.h" -#include "Error.h" -#include "Record.h" #include "X86DisassemblerTables.h" #include "X86RecognizableInstr.h" #include "ARMDecoderEmitter.h" #include "FixedLenDecoderEmitter.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" using namespace llvm; using namespace llvm::X86Disassembler; @@ -128,12 +128,16 @@ void DisassemblerEmitter::run(raw_ostream &OS) { return; } - // Fixed-instruction-length targets use a common disassembler. - // ARM use its own implementation for now. - if (Target.getName() == "ARM") { - ARMDecoderEmitter(Records).run(OS); + // ARM and Thumb have a CHECK() macro to deal with DecodeStatuses. + if (Target.getName() == "ARM" || + Target.getName() == "Thumb") { + FixedLenDecoderEmitter(Records, + "ARM", + "if (!Check(S, ", ")) return MCDisassembler::Fail;", + "S", "MCDisassembler::Fail", + " MCDisassembler::DecodeStatus S = MCDisassembler::Success;\n(void)S;").run(OS); return; - } + } - FixedLenDecoderEmitter(Records).run(OS); + FixedLenDecoderEmitter(Records, Target.getName()).run(OS); } diff --git a/utils/TableGen/DisassemblerEmitter.h b/utils/TableGen/DisassemblerEmitter.h index 7229d81..63ee552 100644 --- a/utils/TableGen/DisassemblerEmitter.h +++ b/utils/TableGen/DisassemblerEmitter.h @@ -10,7 +10,7 @@ #ifndef DISASSEMBLEREMITTER_H #define DISASSEMBLEREMITTER_H -#include "TableGenBackend.h" +#include "llvm/TableGen/TableGenBackend.h" namespace llvm { diff --git a/utils/TableGen/EDEmitter.cpp b/utils/TableGen/EDEmitter.cpp index 2f9814a..abef70e 100644 --- a/utils/TableGen/EDEmitter.cpp +++ b/utils/TableGen/EDEmitter.cpp @@ -17,8 +17,8 @@ #include "AsmWriterInst.h" #include "CodeGenTarget.h" -#include "Record.h" +#include "llvm/TableGen/Record.h" #include "llvm/MC/EDInstInfo.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Format.h" @@ -256,12 +256,15 @@ static int X86TypeFromOpName(LiteralConstantEmitter *type, REG("GR8"); REG("GR8_NOREX"); REG("GR16"); + REG("GR16_NOAX"); REG("GR32"); + REG("GR32_NOAX"); REG("GR32_NOREX"); REG("GR32_TC"); REG("FR32"); REG("RFP32"); REG("GR64"); + REG("GR64_NOAX"); REG("GR64_TC"); REG("FR64"); REG("VR64"); @@ -279,6 +282,7 @@ static int X86TypeFromOpName(LiteralConstantEmitter *type, IMM("i16i8imm"); IMM("i32imm"); IMM("i32i8imm"); + IMM("u32u8imm"); IMM("i64imm"); IMM("i64i8imm"); IMM("i64i32imm"); @@ -554,6 +558,8 @@ static int ARMFlagFromOpName(LiteralConstantEmitter *type, const std::string &name) { REG("GPR"); REG("rGPR"); + REG("GPRnopc"); + REG("GPRsp"); REG("tcGPR"); REG("cc_out"); REG("s_cc_out"); @@ -575,6 +581,7 @@ static int ARMFlagFromOpName(LiteralConstantEmitter *type, IMM("nohash_imm"); IMM("p_imm"); IMM("c_imm"); + IMM("coproc_option_imm"); IMM("imod_op"); IMM("iflags_op"); IMM("cpinst_operand"); @@ -587,26 +594,40 @@ static int ARMFlagFromOpName(LiteralConstantEmitter *type, IMM("neg_zero"); IMM("imm0_31"); IMM("imm0_31_m1"); + IMM("imm1_16"); + IMM("imm1_32"); IMM("nModImm"); IMM("imm0_7"); IMM("imm0_15"); IMM("imm0_255"); IMM("imm0_4095"); IMM("imm0_65535"); + IMM("imm0_65535_expr"); + IMM("imm24b"); + IMM("pkh_lsl_amt"); + IMM("pkh_asr_amt"); IMM("jt2block_operand"); - IMM("t_imm_s4"); + IMM("t_imm0_1020s4"); + IMM("t_imm0_508s4"); IMM("pclabel"); IMM("adrlabel"); IMM("t_adrlabel"); IMM("t2adrlabel"); IMM("shift_imm"); - IMM("ssat_imm"); + IMM("t2_shift_imm"); IMM("neon_vcvt_imm32"); IMM("shr_imm8"); IMM("shr_imm16"); IMM("shr_imm32"); IMM("shr_imm64"); IMM("t2ldrlabel"); + IMM("postidx_imm8"); + IMM("postidx_imm8s4"); + IMM("imm_sr"); + IMM("imm1_31"); + IMM("VectorIndex8"); + IMM("VectorIndex16"); + IMM("VectorIndex32"); MISC("brtarget", "kOperandTypeARMBranchTarget"); // ? MISC("uncondbrtarget", "kOperandTypeARMBranchTarget"); // ? @@ -617,11 +638,14 @@ static int ARMFlagFromOpName(LiteralConstantEmitter *type, MISC("br_target", "kOperandTypeARMBranchTarget"); // ? MISC("bl_target", "kOperandTypeARMBranchTarget"); // ? + MISC("blx_target", "kOperandTypeARMBranchTarget"); // ? MISC("t_bltarget", "kOperandTypeARMBranchTarget"); // ? MISC("t_blxtarget", "kOperandTypeARMBranchTarget"); // ? - MISC("so_reg", "kOperandTypeARMSoReg"); // R, R, I - MISC("shift_so_reg", "kOperandTypeARMSoReg"); // R, R, I + MISC("so_reg_imm", "kOperandTypeARMSoRegReg"); // R, R, I + MISC("so_reg_reg", "kOperandTypeARMSoRegImm"); // R, R, I + MISC("shift_so_reg_reg", "kOperandTypeARMSoRegReg"); // R, R, I + MISC("shift_so_reg_imm", "kOperandTypeARMSoRegImm"); // R, R, I MISC("t2_so_reg", "kOperandTypeThumb2SoReg"); // R, I MISC("so_imm", "kOperandTypeARMSoImm"); // I MISC("rot_imm", "kOperandTypeARMRotImm"); // I @@ -631,8 +655,10 @@ static int ARMFlagFromOpName(LiteralConstantEmitter *type, MISC("it_pred", "kOperandTypeARMPredicate"); // I MISC("addrmode_imm12", "kOperandTypeAddrModeImm12"); // R, I MISC("ldst_so_reg", "kOperandTypeLdStSOReg"); // R, R, I + MISC("postidx_reg", "kOperandTypeARMAddrMode3Offset"); // R, I MISC("addrmode2", "kOperandTypeARMAddrMode2"); // R, R, I - MISC("am2offset", "kOperandTypeARMAddrMode2Offset"); // R, I + MISC("am2offset_reg", "kOperandTypeARMAddrMode2Offset"); // R, I + MISC("am2offset_imm", "kOperandTypeARMAddrMode2Offset"); // R, I MISC("addrmode3", "kOperandTypeARMAddrMode3"); // R, R, I MISC("am3offset", "kOperandTypeARMAddrMode3Offset"); // R, I MISC("ldstm_mode", "kOperandTypeARMLdStmMode"); // I @@ -642,17 +668,20 @@ static int ARMFlagFromOpName(LiteralConstantEmitter *type, MISC("addrmode6dup", "kOperandTypeARMAddrMode6"); // R, R, I, I MISC("addrmode6oneL32", "kOperandTypeARMAddrMode6"); // R, R, I, I MISC("addrmodepc", "kOperandTypeARMAddrModePC"); // R, I - MISC("addrmode7", "kOperandTypeARMAddrMode7"); // R + MISC("addr_offset_none", "kOperandTypeARMAddrMode7"); // R MISC("reglist", "kOperandTypeARMRegisterList"); // I, R, ... MISC("dpr_reglist", "kOperandTypeARMDPRRegisterList"); // I, R, ... MISC("spr_reglist", "kOperandTypeARMSPRRegisterList"); // I, R, ... MISC("it_mask", "kOperandTypeThumbITMask"); // I MISC("t2addrmode_reg", "kOperandTypeThumb2AddrModeReg"); // R + MISC("t2addrmode_posimm8", "kOperandTypeThumb2AddrModeImm8"); // R, I + MISC("t2addrmode_negimm8", "kOperandTypeThumb2AddrModeImm8"); // R, I MISC("t2addrmode_imm8", "kOperandTypeThumb2AddrModeImm8"); // R, I MISC("t2am_imm8_offset", "kOperandTypeThumb2AddrModeImm8Offset");//I MISC("t2addrmode_imm12", "kOperandTypeThumb2AddrModeImm12"); // R, I MISC("t2addrmode_so_reg", "kOperandTypeThumb2AddrModeSoReg"); // R, R, I MISC("t2addrmode_imm8s4", "kOperandTypeThumb2AddrModeImm8s4"); // R, I + MISC("t2addrmode_imm0_1020s4", "kOperandTypeThumb2AddrModeImm8s4"); // R, I MISC("t2am_imm8s4_offset", "kOperandTypeThumb2AddrModeImm8s4Offset"); // R, I MISC("tb_addrmode", "kOperandTypeARMTBAddrMode"); // I @@ -665,6 +694,8 @@ static int ARMFlagFromOpName(LiteralConstantEmitter *type, MISC("t_addrmode_rr", "kOperandTypeThumbAddrModeRR"); // R, R MISC("t_addrmode_sp", "kOperandTypeThumbAddrModeSP"); // R, I MISC("t_addrmode_pc", "kOperandTypeThumbAddrModePC"); // R, I + MISC("addrmode_tbb", "kOperandTypeThumbAddrModeRR"); // R, R + MISC("addrmode_tbh", "kOperandTypeThumbAddrModeRR"); // R, R return 1; } @@ -772,11 +803,6 @@ static void populateInstInfo(CompoundConstantEmitter &infoArray, for (index = 0; index < numInstructions; ++index) { const CodeGenInstruction& inst = *numberedInstructions[index]; - // We don't need to do anything for pseudo-instructions, as we'll never - // see them here. We'll only see real instructions. - if (inst.isPseudo) - continue; - CompoundConstantEmitter *infoStruct = new CompoundConstantEmitter; infoArray.addEntry(infoStruct); @@ -809,15 +835,20 @@ static void populateInstInfo(CompoundConstantEmitter &infoArray, unsigned numSyntaxes = 0; - if (target.getName() == "X86") { - X86PopulateOperands(operandTypes, inst); - X86ExtractSemantics(*instType, operandFlags, inst); - numSyntaxes = 2; - } - else if (target.getName() == "ARM") { - ARMPopulateOperands(operandTypes, inst); - ARMExtractSemantics(*instType, operandTypes, operandFlags, inst); - numSyntaxes = 1; + // We don't need to do anything for pseudo-instructions, as we'll never + // see them here. We'll only see real instructions. + // We still need to emit null initializers for everything. + if (!inst.isPseudo) { + if (target.getName() == "X86") { + X86PopulateOperands(operandTypes, inst); + X86ExtractSemantics(*instType, operandFlags, inst); + numSyntaxes = 2; + } + else if (target.getName() == "ARM") { + ARMPopulateOperands(operandTypes, inst); + ARMExtractSemantics(*instType, operandTypes, operandFlags, inst); + numSyntaxes = 1; + } } CompoundConstantEmitter *operandOrderArray = new CompoundConstantEmitter; @@ -850,7 +881,8 @@ static void emitCommonEnums(raw_ostream &o, unsigned int &i) { operandTypes.addEntry("kOperandTypeX86EffectiveAddress"); operandTypes.addEntry("kOperandTypeX86PCRelative"); operandTypes.addEntry("kOperandTypeARMBranchTarget"); - operandTypes.addEntry("kOperandTypeARMSoReg"); + operandTypes.addEntry("kOperandTypeARMSoRegReg"); + operandTypes.addEntry("kOperandTypeARMSoRegImm"); operandTypes.addEntry("kOperandTypeARMSoImm"); operandTypes.addEntry("kOperandTypeARMRotImm"); operandTypes.addEntry("kOperandTypeARMSoImm2Part"); diff --git a/utils/TableGen/EDEmitter.h b/utils/TableGen/EDEmitter.h index e30373f..f268375 100644 --- a/utils/TableGen/EDEmitter.h +++ b/utils/TableGen/EDEmitter.h @@ -16,7 +16,7 @@ #ifndef SEMANTIC_INFO_EMITTER_H #define SEMANTIC_INFO_EMITTER_H -#include "TableGenBackend.h" +#include "llvm/TableGen/TableGenBackend.h" namespace llvm { diff --git a/utils/TableGen/Error.cpp b/utils/TableGen/Error.cpp deleted file mode 100644 index 3f6cda8..0000000 --- a/utils/TableGen/Error.cpp +++ /dev/null @@ -1,39 +0,0 @@ -//===- Error.cpp - tblgen error handling helper routines --------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file contains error handling helper routines to pretty-print diagnostic -// messages from tblgen. -// -//===----------------------------------------------------------------------===// - -#include "Error.h" -#include "llvm/ADT/Twine.h" -#include "llvm/Support/raw_ostream.h" - -namespace llvm { - -SourceMgr SrcMgr; - -void PrintError(SMLoc ErrorLoc, const Twine &Msg) { - SrcMgr.PrintMessage(ErrorLoc, Msg, "error"); -} - -void PrintError(const char *Loc, const Twine &Msg) { - SrcMgr.PrintMessage(SMLoc::getFromPointer(Loc), Msg, "error"); -} - -void PrintError(const Twine &Msg) { - errs() << "error:" << Msg << "\n"; -} - -void PrintError(const TGError &Error) { - PrintError(Error.getLoc(), Error.getMessage()); -} - -} // end namespace llvm diff --git a/utils/TableGen/Error.h b/utils/TableGen/Error.h deleted file mode 100644 index b3a0146..0000000 --- a/utils/TableGen/Error.h +++ /dev/null @@ -1,43 +0,0 @@ -//===- Error.h - tblgen error handling helper routines ----------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file contains error handling helper routines to pretty-print diagnostic -// messages from tblgen. -// -//===----------------------------------------------------------------------===// - -#ifndef ERROR_H -#define ERROR_H - -#include "llvm/Support/SourceMgr.h" - -namespace llvm { - -class TGError { - SMLoc Loc; - std::string Message; -public: - TGError(SMLoc loc, const std::string &message) : Loc(loc), Message(message) {} - - SMLoc getLoc() const { return Loc; } - const std::string &getMessage() const { return Message; } -}; - -void PrintError(SMLoc ErrorLoc, const Twine &Msg); -void PrintError(const char *Loc, const Twine &Msg); -void PrintError(const Twine &Msg); -void PrintError(const TGError &Error); - - -extern SourceMgr SrcMgr; - - -} // end namespace "llvm" - -#endif diff --git a/utils/TableGen/FastISelEmitter.cpp b/utils/TableGen/FastISelEmitter.cpp index f54e8df..9fdc2e3 100644 --- a/utils/TableGen/FastISelEmitter.cpp +++ b/utils/TableGen/FastISelEmitter.cpp @@ -18,8 +18,8 @@ //===----------------------------------------------------------------------===// #include "FastISelEmitter.h" -#include "Error.h" -#include "Record.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/VectorExtras.h" #include "llvm/Support/Debug.h" @@ -504,7 +504,7 @@ void FastISelMap::collectPatterns(CodeGenDAGPatterns &CGP) { std::vector<std::string>* PhysRegInputs = new std::vector<std::string>(); if (InstPatNode->getOperator()->getName() == "imm" || - InstPatNode->getOperator()->getName() == "fpimmm") + InstPatNode->getOperator()->getName() == "fpimm") PhysRegInputs->push_back(""); else { // Compute the PhysRegs used by the given pattern, and check that diff --git a/utils/TableGen/FastISelEmitter.h b/utils/TableGen/FastISelEmitter.h index ce4e77e..4f75ac1 100644 --- a/utils/TableGen/FastISelEmitter.h +++ b/utils/TableGen/FastISelEmitter.h @@ -14,8 +14,8 @@ #ifndef FASTISEL_EMITTER_H #define FASTISEL_EMITTER_H -#include "TableGenBackend.h" #include "CodeGenDAGPatterns.h" +#include "llvm/TableGen/TableGenBackend.h" namespace llvm { diff --git a/utils/TableGen/FixedLenDecoderEmitter.cpp b/utils/TableGen/FixedLenDecoderEmitter.cpp index c9dcb01..02b966a 100644 --- a/utils/TableGen/FixedLenDecoderEmitter.cpp +++ b/utils/TableGen/FixedLenDecoderEmitter.cpp @@ -16,7 +16,7 @@ #include "FixedLenDecoderEmitter.h" #include "CodeGenTarget.h" -#include "Record.h" +#include "llvm/TableGen/Record.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" @@ -84,11 +84,8 @@ static BitsInit &getBitsField(const Record &def, const char *str) { // Forward declaration. class FilterChooser; -// FIXME: Possibly auto-detected? -#define BIT_WIDTH 32 - // Representation of the instruction to work on. -typedef bit_value_t insn_t[BIT_WIDTH]; +typedef std::vector<bit_value_t> insn_t; /// Filter - Filter works with FilterChooser to produce the decoding tree for /// the ISA. @@ -230,7 +227,7 @@ protected: // Array of bit values passed down from our parent. // Set to all BIT_UNFILTERED's for Parent == NULL. - bit_value_t FilterBitValues[BIT_WIDTH]; + std::vector<bit_value_t> FilterBitValues; // Links to the FilterChooser above us in the decoding tree. FilterChooser *Parent; @@ -238,21 +235,29 @@ protected: // Index of the best filter from Filters. int BestIndex; + // Width of instructions + unsigned BitWidth; + + // Parent emitter + const FixedLenDecoderEmitter *Emitter; + public: FilterChooser(const FilterChooser &FC) : AllInstructions(FC.AllInstructions), Opcodes(FC.Opcodes), - Operands(FC.Operands), Filters(FC.Filters), Parent(FC.Parent), - BestIndex(FC.BestIndex) { - memcpy(FilterBitValues, FC.FilterBitValues, sizeof(FilterBitValues)); - } + Operands(FC.Operands), Filters(FC.Filters), + FilterBitValues(FC.FilterBitValues), Parent(FC.Parent), + BestIndex(FC.BestIndex), BitWidth(FC.BitWidth), + Emitter(FC.Emitter) { } FilterChooser(const std::vector<const CodeGenInstruction*> &Insts, const std::vector<unsigned> &IDs, - std::map<unsigned, std::vector<OperandInfo> > &Ops) : + std::map<unsigned, std::vector<OperandInfo> > &Ops, + unsigned BW, + const FixedLenDecoderEmitter *E) : AllInstructions(Insts), Opcodes(IDs), Operands(Ops), Filters(), - Parent(NULL), BestIndex(-1) { - for (unsigned i = 0; i < BIT_WIDTH; ++i) - FilterBitValues[i] = BIT_UNFILTERED; + Parent(NULL), BestIndex(-1), BitWidth(BW), Emitter(E) { + for (unsigned i = 0; i < BitWidth; ++i) + FilterBitValues.push_back(BIT_UNFILTERED); doFilter(); } @@ -260,13 +265,12 @@ public: FilterChooser(const std::vector<const CodeGenInstruction*> &Insts, const std::vector<unsigned> &IDs, std::map<unsigned, std::vector<OperandInfo> > &Ops, - bit_value_t (&ParentFilterBitValues)[BIT_WIDTH], + std::vector<bit_value_t> &ParentFilterBitValues, FilterChooser &parent) : AllInstructions(Insts), Opcodes(IDs), Operands(Ops), - Filters(), Parent(&parent), BestIndex(-1) { - for (unsigned i = 0; i < BIT_WIDTH; ++i) - FilterBitValues[i] = ParentFilterBitValues[i]; - + Filters(), FilterBitValues(ParentFilterBitValues), + Parent(&parent), BestIndex(-1), BitWidth(parent.BitWidth), + Emitter(parent.Emitter) { doFilter(); } @@ -274,15 +278,15 @@ public: bool isTopLevel() { return Parent == NULL; } // Emit the top level typedef and decodeInstruction() function. - void emitTop(raw_ostream &o, unsigned Indentation); + void emitTop(raw_ostream &o, unsigned Indentation, std::string Namespace); protected: // Populates the insn given the uid. void insnWithID(insn_t &Insn, unsigned Opcode) const { BitsInit &Bits = getBitsField(*AllInstructions[Opcode]->TheDef, "Inst"); - for (unsigned i = 0; i < BIT_WIDTH; ++i) - Insn[i] = bitFromBits(Bits, i); + for (unsigned i = 0; i < BitWidth; ++i) + Insn.push_back(bitFromBits(Bits, i)); } // Returns the record name. @@ -300,7 +304,7 @@ protected: /// dumpFilterArray - dumpFilterArray prints out debugging info for the given /// filter array as a series of chars. - void dumpFilterArray(raw_ostream &o, bit_value_t (&filter)[BIT_WIDTH]); + void dumpFilterArray(raw_ostream &o, std::vector<bit_value_t> & filter); /// dumpStack - dumpStack traverses the filter chooser chain and calls /// dumpFilterArray on each filter chooser up to the top level one. @@ -326,6 +330,10 @@ protected: std::vector<unsigned> &EndBits, std::vector<uint64_t> &FieldVals, insn_t &Insn); + // Emits code to check the Predicates member of an instruction are true. + // Returns true if predicate matches were emitted, false otherwise. + bool emitPredicateMatch(raw_ostream &o, unsigned &Indentation,unsigned Opc); + // Emits code to decode the singleton. Return true if we have matched all the // well-known bits. bool emitSingletonDecoder(raw_ostream &o, unsigned &Indentation,unsigned Opc); @@ -333,6 +341,9 @@ protected: // Emits code to decode the singleton, and then to decode the rest. void emitSingletonDecoder(raw_ostream &o, unsigned &Indentation,Filter &Best); + void emitBinaryParser(raw_ostream &o , unsigned &Indentation, + OperandInfo &OpInfo); + // Assign a single filter and run with it. void runSingleFilter(FilterChooser &owner, unsigned startBit, unsigned numBit, bool mixed); @@ -375,7 +386,7 @@ Filter::Filter(const Filter &f) : Filter::Filter(FilterChooser &owner, unsigned startBit, unsigned numBits, bool mixed) : Owner(&owner), StartBit(startBit), NumBits(numBits), Mixed(mixed) { - assert(StartBit + NumBits - 1 < BIT_WIDTH); + assert(StartBit + NumBits - 1 < Owner->BitWidth); NumFiltered = 0; LastOpcFiltered = 0; @@ -427,9 +438,8 @@ Filter::~Filter() { void Filter::recurse() { std::map<uint64_t, std::vector<unsigned> >::const_iterator mapIterator; - bit_value_t BitValueArray[BIT_WIDTH]; // Starts by inheriting our parent filter chooser's filter bit values. - memcpy(BitValueArray, Owner->FilterBitValues, sizeof(BitValueArray)); + std::vector<bit_value_t> BitValueArray(Owner->FilterBitValues); unsigned bitIndex; @@ -493,8 +503,9 @@ void Filter::emit(raw_ostream &o, unsigned &Indentation) { o << StartBit << "} ...\n"; - o.indent(Indentation) << "switch (fieldFromInstruction(insn, " - << StartBit << ", " << NumBits << ")) {\n"; + o.indent(Indentation) << "switch (fieldFromInstruction" << Owner->BitWidth + << "(insn, " << StartBit << ", " + << NumBits << ")) {\n"; std::map<unsigned, FilterChooser*>::iterator filterIterator; @@ -559,68 +570,21 @@ unsigned Filter::usefulness() const { ////////////////////////////////// // Emit the top level typedef and decodeInstruction() function. -void FilterChooser::emitTop(raw_ostream &o, unsigned Indentation) { - switch (BIT_WIDTH) { - case 8: - o.indent(Indentation) << "typedef uint8_t field_t;\n"; - break; - case 16: - o.indent(Indentation) << "typedef uint16_t field_t;\n"; - break; - case 32: - o.indent(Indentation) << "typedef uint32_t field_t;\n"; - break; - case 64: - o.indent(Indentation) << "typedef uint64_t field_t;\n"; - break; - default: - assert(0 && "Unexpected instruction size!"); - } - - o << '\n'; - - o.indent(Indentation) << "static field_t " << - "fieldFromInstruction(field_t insn, unsigned startBit, unsigned numBits)\n"; - - o.indent(Indentation) << "{\n"; - - ++Indentation; ++Indentation; - o.indent(Indentation) << "assert(startBit + numBits <= " << BIT_WIDTH - << " && \"Instruction field out of bounds!\");\n"; - o << '\n'; - o.indent(Indentation) << "field_t fieldMask;\n"; - o << '\n'; - o.indent(Indentation) << "if (numBits == " << BIT_WIDTH << ")\n"; - - ++Indentation; ++Indentation; - o.indent(Indentation) << "fieldMask = (field_t)-1;\n"; - --Indentation; --Indentation; - - o.indent(Indentation) << "else\n"; - - ++Indentation; ++Indentation; - o.indent(Indentation) << "fieldMask = ((1 << numBits) - 1) << startBit;\n"; - --Indentation; --Indentation; - - o << '\n'; - o.indent(Indentation) << "return (insn & fieldMask) >> startBit;\n"; - --Indentation; --Indentation; - - o.indent(Indentation) << "}\n"; - - o << '\n'; - +void FilterChooser::emitTop(raw_ostream &o, unsigned Indentation, + std::string Namespace) { o.indent(Indentation) << - "static bool decodeInstruction(MCInst &MI, field_t insn, " - "uint64_t Address, const void *Decoder) {\n"; - o.indent(Indentation) << " unsigned tmp = 0;\n"; + "static MCDisassembler::DecodeStatus decode" << Namespace << "Instruction" << BitWidth + << "(MCInst &MI, uint" << BitWidth << "_t insn, uint64_t Address, " + << "const void *Decoder, const MCSubtargetInfo &STI) {\n"; + o.indent(Indentation) << " unsigned tmp = 0;\n (void)tmp;\n" << Emitter->Locals << "\n"; + o.indent(Indentation) << " uint64_t Bits = STI.getFeatureBits();\n"; ++Indentation; ++Indentation; // Emits code to decode the instructions. emit(o, Indentation); o << '\n'; - o.indent(Indentation) << "return false;\n"; + o.indent(Indentation) << "return " << Emitter->ReturnFail << ";\n"; --Indentation; --Indentation; o.indent(Indentation) << "}\n"; @@ -651,10 +615,10 @@ bool FilterChooser::fieldFromInsn(uint64_t &Field, insn_t &Insn, /// dumpFilterArray - dumpFilterArray prints out debugging info for the given /// filter array as a series of chars. void FilterChooser::dumpFilterArray(raw_ostream &o, - bit_value_t (&filter)[BIT_WIDTH]) { + std::vector<bit_value_t> &filter) { unsigned bitIndex; - for (bitIndex = BIT_WIDTH; bitIndex > 0; bitIndex--) { + for (bitIndex = BitWidth; bitIndex > 0; bitIndex--) { switch (filter[bitIndex - 1]) { case BIT_UNFILTERED: o << "."; @@ -727,7 +691,7 @@ unsigned FilterChooser::getIslands(std::vector<unsigned> &StartBits, int State = 0; int Val = -1; - for (unsigned i = 0; i < BIT_WIDTH; ++i) { + for (unsigned i = 0; i < BitWidth; ++i) { Val = Value(Insn[i]); bool Filtered = PositionFiltered(i); switch (State) { @@ -761,7 +725,7 @@ unsigned FilterChooser::getIslands(std::vector<unsigned> &StartBits, } // If we are still in Island after the loop, do some housekeeping. if (State == 2) { - EndBits.push_back(BIT_WIDTH - 1); + EndBits.push_back(BitWidth - 1); FieldVals.push_back(FieldVal); ++Num; } @@ -771,6 +735,70 @@ unsigned FilterChooser::getIslands(std::vector<unsigned> &StartBits, return Num; } +void FilterChooser::emitBinaryParser(raw_ostream &o, unsigned &Indentation, + OperandInfo &OpInfo) { + std::string &Decoder = OpInfo.Decoder; + + if (OpInfo.numFields() == 1) { + OperandInfo::iterator OI = OpInfo.begin(); + o.indent(Indentation) << " tmp = fieldFromInstruction" << BitWidth + << "(insn, " << OI->Base << ", " << OI->Width + << ");\n"; + } else { + o.indent(Indentation) << " tmp = 0;\n"; + for (OperandInfo::iterator OI = OpInfo.begin(), OE = OpInfo.end(); + OI != OE; ++OI) { + o.indent(Indentation) << " tmp |= (fieldFromInstruction" << BitWidth + << "(insn, " << OI->Base << ", " << OI->Width + << ") << " << OI->Offset << ");\n"; + } + } + + if (Decoder != "") + o.indent(Indentation) << " " << Emitter->GuardPrefix << Decoder + << "(MI, tmp, Address, Decoder)" << Emitter->GuardPostfix << "\n"; + else + o.indent(Indentation) << " MI.addOperand(MCOperand::CreateImm(tmp));\n"; + +} + +static void emitSinglePredicateMatch(raw_ostream &o, StringRef str, + std::string PredicateNamespace) { + if (str[0] == '!') + o << "!(Bits & " << PredicateNamespace << "::" + << str.slice(1,str.size()) << ")"; + else + o << "(Bits & " << PredicateNamespace << "::" << str << ")"; +} + +bool FilterChooser::emitPredicateMatch(raw_ostream &o, unsigned &Indentation, + unsigned Opc) { + ListInit *Predicates = AllInstructions[Opc]->TheDef->getValueAsListInit("Predicates"); + for (unsigned i = 0; i < Predicates->getSize(); ++i) { + Record *Pred = Predicates->getElementAsRecord(i); + if (!Pred->getValue("AssemblerMatcherPredicate")) + continue; + + std::string P = Pred->getValueAsString("AssemblerCondString"); + + if (!P.length()) + continue; + + if (i != 0) + o << " && "; + + StringRef SR(P); + std::pair<StringRef, StringRef> pairs = SR.split(','); + while (pairs.second.size()) { + emitSinglePredicateMatch(o, pairs.first, Emitter->PredicateNamespace); + o << " && "; + pairs = pairs.second.split(','); + } + emitSinglePredicateMatch(o, pairs.first, Emitter->PredicateNamespace); + } + return Predicates->getSize() > 0; +} + // Emits code to decode the singleton. Return true if we have matched all the // well-known bits. bool FilterChooser::emitSingletonDecoder(raw_ostream &o, unsigned &Indentation, @@ -789,33 +817,27 @@ bool FilterChooser::emitSingletonDecoder(raw_ostream &o, unsigned &Indentation, // If we have matched all the well-known bits, just issue a return. if (Size == 0) { - o.indent(Indentation) << "{\n"; + o.indent(Indentation) << "if ("; + if (!emitPredicateMatch(o, Indentation, Opc)) + o << "1"; + o << ") {\n"; o.indent(Indentation) << " MI.setOpcode(" << Opc << ");\n"; std::vector<OperandInfo>& InsnOperands = Operands[Opc]; for (std::vector<OperandInfo>::iterator I = InsnOperands.begin(), E = InsnOperands.end(); I != E; ++I) { // If a custom instruction decoder was specified, use that. - if (I->FieldBase == ~0U && I->FieldLength == ~0U) { - o.indent(Indentation) << " " << I->Decoder - << "(MI, insn, Address, Decoder);\n"; + if (I->numFields() == 0 && I->Decoder.size()) { + o.indent(Indentation) << " " << Emitter->GuardPrefix << I->Decoder + << "(MI, insn, Address, Decoder)" << Emitter->GuardPostfix << "\n"; break; } - o.indent(Indentation) - << " tmp = fieldFromInstruction(insn, " << I->FieldBase - << ", " << I->FieldLength << ");\n"; - if (I->Decoder != "") { - o.indent(Indentation) << " " << I->Decoder - << "(MI, tmp, Address, Decoder);\n"; - } else { - o.indent(Indentation) - << " MI.addOperand(MCOperand::CreateImm(tmp));\n"; - } + emitBinaryParser(o, Indentation, *I); } - o.indent(Indentation) << " return true; // " << nameWithID(Opc) + o.indent(Indentation) << " return " << Emitter->ReturnOK << "; // " << nameWithID(Opc) << '\n'; - o.indent(Indentation) << "}\n"; + o.indent(Indentation) << "}\n"; // Closing predicate block. return true; } @@ -827,16 +849,21 @@ bool FilterChooser::emitSingletonDecoder(raw_ostream &o, unsigned &Indentation, for (I = Size; I != 0; --I) { o << "Inst{" << EndBits[I-1] << '-' << StartBits[I-1] << "} "; if (I > 1) - o << "&& "; + o << " && "; else o << "for singleton decoding...\n"; } o.indent(Indentation) << "if ("; + if (emitPredicateMatch(o, Indentation, Opc)) { + o << " &&\n"; + o.indent(Indentation+4); + } for (I = Size; I != 0; --I) { NumBits = EndBits[I-1] - StartBits[I-1] + 1; - o << "fieldFromInstruction(insn, " << StartBits[I-1] << ", " << NumBits + o << "fieldFromInstruction" << BitWidth << "(insn, " + << StartBits[I-1] << ", " << NumBits << ") == " << FieldVals[I-1]; if (I > 1) o << " && "; @@ -848,24 +875,15 @@ bool FilterChooser::emitSingletonDecoder(raw_ostream &o, unsigned &Indentation, for (std::vector<OperandInfo>::iterator I = InsnOperands.begin(), E = InsnOperands.end(); I != E; ++I) { // If a custom instruction decoder was specified, use that. - if (I->FieldBase == ~0U && I->FieldLength == ~0U) { - o.indent(Indentation) << " " << I->Decoder - << "(MI, insn, Address, Decoder);\n"; + if (I->numFields() == 0 && I->Decoder.size()) { + o.indent(Indentation) << " " << Emitter->GuardPrefix << I->Decoder + << "(MI, insn, Address, Decoder)" << Emitter->GuardPostfix << "\n"; break; } - o.indent(Indentation) - << " tmp = fieldFromInstruction(insn, " << I->FieldBase - << ", " << I->FieldLength << ");\n"; - if (I->Decoder != "") { - o.indent(Indentation) << " " << I->Decoder - << "(MI, tmp, Address, Decoder);\n"; - } else { - o.indent(Indentation) - << " MI.addOperand(MCOperand::CreateImm(tmp));\n"; - } + emitBinaryParser(o, Indentation, *I); } - o.indent(Indentation) << " return true; // " << nameWithID(Opc) + o.indent(Indentation) << " return " << Emitter->ReturnOK << "; // " << nameWithID(Opc) << '\n'; o.indent(Indentation) << "}\n"; @@ -965,23 +983,23 @@ bool FilterChooser::filterProcessor(bool AllowMixed, bool Greedy) { // (MIXED) ------ . ----> (MIXED) // (FILTERED)---- . ----> (FILTERED) - bitAttr_t bitAttrs[BIT_WIDTH]; + std::vector<bitAttr_t> bitAttrs; // FILTERED bit positions provide no entropy and are not worthy of pursuing. // Filter::recurse() set either BIT_TRUE or BIT_FALSE for each position. - for (BitIndex = 0; BitIndex < BIT_WIDTH; ++BitIndex) + for (BitIndex = 0; BitIndex < BitWidth; ++BitIndex) if (FilterBitValues[BitIndex] == BIT_TRUE || FilterBitValues[BitIndex] == BIT_FALSE) - bitAttrs[BitIndex] = ATTR_FILTERED; + bitAttrs.push_back(ATTR_FILTERED); else - bitAttrs[BitIndex] = ATTR_NONE; + bitAttrs.push_back(ATTR_NONE); for (InsnIndex = 0; InsnIndex < numInstructions; ++InsnIndex) { insn_t insn; insnWithID(insn, Opcodes[InsnIndex]); - for (BitIndex = 0; BitIndex < BIT_WIDTH; ++BitIndex) { + for (BitIndex = 0; BitIndex < BitWidth; ++BitIndex) { switch (bitAttrs[BitIndex]) { case ATTR_NONE: if (insn[BitIndex] == BIT_UNSET) @@ -1027,7 +1045,7 @@ bool FilterChooser::filterProcessor(bool AllowMixed, bool Greedy) { bitAttr_t RA = ATTR_NONE; unsigned StartBit = 0; - for (BitIndex = 0; BitIndex < BIT_WIDTH; BitIndex++) { + for (BitIndex = 0; BitIndex < BitWidth; BitIndex++) { bitAttr_t bitAttr = bitAttrs[BitIndex]; assert(bitAttr != ATTR_NONE && "Bit without attributes"); @@ -1216,8 +1234,9 @@ bool FilterChooser::emit(raw_ostream &o, unsigned &Indentation) { return true; } -bool FixedLenDecoderEmitter::populateInstruction(const CodeGenInstruction &CGI, - unsigned Opc){ +static bool populateInstruction(const CodeGenInstruction &CGI, + unsigned Opc, + std::map<unsigned, std::vector<OperandInfo> >& Operands){ const Record &Def = *CGI.TheDef; // If all the bit positions are not specified; do not decode this instruction. // We are bound to fail! For proper disassembly, the well-known encoding bits @@ -1239,7 +1258,7 @@ bool FixedLenDecoderEmitter::populateInstruction(const CodeGenInstruction &CGI, // of trying to auto-generate the decoder. std::string InstDecoder = Def.getValueAsString("DecoderMethod"); if (InstDecoder != "") { - InsnOperands.push_back(OperandInfo(~0U, ~0U, InstDecoder)); + InsnOperands.push_back(OperandInfo(InstDecoder)); Operands[Opc] = InsnOperands; return true; } @@ -1259,72 +1278,97 @@ bool FixedLenDecoderEmitter::populateInstruction(const CodeGenInstruction &CGI, for (unsigned i = 0; i < In->getNumArgs(); ++i) InOutOperands.push_back(std::make_pair(In->getArg(i), In->getArgName(i))); + // Search for tied operands, so that we can correctly instantiate + // operands that are not explicitly represented in the encoding. + std::map<std::string, std::string> TiedNames; + for (unsigned i = 0; i < CGI.Operands.size(); ++i) { + int tiedTo = CGI.Operands[i].getTiedRegister(); + if (tiedTo != -1) { + TiedNames[InOutOperands[i].second] = InOutOperands[tiedTo].second; + TiedNames[InOutOperands[tiedTo].second] = InOutOperands[i].second; + } + } + // For each operand, see if we can figure out where it is encoded. for (std::vector<std::pair<Init*, std::string> >::iterator NI = InOutOperands.begin(), NE = InOutOperands.end(); NI != NE; ++NI) { - unsigned PrevBit = ~0; - unsigned Base = ~0; - unsigned PrevPos = ~0; std::string Decoder = ""; + // At this point, we can locate the field, but we need to know how to + // interpret it. As a first step, require the target to provide callbacks + // for decoding register classes. + // FIXME: This need to be extended to handle instructions with custom + // decoder methods, and operands with (simple) MIOperandInfo's. + TypedInit *TI = dynamic_cast<TypedInit*>(NI->first); + RecordRecTy *Type = dynamic_cast<RecordRecTy*>(TI->getType()); + Record *TypeRecord = Type->getRecord(); + bool isReg = false; + if (TypeRecord->isSubClassOf("RegisterOperand")) + TypeRecord = TypeRecord->getValueAsDef("RegClass"); + if (TypeRecord->isSubClassOf("RegisterClass")) { + Decoder = "Decode" + TypeRecord->getName() + "RegisterClass"; + isReg = true; + } + + RecordVal *DecoderString = TypeRecord->getValue("DecoderMethod"); + StringInit *String = DecoderString ? + dynamic_cast<StringInit*>(DecoderString->getValue()) : 0; + if (!isReg && String && String->getValue() != "") + Decoder = String->getValue(); + + OperandInfo OpInfo(Decoder); + unsigned Base = ~0U; + unsigned Width = 0; + unsigned Offset = 0; + for (unsigned bi = 0; bi < Bits.getNumBits(); ++bi) { + VarInit *Var = 0; VarBitInit *BI = dynamic_cast<VarBitInit*>(Bits.getBit(bi)); - if (!BI) continue; - - VarInit *Var = dynamic_cast<VarInit*>(BI->getVariable()); - assert(Var); - unsigned CurrBit = BI->getBitNum(); - if (Var->getName() != NI->second) continue; - - // Figure out the lowest bit of the value, and the width of the field. - // Deliberately don't try to handle cases where the field is scattered, - // or where not all bits of the the field are explicit. - if (Base == ~0U && PrevBit == ~0U && PrevPos == ~0U) { - if (CurrBit == 0) - Base = bi; - else - continue; + if (BI) + Var = dynamic_cast<VarInit*>(BI->getVariable()); + else + Var = dynamic_cast<VarInit*>(Bits.getBit(bi)); + + if (!Var) { + if (Base != ~0U) { + OpInfo.addField(Base, Width, Offset); + Base = ~0U; + Width = 0; + Offset = 0; + } + continue; } - if ((PrevPos != ~0U && bi-1 != PrevPos) || - (CurrBit != ~0U && CurrBit-1 != PrevBit)) { - PrevBit = ~0; - Base = ~0; - PrevPos = ~0; + if (Var->getName() != NI->second && + Var->getName() != TiedNames[NI->second]) { + if (Base != ~0U) { + OpInfo.addField(Base, Width, Offset); + Base = ~0U; + Width = 0; + Offset = 0; + } + continue; } - PrevPos = bi; - PrevBit = CurrBit; - - // At this point, we can locate the field, but we need to know how to - // interpret it. As a first step, require the target to provide callbacks - // for decoding register classes. - // FIXME: This need to be extended to handle instructions with custom - // decoder methods, and operands with (simple) MIOperandInfo's. - TypedInit *TI = dynamic_cast<TypedInit*>(NI->first); - RecordRecTy *Type = dynamic_cast<RecordRecTy*>(TI->getType()); - Record *TypeRecord = Type->getRecord(); - bool isReg = false; - if (TypeRecord->isSubClassOf("RegisterOperand")) - TypeRecord = TypeRecord->getValueAsDef("RegClass"); - if (TypeRecord->isSubClassOf("RegisterClass")) { - Decoder = "Decode" + TypeRecord->getName() + "RegisterClass"; - isReg = true; + if (Base == ~0U) { + Base = bi; + Width = 1; + Offset = BI ? BI->getBitNum() : 0; + } else if (BI && BI->getBitNum() != Offset + Width) { + OpInfo.addField(Base, Width, Offset); + Base = bi; + Width = 1; + Offset = BI->getBitNum(); + } else { + ++Width; } - - RecordVal *DecoderString = TypeRecord->getValue("DecoderMethod"); - StringInit *String = DecoderString ? - dynamic_cast<StringInit*>(DecoderString->getValue()) : - 0; - if (!isReg && String && String->getValue() != "") - Decoder = String->getValue(); } - if (Base != ~0U) { - InsnOperands.push_back(OperandInfo(Base, PrevBit+1, Decoder)); - DEBUG(errs() << "ENCODED OPERAND: $" << NI->second << " @ (" - << utostr(Base+PrevBit) << ", " << utostr(Base) << ")\n"); - } + if (Base != ~0U) + OpInfo.addField(Base, Width, Offset); + + if (OpInfo.numFields() > 0) + InsnOperands.push_back(OpInfo); } Operands[Opc] = InsnOperands; @@ -1351,16 +1395,43 @@ bool FixedLenDecoderEmitter::populateInstruction(const CodeGenInstruction &CGI, return true; } -void FixedLenDecoderEmitter::populateInstructions() { - for (unsigned i = 0, e = NumberedInstructions.size(); i < e; ++i) { - Record *R = NumberedInstructions[i]->TheDef; - if (R->getValueAsString("Namespace") == "TargetOpcode" || - R->getValueAsBit("isPseudo")) - continue; +static void emitHelper(llvm::raw_ostream &o, unsigned BitWidth) { + unsigned Indentation = 0; + std::string WidthStr = "uint" + utostr(BitWidth) + "_t"; - if (populateInstruction(*NumberedInstructions[i], i)) - Opcodes.push_back(i); - } + o << '\n'; + + o.indent(Indentation) << "static " << WidthStr << + " fieldFromInstruction" << BitWidth << + "(" << WidthStr <<" insn, unsigned startBit, unsigned numBits)\n"; + + o.indent(Indentation) << "{\n"; + + ++Indentation; ++Indentation; + o.indent(Indentation) << "assert(startBit + numBits <= " << BitWidth + << " && \"Instruction field out of bounds!\");\n"; + o << '\n'; + o.indent(Indentation) << WidthStr << " fieldMask;\n"; + o << '\n'; + o.indent(Indentation) << "if (numBits == " << BitWidth << ")\n"; + + ++Indentation; ++Indentation; + o.indent(Indentation) << "fieldMask = (" << WidthStr << ")-1;\n"; + --Indentation; --Indentation; + + o.indent(Indentation) << "else\n"; + + ++Indentation; ++Indentation; + o.indent(Indentation) << "fieldMask = ((1 << numBits) - 1) << startBit;\n"; + --Indentation; --Indentation; + + o << '\n'; + o.indent(Indentation) << "return (insn & fieldMask) >> startBit;\n"; + --Indentation; --Indentation; + + o.indent(Indentation) << "}\n"; + + o << '\n'; } // Emits disassembler code for instruction decoding. @@ -1372,10 +1443,47 @@ void FixedLenDecoderEmitter::run(raw_ostream &o) o << '\n'; o << "namespace llvm {\n\n"; + // Parameterize the decoders based on namespace and instruction width. NumberedInstructions = Target.getInstructionsByEnumValue(); - populateInstructions(); - FilterChooser FC(NumberedInstructions, Opcodes, Operands); - FC.emitTop(o, 0); + std::map<std::pair<std::string, unsigned>, + std::vector<unsigned> > OpcMap; + std::map<unsigned, std::vector<OperandInfo> > Operands; + + for (unsigned i = 0; i < NumberedInstructions.size(); ++i) { + const CodeGenInstruction *Inst = NumberedInstructions[i]; + Record *Def = Inst->TheDef; + unsigned Size = Def->getValueAsInt("Size"); + if (Def->getValueAsString("Namespace") == "TargetOpcode" || + Def->getValueAsBit("isPseudo") || + Def->getValueAsBit("isAsmParserOnly") || + Def->getValueAsBit("isCodeGenOnly")) + continue; + + std::string DecoderNamespace = Def->getValueAsString("DecoderNamespace"); + + if (Size) { + if (populateInstruction(*Inst, i, Operands)) { + OpcMap[std::make_pair(DecoderNamespace, Size)].push_back(i); + } + } + } + + std::set<unsigned> Sizes; + for (std::map<std::pair<std::string, unsigned>, + std::vector<unsigned> >::iterator + I = OpcMap.begin(), E = OpcMap.end(); I != E; ++I) { + // If we haven't visited this instruction width before, emit the + // helper method to extract fields. + if (!Sizes.count(I->first.second)) { + emitHelper(o, 8*I->first.second); + Sizes.insert(I->first.second); + } + + // Emit the decoder for this namespace+width combination. + FilterChooser FC(NumberedInstructions, I->second, Operands, + 8*I->first.second, this); + FC.emitTop(o, 0, I->first.first); + } o << "\n} // End llvm namespace \n"; } diff --git a/utils/TableGen/FixedLenDecoderEmitter.h b/utils/TableGen/FixedLenDecoderEmitter.h index d46a495..2df5448 100644 --- a/utils/TableGen/FixedLenDecoderEmitter.h +++ b/utils/TableGen/FixedLenDecoderEmitter.h @@ -16,26 +16,51 @@ #define FixedLenDECODEREMITTER_H #include "CodeGenTarget.h" -#include "TableGenBackend.h" +#include "llvm/TableGen/TableGenBackend.h" #include "llvm/Support/DataTypes.h" namespace llvm { +struct EncodingField { + unsigned Base, Width, Offset; + EncodingField(unsigned B, unsigned W, unsigned O) + : Base(B), Width(W), Offset(O) { } +}; + struct OperandInfo { - unsigned FieldBase; - unsigned FieldLength; + std::vector<EncodingField> Fields; std::string Decoder; - OperandInfo(unsigned FB, unsigned FL, std::string D) - : FieldBase(FB), FieldLength(FL), Decoder(D) { } + OperandInfo(std::string D) + : Decoder(D) { } + + void addField(unsigned Base, unsigned Width, unsigned Offset) { + Fields.push_back(EncodingField(Base, Width, Offset)); + } + + unsigned numFields() { return Fields.size(); } + + typedef std::vector<EncodingField>::iterator iterator; + + iterator begin() { return Fields.begin(); } + iterator end() { return Fields.end(); } }; class FixedLenDecoderEmitter : public TableGenBackend { public: - FixedLenDecoderEmitter(RecordKeeper &R) : + FixedLenDecoderEmitter(RecordKeeper &R, + std::string PredicateNamespace, + std::string GPrefix = "if (", + std::string GPostfix = " == MCDisassembler::Fail) return MCDisassembler::Fail;", + std::string ROK = "MCDisassembler::Success", + std::string RFail = "MCDisassembler::Fail", + std::string L = "") : Records(R), Target(R), - NumberedInstructions(Target.getInstructionsByEnumValue()) {} + NumberedInstructions(Target.getInstructionsByEnumValue()), + PredicateNamespace(PredicateNamespace), + GuardPrefix(GPrefix), GuardPostfix(GPostfix), + ReturnOK(ROK), ReturnFail(RFail), Locals(L) {} // run - Output the code emitter void run(raw_ostream &o); @@ -46,9 +71,11 @@ private: std::vector<const CodeGenInstruction*> NumberedInstructions; std::vector<unsigned> Opcodes; std::map<unsigned, std::vector<OperandInfo> > Operands; - - bool populateInstruction(const CodeGenInstruction &CGI, unsigned Opc); - void populateInstructions(); +public: + std::string PredicateNamespace; + std::string GuardPrefix, GuardPostfix; + std::string ReturnOK, ReturnFail; + std::string Locals; }; } // end llvm namespace diff --git a/utils/TableGen/InstrEnumEmitter.cpp b/utils/TableGen/InstrEnumEmitter.cpp index aa59689..5981afd 100644 --- a/utils/TableGen/InstrEnumEmitter.cpp +++ b/utils/TableGen/InstrEnumEmitter.cpp @@ -14,7 +14,7 @@ #include "InstrEnumEmitter.h" #include "CodeGenTarget.h" -#include "Record.h" +#include "llvm/TableGen/Record.h" #include <cstdio> using namespace llvm; diff --git a/utils/TableGen/InstrEnumEmitter.h b/utils/TableGen/InstrEnumEmitter.h index 89f8b65..c29a309 100644 --- a/utils/TableGen/InstrEnumEmitter.h +++ b/utils/TableGen/InstrEnumEmitter.h @@ -15,7 +15,7 @@ #ifndef INSTRENUM_EMITTER_H #define INSTRENUM_EMITTER_H -#include "TableGenBackend.h" +#include "llvm/TableGen/TableGenBackend.h" namespace llvm { diff --git a/utils/TableGen/InstrInfoEmitter.cpp b/utils/TableGen/InstrInfoEmitter.cpp index 5ebaf17..8341724 100644 --- a/utils/TableGen/InstrInfoEmitter.cpp +++ b/utils/TableGen/InstrInfoEmitter.cpp @@ -14,7 +14,7 @@ #include "InstrInfoEmitter.h" #include "CodeGenTarget.h" -#include "Record.h" +#include "llvm/TableGen/Record.h" #include "llvm/ADT/StringExtras.h" #include <algorithm> using namespace llvm; @@ -268,6 +268,7 @@ void InstrInfoEmitter::emitRecord(const CodeGenInstruction &Inst, unsigned Num, << Inst.TheDef->getName() << "\", 0"; // Emit all of the target indepedent flags... + if (Inst.isPseudo) OS << "|(1<<MCID::Pseudo)"; if (Inst.isReturn) OS << "|(1<<MCID::Return)"; if (Inst.isBranch) OS << "|(1<<MCID::Branch)"; if (Inst.isIndirectBranch) OS << "|(1<<MCID::IndirectBranch)"; @@ -288,6 +289,7 @@ void InstrInfoEmitter::emitRecord(const CodeGenInstruction &Inst, unsigned Num, if (Inst.isNotDuplicable) OS << "|(1<<MCID::NotDuplicable)"; if (Inst.Operands.hasOptionalDef) OS << "|(1<<MCID::HasOptionalDef)"; if (Inst.usesCustomInserter) OS << "|(1<<MCID::UsesCustomInserter)"; + if (Inst.hasPostISelHook) OS << "|(1<<MCID::HasPostISelHook)"; if (Inst.Operands.isVariadic)OS << "|(1<<MCID::Variadic)"; if (Inst.hasSideEffects) OS << "|(1<<MCID::UnmodeledSideEffects)"; if (Inst.isAsCheapAsAMove) OS << "|(1<<MCID::CheapAsAMove)"; diff --git a/utils/TableGen/InstrInfoEmitter.h b/utils/TableGen/InstrInfoEmitter.h index 165ce42..1461e2c 100644 --- a/utils/TableGen/InstrInfoEmitter.h +++ b/utils/TableGen/InstrInfoEmitter.h @@ -15,8 +15,8 @@ #ifndef INSTRINFO_EMITTER_H #define INSTRINFO_EMITTER_H -#include "TableGenBackend.h" #include "CodeGenDAGPatterns.h" +#include "llvm/TableGen/TableGenBackend.h" #include <vector> #include <map> diff --git a/utils/TableGen/IntrinsicEmitter.cpp b/utils/TableGen/IntrinsicEmitter.cpp index e5e7cea..782b89e 100644 --- a/utils/TableGen/IntrinsicEmitter.cpp +++ b/utils/TableGen/IntrinsicEmitter.cpp @@ -13,8 +13,8 @@ #include "CodeGenTarget.h" #include "IntrinsicEmitter.h" -#include "Record.h" #include "StringMatcher.h" +#include "llvm/TableGen/Record.h" #include "llvm/ADT/StringExtras.h" #include <algorithm> using namespace llvm; diff --git a/utils/TableGen/IntrinsicEmitter.h b/utils/TableGen/IntrinsicEmitter.h index b1efecb..eb6379c 100644 --- a/utils/TableGen/IntrinsicEmitter.h +++ b/utils/TableGen/IntrinsicEmitter.h @@ -15,7 +15,7 @@ #define INTRINSIC_EMITTER_H #include "CodeGenIntrinsics.h" -#include "TableGenBackend.h" +#include "llvm/TableGen/TableGenBackend.h" namespace llvm { class IntrinsicEmitter : public TableGenBackend { diff --git a/utils/TableGen/LLVMCConfigurationEmitter.cpp b/utils/TableGen/LLVMCConfigurationEmitter.cpp deleted file mode 100644 index cd0cbeb..0000000 --- a/utils/TableGen/LLVMCConfigurationEmitter.cpp +++ /dev/null @@ -1,3134 +0,0 @@ -//===- LLVMCConfigurationEmitter.cpp - Generate LLVMC config ----*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open -// Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This tablegen backend is responsible for emitting LLVMC configuration code. -// -//===----------------------------------------------------------------------===// - -#include "LLVMCConfigurationEmitter.h" -#include "Record.h" - -#include "llvm/ADT/IntrusiveRefCntPtr.h" -#include "llvm/ADT/StringMap.h" -#include "llvm/ADT/StringSet.h" - -#include <algorithm> -#include <cassert> -#include <functional> -#include <stdexcept> -#include <string> -#include <typeinfo> - - -using namespace llvm; - -namespace { - -//===----------------------------------------------------------------------===// -/// Typedefs - -typedef std::vector<Record*> RecordVector; -typedef std::vector<const DagInit*> DagVector; -typedef std::vector<std::string> StrVector; - -//===----------------------------------------------------------------------===// -/// Constants - -// Indentation. -const unsigned TabWidth = 4; -const unsigned Indent1 = TabWidth*1; -const unsigned Indent2 = TabWidth*2; -const unsigned Indent3 = TabWidth*3; -const unsigned Indent4 = TabWidth*4; - -// Default help string. -const char * const DefaultHelpString = "NO HELP MESSAGE PROVIDED"; - -// Name for the "sink" option. -const char * const SinkOptionName = "SinkOption"; - -//===----------------------------------------------------------------------===// -/// Helper functions - -/// Id - An 'identity' function object. -struct Id { - template<typename T0> - void operator()(const T0&) const { - } - template<typename T0, typename T1> - void operator()(const T0&, const T1&) const { - } - template<typename T0, typename T1, typename T2> - void operator()(const T0&, const T1&, const T2&) const { - } -}; - -int InitPtrToInt(const Init* ptr) { - const IntInit& val = dynamic_cast<const IntInit&>(*ptr); - return val.getValue(); -} - -bool InitPtrToBool(const Init* ptr) { - bool ret = false; - const DefInit& val = dynamic_cast<const DefInit&>(*ptr); - const std::string& str = val.getAsString(); - - if (str == "true") { - ret = true; - } - else if (str == "false") { - ret = false; - } - else { - throw "Incorrect boolean value: '" + str + - "': must be either 'true' or 'false'"; - } - - return ret; -} - -const std::string& InitPtrToString(const Init* ptr) { - const StringInit& val = dynamic_cast<const StringInit&>(*ptr); - return val.getValue(); -} - -const ListInit& InitPtrToList(const Init* ptr) { - const ListInit& val = dynamic_cast<const ListInit&>(*ptr); - return val; -} - -const DagInit& InitPtrToDag(const Init* ptr) { - const DagInit& val = dynamic_cast<const DagInit&>(*ptr); - return val; -} - -const std::string GetOperatorName(const DagInit& D) { - return D.getOperator()->getAsString(); -} - -/// CheckBooleanConstant - Check that the provided value is a boolean constant. -void CheckBooleanConstant(const Init* I) { - InitPtrToBool(I); -} - -// CheckNumberOfArguments - Ensure that the number of args in d is -// greater than or equal to min_arguments, otherwise throw an exception. -void CheckNumberOfArguments (const DagInit& d, unsigned minArgs) { - if (d.getNumArgs() < minArgs) - throw GetOperatorName(d) + ": too few arguments!"; -} - -// EscapeVariableName - Escape commas and other symbols not allowed -// in the C++ variable names. Makes it possible to use options named -// like "Wa," (useful for prefix options). -std::string EscapeVariableName (const std::string& Var) { - std::string ret; - for (unsigned i = 0; i != Var.size(); ++i) { - char cur_char = Var[i]; - if (cur_char == ',') { - ret += "_comma_"; - } - else if (cur_char == '+') { - ret += "_plus_"; - } - else if (cur_char == '-') { - ret += "_dash_"; - } - else { - ret.push_back(cur_char); - } - } - return ret; -} - -/// EscapeQuotes - Replace '"' with '\"'. -std::string EscapeQuotes (const std::string& Var) { - std::string ret; - for (unsigned i = 0; i != Var.size(); ++i) { - char cur_char = Var[i]; - if (cur_char == '"') { - ret += "\\\""; - } - else { - ret.push_back(cur_char); - } - } - return ret; -} - -/// OneOf - Does the input string contain this character? -bool OneOf(const char* lst, char c) { - while (*lst) { - if (*lst++ == c) - return true; - } - return false; -} - -template <class I, class S> -void CheckedIncrement(I& P, I E, S ErrorString) { - ++P; - if (P == E) - throw ErrorString; -} - -//===----------------------------------------------------------------------===// -/// Back-end specific code - - -/// OptionType - One of six different option types. See the -/// documentation for detailed description of differences. -namespace OptionType { - - enum OptionType { Alias, Switch, SwitchList, - Parameter, ParameterList, Prefix, PrefixList }; - - bool IsAlias(OptionType t) { - return (t == Alias); - } - - bool IsList (OptionType t) { - return (t == SwitchList || t == ParameterList || t == PrefixList); - } - - bool IsSwitch (OptionType t) { - return (t == Switch); - } - - bool IsSwitchList (OptionType t) { - return (t == SwitchList); - } - - bool IsParameter (OptionType t) { - return (t == Parameter || t == Prefix); - } - -} - -OptionType::OptionType stringToOptionType(const std::string& T) { - if (T == "alias_option") - return OptionType::Alias; - else if (T == "switch_option") - return OptionType::Switch; - else if (T == "switch_list_option") - return OptionType::SwitchList; - else if (T == "parameter_option") - return OptionType::Parameter; - else if (T == "parameter_list_option") - return OptionType::ParameterList; - else if (T == "prefix_option") - return OptionType::Prefix; - else if (T == "prefix_list_option") - return OptionType::PrefixList; - else - throw "Unknown option type: " + T + '!'; -} - -namespace OptionDescriptionFlags { - enum OptionDescriptionFlags { Required = 0x1, Hidden = 0x2, - ReallyHidden = 0x4, OneOrMore = 0x8, - Optional = 0x10, CommaSeparated = 0x20, - ForwardNotSplit = 0x40, ZeroOrMore = 0x80 }; -} - -/// OptionDescription - Represents data contained in a single -/// OptionList entry. -struct OptionDescription { - OptionType::OptionType Type; - std::string Name; - unsigned Flags; - std::string Help; - unsigned MultiVal; - Init* InitVal; - - OptionDescription(OptionType::OptionType t = OptionType::Switch, - const std::string& n = "", - const std::string& h = DefaultHelpString) - : Type(t), Name(n), Flags(0x0), Help(h), MultiVal(1), InitVal(0) - {} - - /// GenTypeDeclaration - Returns the C++ variable type of this - /// option. - const char* GenTypeDeclaration() const; - - /// GenVariableName - Returns the variable name used in the - /// generated C++ code. - std::string GenVariableName() const - { return "autogenerated::" + GenOptionType() + EscapeVariableName(Name); } - - /// GenPlainVariableName - Returns the variable name without the namespace - /// prefix. - std::string GenPlainVariableName() const - { return GenOptionType() + EscapeVariableName(Name); } - - /// Merge - Merge two option descriptions. - void Merge (const OptionDescription& other); - - /// CheckConsistency - Check that the flags are consistent. - void CheckConsistency() const; - - // Misc convenient getters/setters. - - bool isAlias() const; - - bool isMultiVal() const; - - bool isCommaSeparated() const; - void setCommaSeparated(); - - bool isForwardNotSplit() const; - void setForwardNotSplit(); - - bool isRequired() const; - void setRequired(); - - bool isOneOrMore() const; - void setOneOrMore(); - - bool isZeroOrMore() const; - void setZeroOrMore(); - - bool isOptional() const; - void setOptional(); - - bool isHidden() const; - void setHidden(); - - bool isReallyHidden() const; - void setReallyHidden(); - - bool isSwitch() const - { return OptionType::IsSwitch(this->Type); } - - bool isSwitchList() const - { return OptionType::IsSwitchList(this->Type); } - - bool isParameter() const - { return OptionType::IsParameter(this->Type); } - - bool isList() const - { return OptionType::IsList(this->Type); } - - bool isParameterList() const - { return (OptionType::IsList(this->Type) - && !OptionType::IsSwitchList(this->Type)); } - -private: - - // GenOptionType - Helper function used by GenVariableName(). - std::string GenOptionType() const; -}; - -void OptionDescription::CheckConsistency() const { - unsigned i = 0; - - i += this->isRequired(); - i += this->isOptional(); - i += this->isOneOrMore(); - i += this->isZeroOrMore(); - - if (i > 1) { - throw "Only one of (required), (optional), (one_or_more) or " - "(zero_or_more) properties is allowed!"; - } -} - -void OptionDescription::Merge (const OptionDescription& other) -{ - if (other.Type != Type) - throw "Conflicting definitions for the option " + Name + "!"; - - if (Help == other.Help || Help == DefaultHelpString) - Help = other.Help; - else if (other.Help != DefaultHelpString) { - llvm::errs() << "Warning: several different help strings" - " defined for option " + Name + "\n"; - } - - Flags |= other.Flags; -} - -bool OptionDescription::isAlias() const { - return OptionType::IsAlias(this->Type); -} - -bool OptionDescription::isMultiVal() const { - return MultiVal > 1; -} - -bool OptionDescription::isCommaSeparated() const { - return Flags & OptionDescriptionFlags::CommaSeparated; -} -void OptionDescription::setCommaSeparated() { - Flags |= OptionDescriptionFlags::CommaSeparated; -} - -bool OptionDescription::isForwardNotSplit() const { - return Flags & OptionDescriptionFlags::ForwardNotSplit; -} -void OptionDescription::setForwardNotSplit() { - Flags |= OptionDescriptionFlags::ForwardNotSplit; -} - -bool OptionDescription::isRequired() const { - return Flags & OptionDescriptionFlags::Required; -} -void OptionDescription::setRequired() { - Flags |= OptionDescriptionFlags::Required; -} - -bool OptionDescription::isOneOrMore() const { - return Flags & OptionDescriptionFlags::OneOrMore; -} -void OptionDescription::setOneOrMore() { - Flags |= OptionDescriptionFlags::OneOrMore; -} - -bool OptionDescription::isZeroOrMore() const { - return Flags & OptionDescriptionFlags::ZeroOrMore; -} -void OptionDescription::setZeroOrMore() { - Flags |= OptionDescriptionFlags::ZeroOrMore; -} - -bool OptionDescription::isOptional() const { - return Flags & OptionDescriptionFlags::Optional; -} -void OptionDescription::setOptional() { - Flags |= OptionDescriptionFlags::Optional; -} - -bool OptionDescription::isHidden() const { - return Flags & OptionDescriptionFlags::Hidden; -} -void OptionDescription::setHidden() { - Flags |= OptionDescriptionFlags::Hidden; -} - -bool OptionDescription::isReallyHidden() const { - return Flags & OptionDescriptionFlags::ReallyHidden; -} -void OptionDescription::setReallyHidden() { - Flags |= OptionDescriptionFlags::ReallyHidden; -} - -const char* OptionDescription::GenTypeDeclaration() const { - switch (Type) { - case OptionType::Alias: - return "cl::alias"; - case OptionType::PrefixList: - case OptionType::ParameterList: - return "cl::list<std::string>"; - case OptionType::Switch: - return "cl::opt<bool>"; - case OptionType::SwitchList: - return "cl::list<bool>"; - case OptionType::Parameter: - case OptionType::Prefix: - default: - return "cl::opt<std::string>"; - } -} - -std::string OptionDescription::GenOptionType() const { - switch (Type) { - case OptionType::Alias: - return "Alias_"; - case OptionType::PrefixList: - case OptionType::ParameterList: - return "List_"; - case OptionType::Switch: - return "Switch_"; - case OptionType::SwitchList: - return "SwitchList_"; - case OptionType::Prefix: - case OptionType::Parameter: - default: - return "Parameter_"; - } -} - -/// OptionDescriptions - An OptionDescription array plus some helper -/// functions. -class OptionDescriptions { - typedef StringMap<OptionDescription> container_type; - - /// Descriptions - A list of OptionDescriptions. - container_type Descriptions; - -public: - /// FindOption - exception-throwing wrapper for find(). - const OptionDescription& FindOption(const std::string& OptName) const; - - // Wrappers for FindOption that throw an exception in case the option has a - // wrong type. - const OptionDescription& FindSwitch(const std::string& OptName) const; - const OptionDescription& FindParameter(const std::string& OptName) const; - const OptionDescription& FindParameterList(const std::string& OptName) const; - const OptionDescription& - FindListOrParameter(const std::string& OptName) const; - const OptionDescription& - FindParameterListOrParameter(const std::string& OptName) const; - - /// insertDescription - Insert new OptionDescription into - /// OptionDescriptions list - void InsertDescription (const OptionDescription& o); - - // Support for STL-style iteration - typedef container_type::const_iterator const_iterator; - const_iterator begin() const { return Descriptions.begin(); } - const_iterator end() const { return Descriptions.end(); } -}; - -const OptionDescription& -OptionDescriptions::FindOption(const std::string& OptName) const { - const_iterator I = Descriptions.find(OptName); - if (I != Descriptions.end()) - return I->second; - else - throw OptName + ": no such option!"; -} - -const OptionDescription& -OptionDescriptions::FindSwitch(const std::string& OptName) const { - const OptionDescription& OptDesc = this->FindOption(OptName); - if (!OptDesc.isSwitch()) - throw OptName + ": incorrect option type - should be a switch!"; - return OptDesc; -} - -const OptionDescription& -OptionDescriptions::FindParameterList(const std::string& OptName) const { - const OptionDescription& OptDesc = this->FindOption(OptName); - if (!OptDesc.isList() || OptDesc.isSwitchList()) - throw OptName + ": incorrect option type - should be a parameter list!"; - return OptDesc; -} - -const OptionDescription& -OptionDescriptions::FindParameter(const std::string& OptName) const { - const OptionDescription& OptDesc = this->FindOption(OptName); - if (!OptDesc.isParameter()) - throw OptName + ": incorrect option type - should be a parameter!"; - return OptDesc; -} - -const OptionDescription& -OptionDescriptions::FindListOrParameter(const std::string& OptName) const { - const OptionDescription& OptDesc = this->FindOption(OptName); - if (!OptDesc.isList() && !OptDesc.isParameter()) - throw OptName - + ": incorrect option type - should be a list or parameter!"; - return OptDesc; -} - -const OptionDescription& -OptionDescriptions::FindParameterListOrParameter -(const std::string& OptName) const { - const OptionDescription& OptDesc = this->FindOption(OptName); - if ((!OptDesc.isList() && !OptDesc.isParameter()) || OptDesc.isSwitchList()) - throw OptName - + ": incorrect option type - should be a parameter list or parameter!"; - return OptDesc; -} - -void OptionDescriptions::InsertDescription (const OptionDescription& o) { - container_type::iterator I = Descriptions.find(o.Name); - if (I != Descriptions.end()) { - OptionDescription& D = I->second; - D.Merge(o); - } - else { - Descriptions[o.Name] = o; - } -} - -/// HandlerTable - A base class for function objects implemented as -/// 'tables of handlers'. -template <typename Handler> -class HandlerTable { -protected: - // Implementation details. - - /// HandlerMap - A map from property names to property handlers - typedef StringMap<Handler> HandlerMap; - - static HandlerMap Handlers_; - static bool staticMembersInitialized_; - -public: - - Handler GetHandler (const std::string& HandlerName) const { - typename HandlerMap::iterator method = Handlers_.find(HandlerName); - - if (method != Handlers_.end()) { - Handler h = method->second; - return h; - } - else { - throw "No handler found for property " + HandlerName + "!"; - } - } - - void AddHandler(const char* Property, Handler H) { - Handlers_[Property] = H; - } - -}; - -template <class Handler, class FunctionObject> -Handler GetHandler(FunctionObject* Obj, const DagInit& Dag) { - const std::string& HandlerName = GetOperatorName(Dag); - return Obj->GetHandler(HandlerName); -} - -template <class FunctionObject> -void InvokeDagInitHandler(FunctionObject* Obj, Init* I) { - typedef void (FunctionObject::*Handler) (const DagInit&); - - const DagInit& Dag = InitPtrToDag(I); - Handler h = GetHandler<Handler>(Obj, Dag); - - ((Obj)->*(h))(Dag); -} - -template <class FunctionObject> -void InvokeDagInitHandler(const FunctionObject* const Obj, - const Init* I, unsigned IndentLevel, raw_ostream& O) -{ - typedef void (FunctionObject::*Handler) - (const DagInit&, unsigned IndentLevel, raw_ostream& O) const; - - const DagInit& Dag = InitPtrToDag(I); - Handler h = GetHandler<Handler>(Obj, Dag); - - ((Obj)->*(h))(Dag, IndentLevel, O); -} - -template <typename H> -typename HandlerTable<H>::HandlerMap HandlerTable<H>::Handlers_; - -template <typename H> -bool HandlerTable<H>::staticMembersInitialized_ = false; - - -/// CollectOptionProperties - Function object for iterating over an -/// option property list. -class CollectOptionProperties; -typedef void (CollectOptionProperties::* CollectOptionPropertiesHandler) -(const DagInit&); - -class CollectOptionProperties -: public HandlerTable<CollectOptionPropertiesHandler> -{ -private: - - /// optDescs_ - OptionDescriptions table. This is where the - /// information is stored. - OptionDescription& optDesc_; - -public: - - explicit CollectOptionProperties(OptionDescription& OD) - : optDesc_(OD) - { - if (!staticMembersInitialized_) { - AddHandler("help", &CollectOptionProperties::onHelp); - AddHandler("hidden", &CollectOptionProperties::onHidden); - AddHandler("init", &CollectOptionProperties::onInit); - AddHandler("multi_val", &CollectOptionProperties::onMultiVal); - AddHandler("one_or_more", &CollectOptionProperties::onOneOrMore); - AddHandler("zero_or_more", &CollectOptionProperties::onZeroOrMore); - AddHandler("really_hidden", &CollectOptionProperties::onReallyHidden); - AddHandler("required", &CollectOptionProperties::onRequired); - AddHandler("optional", &CollectOptionProperties::onOptional); - AddHandler("comma_separated", &CollectOptionProperties::onCommaSeparated); - AddHandler("forward_not_split", - &CollectOptionProperties::onForwardNotSplit); - - staticMembersInitialized_ = true; - } - } - - /// operator() - Just forwards to the corresponding property - /// handler. - void operator() (Init* I) { - InvokeDagInitHandler(this, I); - } - -private: - - /// Option property handlers -- - /// Methods that handle option properties such as (help) or (hidden). - - void onHelp (const DagInit& d) { - CheckNumberOfArguments(d, 1); - optDesc_.Help = EscapeQuotes(InitPtrToString(d.getArg(0))); - } - - void onHidden (const DagInit& d) { - CheckNumberOfArguments(d, 0); - optDesc_.setHidden(); - } - - void onReallyHidden (const DagInit& d) { - CheckNumberOfArguments(d, 0); - optDesc_.setReallyHidden(); - } - - void onCommaSeparated (const DagInit& d) { - CheckNumberOfArguments(d, 0); - if (!optDesc_.isParameterList()) - throw "'comma_separated' is valid only on parameter list options!"; - optDesc_.setCommaSeparated(); - } - - void onForwardNotSplit (const DagInit& d) { - CheckNumberOfArguments(d, 0); - if (!optDesc_.isParameter()) - throw "'forward_not_split' is valid only for parameter options!"; - optDesc_.setForwardNotSplit(); - } - - void onRequired (const DagInit& d) { - CheckNumberOfArguments(d, 0); - - optDesc_.setRequired(); - optDesc_.CheckConsistency(); - } - - void onInit (const DagInit& d) { - CheckNumberOfArguments(d, 1); - Init* i = d.getArg(0); - const std::string& str = i->getAsString(); - - bool correct = optDesc_.isParameter() && dynamic_cast<StringInit*>(i); - correct |= (optDesc_.isSwitch() && (str == "true" || str == "false")); - - if (!correct) - throw "Incorrect usage of the 'init' option property!"; - - optDesc_.InitVal = i; - } - - void onOneOrMore (const DagInit& d) { - CheckNumberOfArguments(d, 0); - - optDesc_.setOneOrMore(); - optDesc_.CheckConsistency(); - } - - void onZeroOrMore (const DagInit& d) { - CheckNumberOfArguments(d, 0); - - if (optDesc_.isList()) - llvm::errs() << "Warning: specifying the 'zero_or_more' property " - "on a list option has no effect.\n"; - - optDesc_.setZeroOrMore(); - optDesc_.CheckConsistency(); - } - - void onOptional (const DagInit& d) { - CheckNumberOfArguments(d, 0); - - if (!optDesc_.isList()) - llvm::errs() << "Warning: specifying the 'optional' property" - "on a non-list option has no effect.\n"; - - optDesc_.setOptional(); - optDesc_.CheckConsistency(); - } - - void onMultiVal (const DagInit& d) { - CheckNumberOfArguments(d, 1); - int val = InitPtrToInt(d.getArg(0)); - if (val < 2) - throw "Error in the 'multi_val' property: " - "the value must be greater than 1!"; - if (!optDesc_.isParameterList()) - throw "The multi_val property is valid only on list options!"; - optDesc_.MultiVal = val; - } - -}; - -/// AddOption - A function object that is applied to every option -/// description. Used by CollectOptionDescriptions. -class AddOption { -private: - OptionDescriptions& OptDescs_; - -public: - explicit AddOption(OptionDescriptions& OD) : OptDescs_(OD) - {} - - void operator()(const Init* i) { - const DagInit& d = InitPtrToDag(i); - CheckNumberOfArguments(d, 1); - - const OptionType::OptionType Type = - stringToOptionType(GetOperatorName(d)); - const std::string& Name = InitPtrToString(d.getArg(0)); - - OptionDescription OD(Type, Name); - - CheckNumberOfArguments(d, 2); - - // Alias option store the aliased option name in the 'Help' field and do not - // have any properties. - if (OD.isAlias()) { - OD.Help = InitPtrToString(d.getArg(1)); - } - else { - processOptionProperties(d, OD); - } - - // Switch options are ZeroOrMore by default. - if (OD.isSwitch()) { - if (!(OD.isOptional() || OD.isOneOrMore() || OD.isRequired())) - OD.setZeroOrMore(); - } - - OptDescs_.InsertDescription(OD); - } - -private: - /// processOptionProperties - Go through the list of option - /// properties and call a corresponding handler for each. - static void processOptionProperties (const DagInit& d, OptionDescription& o) { - CheckNumberOfArguments(d, 2); - DagInit::const_arg_iterator B = d.arg_begin(); - // Skip the first argument: it's always the option name. - ++B; - std::for_each(B, d.arg_end(), CollectOptionProperties(o)); - } - -}; - -/// CollectOptionDescriptions - Collects option properties from all -/// OptionLists. -void CollectOptionDescriptions (const RecordVector& V, - OptionDescriptions& OptDescs) -{ - // For every OptionList: - for (RecordVector::const_iterator B = V.begin(), E = V.end(); B!=E; ++B) - { - // Throws an exception if the value does not exist. - ListInit* PropList = (*B)->getValueAsListInit("options"); - - // For every option description in this list: invoke AddOption. - std::for_each(PropList->begin(), PropList->end(), AddOption(OptDescs)); - } -} - -// Tool information record - -namespace ToolFlags { - enum ToolFlags { Join = 0x1, Sink = 0x2 }; -} - -struct ToolDescription : public RefCountedBase<ToolDescription> { - std::string Name; - Init* CmdLine; - Init* Actions; - StrVector InLanguage; - std::string InFileOption; - std::string OutFileOption; - StrVector OutLanguage; - std::string OutputSuffix; - unsigned Flags; - const Init* OnEmpty; - - // Various boolean properties - void setSink() { Flags |= ToolFlags::Sink; } - bool isSink() const { return Flags & ToolFlags::Sink; } - void setJoin() { Flags |= ToolFlags::Join; } - bool isJoin() const { return Flags & ToolFlags::Join; } - - // Default ctor here is needed because StringMap can only store - // DefaultConstructible objects - ToolDescription (const std::string &n = "") - : Name(n), CmdLine(0), Actions(0), OutFileOption("-o"), - Flags(0), OnEmpty(0) - {} -}; - -/// ToolDescriptions - A list of Tool information records. -typedef std::vector<IntrusiveRefCntPtr<ToolDescription> > ToolDescriptions; - - -/// CollectToolProperties - Function object for iterating over a list of -/// tool property records. - -class CollectToolProperties; -typedef void (CollectToolProperties::* CollectToolPropertiesHandler) -(const DagInit&); - -class CollectToolProperties : public HandlerTable<CollectToolPropertiesHandler> -{ -private: - - /// toolDesc_ - Properties of the current Tool. This is where the - /// information is stored. - ToolDescription& toolDesc_; - -public: - - explicit CollectToolProperties (ToolDescription& d) - : toolDesc_(d) - { - if (!staticMembersInitialized_) { - - AddHandler("actions", &CollectToolProperties::onActions); - AddHandler("command", &CollectToolProperties::onCommand); - AddHandler("in_language", &CollectToolProperties::onInLanguage); - AddHandler("join", &CollectToolProperties::onJoin); - AddHandler("out_language", &CollectToolProperties::onOutLanguage); - - AddHandler("out_file_option", &CollectToolProperties::onOutFileOption); - AddHandler("in_file_option", &CollectToolProperties::onInFileOption); - - AddHandler("output_suffix", &CollectToolProperties::onOutputSuffix); - AddHandler("sink", &CollectToolProperties::onSink); - AddHandler("works_on_empty", &CollectToolProperties::onWorksOnEmpty); - - staticMembersInitialized_ = true; - } - } - - void operator() (Init* I) { - InvokeDagInitHandler(this, I); - } - -private: - - /// Property handlers -- - /// Functions that extract information about tool properties from - /// DAG representation. - - void onActions (const DagInit& d) { - CheckNumberOfArguments(d, 1); - Init* Case = d.getArg(0); - if (typeid(*Case) != typeid(DagInit) || - GetOperatorName(static_cast<DagInit&>(*Case)) != "case") - throw "The argument to (actions) should be a 'case' construct!"; - toolDesc_.Actions = Case; - } - - void onCommand (const DagInit& d) { - CheckNumberOfArguments(d, 1); - toolDesc_.CmdLine = d.getArg(0); - } - - /// onInOutLanguage - Common implementation of on{In,Out}Language(). - void onInOutLanguage (const DagInit& d, StrVector& OutVec) { - CheckNumberOfArguments(d, 1); - - // Copy strings to the output vector. - for (unsigned i = 0, NumArgs = d.getNumArgs(); i < NumArgs; ++i) { - OutVec.push_back(InitPtrToString(d.getArg(i))); - } - - // Remove duplicates. - std::sort(OutVec.begin(), OutVec.end()); - StrVector::iterator newE = std::unique(OutVec.begin(), OutVec.end()); - OutVec.erase(newE, OutVec.end()); - } - - - void onInLanguage (const DagInit& d) { - this->onInOutLanguage(d, toolDesc_.InLanguage); - } - - void onJoin (const DagInit& d) { - bool isReallyJoin = false; - - if (d.getNumArgs() == 0) { - isReallyJoin = true; - } - else { - Init* I = d.getArg(0); - isReallyJoin = InitPtrToBool(I); - } - - // Is this *really* a join tool? We allow (join false) for generating two - // tool descriptions from a single generic one. - // TOFIX: come up with a cleaner solution. - if (isReallyJoin) { - toolDesc_.setJoin(); - } - } - - void onOutLanguage (const DagInit& d) { - this->onInOutLanguage(d, toolDesc_.OutLanguage); - } - - void onOutFileOption (const DagInit& d) { - CheckNumberOfArguments(d, 1); - toolDesc_.OutFileOption = InitPtrToString(d.getArg(0)); - } - - void onInFileOption (const DagInit& d) { - CheckNumberOfArguments(d, 1); - toolDesc_.InFileOption = InitPtrToString(d.getArg(0)); - } - - void onOutputSuffix (const DagInit& d) { - CheckNumberOfArguments(d, 1); - toolDesc_.OutputSuffix = InitPtrToString(d.getArg(0)); - } - - void onSink (const DagInit& d) { - CheckNumberOfArguments(d, 0); - toolDesc_.setSink(); - } - - void onWorksOnEmpty (const DagInit& d) { - toolDesc_.OnEmpty = d.getArg(0); - } - -}; - -/// CollectToolDescriptions - Gather information about tool properties -/// from the parsed TableGen data (basically a wrapper for the -/// CollectToolProperties function object). -void CollectToolDescriptions (const RecordVector& Tools, - ToolDescriptions& ToolDescs) -{ - // Iterate over a properties list of every Tool definition - for (RecordVector::const_iterator B = Tools.begin(), - E = Tools.end(); B!=E; ++B) { - const Record* T = *B; - // Throws an exception if the value does not exist. - ListInit* PropList = T->getValueAsListInit("properties"); - - IntrusiveRefCntPtr<ToolDescription> - ToolDesc(new ToolDescription(T->getName())); - - std::for_each(PropList->begin(), PropList->end(), - CollectToolProperties(*ToolDesc)); - ToolDescs.push_back(ToolDesc); - } -} - -/// FillInEdgeVector - Merge all compilation graph definitions into -/// one single edge list. -void FillInEdgeVector(const RecordVector& CompilationGraphs, - DagVector& Out) { - for (RecordVector::const_iterator B = CompilationGraphs.begin(), - E = CompilationGraphs.end(); B != E; ++B) { - const ListInit* Edges = (*B)->getValueAsListInit("edges"); - - for (ListInit::const_iterator B = Edges->begin(), - E = Edges->end(); B != E; ++B) { - Out.push_back(&InitPtrToDag(*B)); - } - } -} - -/// NotInGraph - Helper function object for FilterNotInGraph. -struct NotInGraph { -private: - const llvm::StringSet<>& ToolsInGraph_; - -public: - NotInGraph(const llvm::StringSet<>& ToolsInGraph) - : ToolsInGraph_(ToolsInGraph) - {} - - bool operator()(const IntrusiveRefCntPtr<ToolDescription>& x) { - return (ToolsInGraph_.count(x->Name) == 0); - } -}; - -/// FilterNotInGraph - Filter out from ToolDescs all Tools not -/// mentioned in the compilation graph definition. -void FilterNotInGraph (const DagVector& EdgeVector, - ToolDescriptions& ToolDescs) { - - // List all tools mentioned in the graph. - llvm::StringSet<> ToolsInGraph; - - for (DagVector::const_iterator B = EdgeVector.begin(), - E = EdgeVector.end(); B != E; ++B) { - - const DagInit* Edge = *B; - const std::string& NodeA = InitPtrToString(Edge->getArg(0)); - const std::string& NodeB = InitPtrToString(Edge->getArg(1)); - - if (NodeA != "root") - ToolsInGraph.insert(NodeA); - ToolsInGraph.insert(NodeB); - } - - // Filter ToolPropertiesList. - ToolDescriptions::iterator new_end = - std::remove_if(ToolDescs.begin(), ToolDescs.end(), - NotInGraph(ToolsInGraph)); - ToolDescs.erase(new_end, ToolDescs.end()); -} - -/// FillInToolToLang - Fills in two tables that map tool names to -/// input & output language names. Helper function used by TypecheckGraph(). -void FillInToolToLang (const ToolDescriptions& ToolDescs, - StringMap<StringSet<> >& ToolToInLang, - StringMap<StringSet<> >& ToolToOutLang) { - for (ToolDescriptions::const_iterator B = ToolDescs.begin(), - E = ToolDescs.end(); B != E; ++B) { - const ToolDescription& D = *(*B); - for (StrVector::const_iterator B = D.InLanguage.begin(), - E = D.InLanguage.end(); B != E; ++B) - ToolToInLang[D.Name].insert(*B); - for (StrVector::const_iterator B = D.OutLanguage.begin(), - E = D.OutLanguage.end(); B != E; ++B) - ToolToOutLang[D.Name].insert(*B); - } -} - -/// Intersect - Is set intersection non-empty? -bool Intersect (const StringSet<>& S1, const StringSet<>& S2) { - for (StringSet<>::const_iterator B = S1.begin(), E = S1.end(); B != E; ++B) { - if (S2.count(B->first()) != 0) - return true; - } - return false; -} - -/// TypecheckGraph - Check that names for output and input languages -/// on all edges do match. -void TypecheckGraph (const DagVector& EdgeVector, - const ToolDescriptions& ToolDescs) { - StringMap<StringSet<> > ToolToInLang; - StringMap<StringSet<> > ToolToOutLang; - - FillInToolToLang(ToolDescs, ToolToInLang, ToolToOutLang); - - for (DagVector::const_iterator B = EdgeVector.begin(), - E = EdgeVector.end(); B != E; ++B) { - const DagInit* Edge = *B; - const std::string& NodeA = InitPtrToString(Edge->getArg(0)); - const std::string& NodeB = InitPtrToString(Edge->getArg(1)); - StringMap<StringSet<> >::iterator IA = ToolToOutLang.find(NodeA); - StringMap<StringSet<> >::iterator IB = ToolToInLang.find(NodeB); - - if (NodeB == "root") - throw "Edges back to the root are not allowed!"; - - if (NodeA != "root") { - if (IA == ToolToOutLang.end()) - throw NodeA + ": no output language defined!"; - if (IB == ToolToInLang.end()) - throw NodeB + ": no input language defined!"; - - if (!Intersect(IA->second, IB->second)) { - throw "Edge " + NodeA + "->" + NodeB - + ": output->input language mismatch"; - } - } - } -} - -/// WalkCase - Walks the 'case' expression DAG and invokes -/// TestCallback on every test, and StatementCallback on every -/// statement. Handles 'case' nesting, but not the 'and' and 'or' -/// combinators (that is, they are passed directly to TestCallback). -/// TestCallback must have type 'void TestCallback(const DagInit*, unsigned -/// IndentLevel, bool FirstTest)'. -/// StatementCallback must have type 'void StatementCallback(const Init*, -/// unsigned IndentLevel)'. -template <typename F1, typename F2> -void WalkCase(const Init* Case, F1 TestCallback, F2 StatementCallback, - unsigned IndentLevel = 0) -{ - const DagInit& d = InitPtrToDag(Case); - - // Error checks. - if (GetOperatorName(d) != "case") - throw "WalkCase should be invoked only on 'case' expressions!"; - - if (d.getNumArgs() < 2) - throw "There should be at least one clause in the 'case' expression:\n" - + d.getAsString(); - - // Main loop. - bool even = false; - const unsigned numArgs = d.getNumArgs(); - unsigned i = 1; - for (DagInit::const_arg_iterator B = d.arg_begin(), E = d.arg_end(); - B != E; ++B) { - Init* arg = *B; - - if (!even) - { - // Handle test. - const DagInit& Test = InitPtrToDag(arg); - - if (GetOperatorName(Test) == "default" && (i+1 != numArgs)) - throw "The 'default' clause should be the last in the " - "'case' construct!"; - if (i == numArgs) - throw "Case construct handler: no corresponding action " - "found for the test " + Test.getAsString() + '!'; - - TestCallback(Test, IndentLevel, (i == 1)); - } - else - { - if (dynamic_cast<DagInit*>(arg) - && GetOperatorName(static_cast<DagInit&>(*arg)) == "case") { - // Nested 'case'. - WalkCase(arg, TestCallback, StatementCallback, IndentLevel + Indent1); - } - - // Handle statement. - StatementCallback(arg, IndentLevel); - } - - ++i; - even = !even; - } -} - -/// ExtractOptionNames - A helper function object used by -/// CheckForSuperfluousOptions() to walk the 'case' DAG. -class ExtractOptionNames { - llvm::StringSet<>& OptionNames_; - - void processDag(const Init* Statement) { - const DagInit& Stmt = InitPtrToDag(Statement); - const std::string& ActionName = GetOperatorName(Stmt); - if (ActionName == "forward" || ActionName == "forward_as" || - ActionName == "forward_value" || - ActionName == "forward_transformed_value" || - ActionName == "parameter_equals" || ActionName == "element_in_list") { - CheckNumberOfArguments(Stmt, 1); - - Init* Arg = Stmt.getArg(0); - if (typeid(*Arg) == typeid(StringInit)) - OptionNames_.insert(InitPtrToString(Arg)); - } - else if (ActionName == "any_switch_on" || ActionName == "switch_on" || - ActionName == "any_not_empty" || ActionName == "any_empty" || - ActionName == "not_empty" || ActionName == "empty") { - for (unsigned i = 0, NumArgs = Stmt.getNumArgs(); i < NumArgs; ++i) { - Init* Arg = Stmt.getArg(i); - if (typeid(*Arg) == typeid(StringInit)) - OptionNames_.insert(InitPtrToString(Arg)); - } - } - else if (ActionName == "and" || ActionName == "or" || ActionName == "not") { - for (unsigned i = 0, NumArgs = Stmt.getNumArgs(); i < NumArgs; ++i) { - this->processDag(Stmt.getArg(i)); - } - } - } - -public: - ExtractOptionNames(llvm::StringSet<>& OptionNames) : OptionNames_(OptionNames) - {} - - void operator()(const Init* Statement) { - // Statement is either a dag, or a list of dags. - if (typeid(*Statement) == typeid(ListInit)) { - const ListInit& DagList = *static_cast<const ListInit*>(Statement); - for (ListInit::const_iterator B = DagList.begin(), E = DagList.end(); - B != E; ++B) - this->processDag(*B); - } - else { - this->processDag(Statement); - } - } - - void operator()(const DagInit& Test, unsigned, bool) { - this->operator()(&Test); - } - void operator()(const Init* Statement, unsigned) { - this->operator()(Statement); - } -}; - -/// IsOptionalEdge - Validate that the 'optional_edge' has proper structure. -bool IsOptionalEdge (const DagInit& Edg) { - return (GetOperatorName(Edg) == "optional_edge") && (Edg.getNumArgs() > 2); -} - -/// CheckForSuperfluousOptions - Check that there are no side -/// effect-free options (specified only in the OptionList). Otherwise, -/// output a warning. -void CheckForSuperfluousOptions (const DagVector& EdgeVector, - const ToolDescriptions& ToolDescs, - const OptionDescriptions& OptDescs) { - llvm::StringSet<> nonSuperfluousOptions; - - // Add all options mentioned in the ToolDesc.Actions to the set of - // non-superfluous options. - for (ToolDescriptions::const_iterator B = ToolDescs.begin(), - E = ToolDescs.end(); B != E; ++B) { - const ToolDescription& TD = *(*B); - ExtractOptionNames Callback(nonSuperfluousOptions); - if (TD.Actions) - WalkCase(TD.Actions, Callback, Callback); - } - - // Add all options mentioned in the 'case' clauses of the - // OptionalEdges of the compilation graph to the set of - // non-superfluous options. - for (DagVector::const_iterator B = EdgeVector.begin(), - E = EdgeVector.end(); B != E; ++B) { - const DagInit& Edge = **B; - if (IsOptionalEdge(Edge)) { - const DagInit& Weight = InitPtrToDag(Edge.getArg(2)); - WalkCase(&Weight, ExtractOptionNames(nonSuperfluousOptions), Id()); - } - } - - // Check that all options in OptDescs belong to the set of - // non-superfluous options. - for (OptionDescriptions::const_iterator B = OptDescs.begin(), - E = OptDescs.end(); B != E; ++B) { - const OptionDescription& Val = B->second; - if (!nonSuperfluousOptions.count(Val.Name) - && Val.Type != OptionType::Alias) - llvm::errs() << "Warning: option '-" << Val.Name << "' has no effect! " - "Probable cause: this option is specified only in the OptionList.\n"; - } -} - -/// EmitCaseTest0Args - Helper function used by EmitCaseConstructHandler(). -bool EmitCaseTest0Args(const std::string& TestName, raw_ostream& O) { - if (TestName == "single_input_file") { - O << "InputFilenames.size() == 1"; - return true; - } - else if (TestName == "multiple_input_files") { - O << "InputFilenames.size() > 1"; - return true; - } - - return false; -} - -/// EmitMultipleArgumentTest - Helper function used by -/// EmitCaseTestMultipleArgs() -template <typename F> -void EmitMultipleArgumentTest(const DagInit& D, const char* LogicOp, - F Callback, raw_ostream& O) -{ - for (unsigned i = 0, NumArgs = D.getNumArgs(); i < NumArgs; ++i) { - if (i != 0) - O << ' ' << LogicOp << ' '; - Callback(InitPtrToString(D.getArg(i)), O); - } -} - -// Callbacks for use with EmitMultipleArgumentTest - -class EmitSwitchOn { - const OptionDescriptions& OptDescs_; -public: - EmitSwitchOn(const OptionDescriptions& OptDescs) : OptDescs_(OptDescs) - {} - - void operator()(const std::string& OptName, raw_ostream& O) const { - const OptionDescription& OptDesc = OptDescs_.FindSwitch(OptName); - O << OptDesc.GenVariableName(); - } -}; - -class EmitEmptyTest { - bool EmitNegate_; - const OptionDescriptions& OptDescs_; -public: - EmitEmptyTest(bool EmitNegate, const OptionDescriptions& OptDescs) - : EmitNegate_(EmitNegate), OptDescs_(OptDescs) - {} - - void operator()(const std::string& OptName, raw_ostream& O) const { - const char* Neg = (EmitNegate_ ? "!" : ""); - if (OptName == "o") { - O << Neg << "OutputFilename.empty()"; - } - else if (OptName == "save-temps") { - O << Neg << "(SaveTemps == SaveTempsEnum::Unset)"; - } - else { - const OptionDescription& OptDesc = OptDescs_.FindListOrParameter(OptName); - O << Neg << OptDesc.GenVariableName() << ".empty()"; - } - } -}; - - -/// EmitCaseTestMultipleArgs - Helper function used by EmitCaseTest1Arg() -bool EmitCaseTestMultipleArgs (const std::string& TestName, - const DagInit& d, - const OptionDescriptions& OptDescs, - raw_ostream& O) { - if (TestName == "any_switch_on") { - EmitMultipleArgumentTest(d, "||", EmitSwitchOn(OptDescs), O); - return true; - } - else if (TestName == "switch_on") { - EmitMultipleArgumentTest(d, "&&", EmitSwitchOn(OptDescs), O); - return true; - } - else if (TestName == "any_not_empty") { - EmitMultipleArgumentTest(d, "||", EmitEmptyTest(true, OptDescs), O); - return true; - } - else if (TestName == "any_empty") { - EmitMultipleArgumentTest(d, "||", EmitEmptyTest(false, OptDescs), O); - return true; - } - else if (TestName == "not_empty") { - EmitMultipleArgumentTest(d, "&&", EmitEmptyTest(true, OptDescs), O); - return true; - } - else if (TestName == "empty") { - EmitMultipleArgumentTest(d, "&&", EmitEmptyTest(false, OptDescs), O); - return true; - } - - return false; -} - -/// EmitCaseTest1Arg - Helper function used by EmitCaseTest1OrMoreArgs() -bool EmitCaseTest1Arg (const std::string& TestName, - const DagInit& d, - const OptionDescriptions& OptDescs, - raw_ostream& O) { - const std::string& Arg = InitPtrToString(d.getArg(0)); - - if (TestName == "input_languages_contain") { - O << "InLangs.count(\"" << Arg << "\") != 0"; - return true; - } - else if (TestName == "in_language") { - // This works only for single-argument Tool::GenerateAction. Join - // tools can process several files in different languages simultaneously. - - // TODO: make this work with Edge::Weight (if possible). - O << "LangMap.GetLanguage(inFile) == \"" << Arg << '\"'; - return true; - } - - return false; -} - -/// EmitCaseTest1OrMoreArgs - Helper function used by -/// EmitCaseConstructHandler() -bool EmitCaseTest1OrMoreArgs(const std::string& TestName, - const DagInit& d, - const OptionDescriptions& OptDescs, - raw_ostream& O) { - CheckNumberOfArguments(d, 1); - return EmitCaseTest1Arg(TestName, d, OptDescs, O) || - EmitCaseTestMultipleArgs(TestName, d, OptDescs, O); -} - -/// EmitCaseTest2Args - Helper function used by EmitCaseConstructHandler(). -bool EmitCaseTest2Args(const std::string& TestName, - const DagInit& d, - unsigned IndentLevel, - const OptionDescriptions& OptDescs, - raw_ostream& O) { - CheckNumberOfArguments(d, 2); - const std::string& OptName = InitPtrToString(d.getArg(0)); - const std::string& OptArg = InitPtrToString(d.getArg(1)); - - if (TestName == "parameter_equals") { - const OptionDescription& OptDesc = OptDescs.FindParameter(OptName); - O << OptDesc.GenVariableName() << " == \"" << OptArg << "\""; - return true; - } - else if (TestName == "element_in_list") { - const OptionDescription& OptDesc = OptDescs.FindParameterList(OptName); - const std::string& VarName = OptDesc.GenVariableName(); - O << "std::find(" << VarName << ".begin(),\n"; - O.indent(IndentLevel + Indent1) - << VarName << ".end(), \"" - << OptArg << "\") != " << VarName << ".end()"; - return true; - } - - return false; -} - -// Forward declaration. -// EmitLogicalOperationTest and EmitCaseTest are mutually recursive. -void EmitCaseTest(const DagInit& d, unsigned IndentLevel, - const OptionDescriptions& OptDescs, - raw_ostream& O); - -/// EmitLogicalOperationTest - Helper function used by -/// EmitCaseConstructHandler. -void EmitLogicalOperationTest(const DagInit& d, const char* LogicOp, - unsigned IndentLevel, - const OptionDescriptions& OptDescs, - raw_ostream& O) { - O << '('; - for (unsigned i = 0, NumArgs = d.getNumArgs(); i < NumArgs; ++i) { - const DagInit& InnerTest = InitPtrToDag(d.getArg(i)); - EmitCaseTest(InnerTest, IndentLevel, OptDescs, O); - if (i != NumArgs - 1) { - O << ")\n"; - O.indent(IndentLevel + Indent1) << ' ' << LogicOp << " ("; - } - else { - O << ')'; - } - } -} - -void EmitLogicalNot(const DagInit& d, unsigned IndentLevel, - const OptionDescriptions& OptDescs, raw_ostream& O) -{ - CheckNumberOfArguments(d, 1); - const DagInit& InnerTest = InitPtrToDag(d.getArg(0)); - O << "! ("; - EmitCaseTest(InnerTest, IndentLevel, OptDescs, O); - O << ")"; -} - -/// EmitCaseTest - Helper function used by EmitCaseConstructHandler. -void EmitCaseTest(const DagInit& d, unsigned IndentLevel, - const OptionDescriptions& OptDescs, - raw_ostream& O) { - const std::string& TestName = GetOperatorName(d); - - if (TestName == "and") - EmitLogicalOperationTest(d, "&&", IndentLevel, OptDescs, O); - else if (TestName == "or") - EmitLogicalOperationTest(d, "||", IndentLevel, OptDescs, O); - else if (TestName == "not") - EmitLogicalNot(d, IndentLevel, OptDescs, O); - else if (EmitCaseTest0Args(TestName, O)) - return; - else if (EmitCaseTest1OrMoreArgs(TestName, d, OptDescs, O)) - return; - else if (EmitCaseTest2Args(TestName, d, IndentLevel, OptDescs, O)) - return; - else - throw "Unknown test '" + TestName + "' used in the 'case' construct!"; -} - - -/// EmitCaseTestCallback - Callback used by EmitCaseConstructHandler. -class EmitCaseTestCallback { - bool EmitElseIf_; - const OptionDescriptions& OptDescs_; - raw_ostream& O_; -public: - - EmitCaseTestCallback(bool EmitElseIf, - const OptionDescriptions& OptDescs, raw_ostream& O) - : EmitElseIf_(EmitElseIf), OptDescs_(OptDescs), O_(O) - {} - - void operator()(const DagInit& Test, unsigned IndentLevel, bool FirstTest) - { - if (GetOperatorName(Test) == "default") { - O_.indent(IndentLevel) << "else {\n"; - } - else { - O_.indent(IndentLevel) - << ((!FirstTest && EmitElseIf_) ? "else if (" : "if ("); - EmitCaseTest(Test, IndentLevel, OptDescs_, O_); - O_ << ") {\n"; - } - } -}; - -/// EmitCaseStatementCallback - Callback used by EmitCaseConstructHandler. -template <typename F> -class EmitCaseStatementCallback { - F Callback_; - raw_ostream& O_; -public: - - EmitCaseStatementCallback(F Callback, raw_ostream& O) - : Callback_(Callback), O_(O) - {} - - void operator() (const Init* Statement, unsigned IndentLevel) { - // Is this a nested 'case'? - bool IsCase = dynamic_cast<const DagInit*>(Statement) && - GetOperatorName(static_cast<const DagInit&>(*Statement)) == "case"; - - // If so, ignore it, it is handled by our caller, WalkCase. - if (!IsCase) { - if (typeid(*Statement) == typeid(ListInit)) { - const ListInit& DagList = *static_cast<const ListInit*>(Statement); - for (ListInit::const_iterator B = DagList.begin(), E = DagList.end(); - B != E; ++B) - Callback_(*B, (IndentLevel + Indent1), O_); - } - else { - Callback_(Statement, (IndentLevel + Indent1), O_); - } - } - O_.indent(IndentLevel) << "}\n"; - } - -}; - -/// EmitCaseConstructHandler - Emit code that handles the 'case' -/// construct. Takes a function object that should emit code for every case -/// clause. Implemented on top of WalkCase. -/// Callback's type is void F(const Init* Statement, unsigned IndentLevel, -/// raw_ostream& O). -/// EmitElseIf parameter controls the type of condition that is emitted ('if -/// (..) {..} else if (..) {} .. else {..}' vs. 'if (..) {..} if(..) {..} -/// .. else {..}'). -template <typename F> -void EmitCaseConstructHandler(const Init* Case, unsigned IndentLevel, - F Callback, bool EmitElseIf, - const OptionDescriptions& OptDescs, - raw_ostream& O) { - WalkCase(Case, EmitCaseTestCallback(EmitElseIf, OptDescs, O), - EmitCaseStatementCallback<F>(Callback, O), IndentLevel); -} - -/// TokenizeCmdLine - converts from -/// "$CALL(HookName, 'Arg1', 'Arg2')/path -arg1 -arg2" to -/// ["$CALL(", "HookName", "Arg1", "Arg2", ")/path", "-arg1", "-arg2"]. -void TokenizeCmdLine(const std::string& CmdLine, StrVector& Out) { - const char* Delimiters = " \t\n\v\f\r"; - enum TokenizerState - { Normal, SpecialCommand, InsideSpecialCommand, InsideQuotationMarks } - cur_st = Normal; - - if (CmdLine.empty()) - return; - Out.push_back(""); - - std::string::size_type B = CmdLine.find_first_not_of(Delimiters), - E = CmdLine.size(); - - for (; B != E; ++B) { - char cur_ch = CmdLine[B]; - - switch (cur_st) { - case Normal: - if (cur_ch == '$') { - cur_st = SpecialCommand; - break; - } - if (OneOf(Delimiters, cur_ch)) { - // Skip whitespace - B = CmdLine.find_first_not_of(Delimiters, B); - if (B == std::string::npos) { - B = E-1; - continue; - } - --B; - Out.push_back(""); - continue; - } - break; - - - case SpecialCommand: - if (OneOf(Delimiters, cur_ch)) { - cur_st = Normal; - Out.push_back(""); - continue; - } - if (cur_ch == '(') { - Out.push_back(""); - cur_st = InsideSpecialCommand; - continue; - } - break; - - case InsideSpecialCommand: - if (OneOf(Delimiters, cur_ch)) { - continue; - } - if (cur_ch == '\'') { - cur_st = InsideQuotationMarks; - Out.push_back(""); - continue; - } - if (cur_ch == ')') { - cur_st = Normal; - Out.push_back(""); - } - if (cur_ch == ',') { - continue; - } - - break; - - case InsideQuotationMarks: - if (cur_ch == '\'') { - cur_st = InsideSpecialCommand; - continue; - } - break; - } - - Out.back().push_back(cur_ch); - } -} - -/// SubstituteCall - Given "$CALL(HookName, [Arg1 [, Arg2 [...]]])", output -/// "hooks::HookName([Arg1 [, Arg2 [, ...]]])". Helper function used by -/// SubstituteSpecialCommands(). -StrVector::const_iterator -SubstituteCall (StrVector::const_iterator Pos, - StrVector::const_iterator End, - bool IsJoin, raw_ostream& O) -{ - const char* errorMessage = "Syntax error in $CALL invocation!"; - CheckedIncrement(Pos, End, errorMessage); - const std::string& CmdName = *Pos; - - if (CmdName == ")") - throw "$CALL invocation: empty argument list!"; - - O << "hooks::"; - O << CmdName << "("; - - - bool firstIteration = true; - while (true) { - CheckedIncrement(Pos, End, errorMessage); - const std::string& Arg = *Pos; - assert(Arg.size() != 0); - - if (Arg[0] == ')') - break; - - if (firstIteration) - firstIteration = false; - else - O << ", "; - - if (Arg == "$INFILE") { - if (IsJoin) - throw "$CALL(Hook, $INFILE) can't be used with a Join tool!"; - else - O << "inFile.c_str()"; - } - else { - O << '"' << Arg << '"'; - } - } - - O << ')'; - - return Pos; -} - -/// SubstituteEnv - Given '$ENV(VAR_NAME)', output 'getenv("VAR_NAME")'. Helper -/// function used by SubstituteSpecialCommands(). -StrVector::const_iterator -SubstituteEnv (StrVector::const_iterator Pos, - StrVector::const_iterator End, raw_ostream& O) -{ - const char* errorMessage = "Syntax error in $ENV invocation!"; - CheckedIncrement(Pos, End, errorMessage); - const std::string& EnvName = *Pos; - - if (EnvName == ")") - throw "$ENV invocation: empty argument list!"; - - O << "checkCString(std::getenv(\""; - O << EnvName; - O << "\"))"; - - CheckedIncrement(Pos, End, errorMessage); - - return Pos; -} - -/// SubstituteSpecialCommands - Given an invocation of $CALL or $ENV, output -/// handler code. Helper function used by EmitCmdLineVecFill(). -StrVector::const_iterator -SubstituteSpecialCommands (StrVector::const_iterator Pos, - StrVector::const_iterator End, - bool IsJoin, raw_ostream& O) -{ - - const std::string& cmd = *Pos; - - // Perform substitution. - if (cmd == "$CALL") { - Pos = SubstituteCall(Pos, End, IsJoin, O); - } - else if (cmd == "$ENV") { - Pos = SubstituteEnv(Pos, End, O); - } - else { - throw "Unknown special command: " + cmd; - } - - // Handle '$CMD(ARG)/additional/text'. - const std::string& Leftover = *Pos; - assert(Leftover.at(0) == ')'); - if (Leftover.size() != 1) - O << " + std::string(\"" << (Leftover.c_str() + 1) << "\")"; - - return Pos; -} - -/// EmitCmdLineVecFill - Emit code that fills in the command line -/// vector. Helper function used by EmitGenerateActionMethod(). -void EmitCmdLineVecFill(const Init* CmdLine, const std::string& ToolName, - bool IsJoin, unsigned IndentLevel, - raw_ostream& O) { - StrVector StrVec; - TokenizeCmdLine(InitPtrToString(CmdLine), StrVec); - - if (StrVec.empty()) - throw "Tool '" + ToolName + "' has empty command line!"; - - StrVector::const_iterator B = StrVec.begin(), E = StrVec.end(); - - // Emit the command itself. - assert(!StrVec[0].empty()); - O.indent(IndentLevel) << "cmd = "; - if (StrVec[0][0] == '$') { - B = SubstituteSpecialCommands(B, E, IsJoin, O); - ++B; - } - else { - O << '"' << StrVec[0] << '"'; - ++B; - } - O << ";\n"; - - // Go through the command arguments. - assert(B <= E); - for (; B != E; ++B) { - const std::string& cmd = *B; - - assert(!cmd.empty()); - O.indent(IndentLevel); - - if (cmd.at(0) == '$') { - O << "vec.push_back(std::make_pair(0, "; - B = SubstituteSpecialCommands(B, E, IsJoin, O); - O << "));\n"; - } - else { - O << "vec.push_back(std::make_pair(0, \"" << cmd << "\"));\n"; - } - } - -} - -/// EmitForEachListElementCycleHeader - Emit common code for iterating through -/// all elements of a list. Helper function used by -/// EmitForwardOptionPropertyHandlingCode. -void EmitForEachListElementCycleHeader (const OptionDescription& D, - unsigned IndentLevel, - raw_ostream& O) { - unsigned IndentLevel1 = IndentLevel + Indent1; - - O.indent(IndentLevel) - << "for (" << D.GenTypeDeclaration() - << "::iterator B = " << D.GenVariableName() << ".begin(),\n"; - O.indent(IndentLevel) - << "E = " << D.GenVariableName() << ".end(); B != E;) {\n"; - O.indent(IndentLevel1) << "unsigned pos = " << D.GenVariableName() - << ".getPosition(B - " << D.GenVariableName() - << ".begin());\n"; -} - -/// EmitForwardOptionPropertyHandlingCode - Helper function used to -/// implement EmitActionHandler. Emits code for -/// handling the (forward) and (forward_as) option properties. -void EmitForwardOptionPropertyHandlingCode (const OptionDescription& D, - unsigned IndentLevel, - const std::string& NewName, - raw_ostream& O) { - const std::string& Name = NewName.empty() - ? ("-" + D.Name) - : NewName; - unsigned IndentLevel1 = IndentLevel + Indent1; - - switch (D.Type) { - case OptionType::Switch: - O.indent(IndentLevel) - << "vec.push_back(std::make_pair(" << D.GenVariableName() - << ".getPosition(), \"" << Name << "\"));\n"; - break; - case OptionType::Parameter: - O.indent(IndentLevel) << "vec.push_back(std::make_pair(" - << D.GenVariableName() - <<".getPosition(), \"" << Name; - - if (!D.isForwardNotSplit()) { - O << "\"));\n"; - O.indent(IndentLevel) << "vec.push_back(std::make_pair(" - << D.GenVariableName() << ".getPosition(), " - << D.GenVariableName() << "));\n"; - } - else { - O << "=\" + " << D.GenVariableName() << "));\n"; - } - break; - case OptionType::Prefix: - O.indent(IndentLevel) << "vec.push_back(std::make_pair(" - << D.GenVariableName() << ".getPosition(), \"" - << Name << "\" + " - << D.GenVariableName() << "));\n"; - break; - case OptionType::PrefixList: - EmitForEachListElementCycleHeader(D, IndentLevel, O); - O.indent(IndentLevel1) << "vec.push_back(std::make_pair(pos, \"" - << Name << "\" + " << "*B));\n"; - O.indent(IndentLevel1) << "++B;\n"; - - for (int i = 1, j = D.MultiVal; i < j; ++i) { - O.indent(IndentLevel1) << "vec.push_back(std::make_pair(pos, *B));\n"; - O.indent(IndentLevel1) << "++B;\n"; - } - - O.indent(IndentLevel) << "}\n"; - break; - case OptionType::ParameterList: - EmitForEachListElementCycleHeader(D, IndentLevel, O); - O.indent(IndentLevel1) << "vec.push_back(std::make_pair(pos, \"" - << Name << "\"));\n"; - - for (int i = 0, j = D.MultiVal; i < j; ++i) { - O.indent(IndentLevel1) << "vec.push_back(std::make_pair(pos, *B));\n"; - O.indent(IndentLevel1) << "++B;\n"; - } - - O.indent(IndentLevel) << "}\n"; - break; - case OptionType::SwitchList: - EmitForEachListElementCycleHeader(D, IndentLevel, O); - O.indent(IndentLevel1) << "vec.push_back(std::make_pair(pos, \"" - << Name << "\"));\n"; - O.indent(IndentLevel1) << "++B;\n"; - O.indent(IndentLevel) << "}\n"; - break; - case OptionType::Alias: - default: - throw "Aliases are not allowed in tool option descriptions!"; - } -} - -/// ActionHandlingCallbackBase - Base class of EmitActionHandlersCallback and -/// EmitPreprocessOptionsCallback. -struct ActionHandlingCallbackBase -{ - - void onErrorDag(const DagInit& d, - unsigned IndentLevel, raw_ostream& O) const - { - O.indent(IndentLevel) - << "PrintError(\"" - << (d.getNumArgs() >= 1 ? InitPtrToString(d.getArg(0)) : "Unknown error!") - << "\");\n"; - O.indent(IndentLevel) << "return 1;\n"; - } - - void onWarningDag(const DagInit& d, - unsigned IndentLevel, raw_ostream& O) const - { - CheckNumberOfArguments(d, 1); - O.indent(IndentLevel) << "llvm::errs() << \"" - << InitPtrToString(d.getArg(0)) << "\";\n"; - } - -}; - -/// EmitActionHandlersCallback - Emit code that handles actions. Used by -/// EmitGenerateActionMethod() as an argument to EmitCaseConstructHandler(). -class EmitActionHandlersCallback; - -typedef void (EmitActionHandlersCallback::* EmitActionHandlersCallbackHandler) -(const DagInit&, unsigned, raw_ostream&) const; - -class EmitActionHandlersCallback : - public ActionHandlingCallbackBase, - public HandlerTable<EmitActionHandlersCallbackHandler> -{ - typedef EmitActionHandlersCallbackHandler Handler; - - const OptionDescriptions& OptDescs; - - /// EmitHookInvocation - Common code for hook invocation from actions. Used by - /// onAppendCmd and onOutputSuffix. - void EmitHookInvocation(const std::string& Str, - const char* BlockOpen, const char* BlockClose, - unsigned IndentLevel, raw_ostream& O) const - { - StrVector Out; - TokenizeCmdLine(Str, Out); - - for (StrVector::const_iterator B = Out.begin(), E = Out.end(); - B != E; ++B) { - const std::string& cmd = *B; - - O.indent(IndentLevel) << BlockOpen; - - if (cmd.at(0) == '$') - B = SubstituteSpecialCommands(B, E, /* IsJoin = */ true, O); - else - O << '"' << cmd << '"'; - - O << BlockClose; - } - } - - void onAppendCmd (const DagInit& Dag, - unsigned IndentLevel, raw_ostream& O) const - { - CheckNumberOfArguments(Dag, 1); - this->EmitHookInvocation(InitPtrToString(Dag.getArg(0)), - "vec.push_back(std::make_pair(65536, ", "));\n", - IndentLevel, O); - } - - void onForward (const DagInit& Dag, - unsigned IndentLevel, raw_ostream& O) const - { - CheckNumberOfArguments(Dag, 1); - const std::string& Name = InitPtrToString(Dag.getArg(0)); - EmitForwardOptionPropertyHandlingCode(OptDescs.FindOption(Name), - IndentLevel, "", O); - } - - void onForwardAs (const DagInit& Dag, - unsigned IndentLevel, raw_ostream& O) const - { - CheckNumberOfArguments(Dag, 2); - const std::string& Name = InitPtrToString(Dag.getArg(0)); - const std::string& NewName = InitPtrToString(Dag.getArg(1)); - EmitForwardOptionPropertyHandlingCode(OptDescs.FindOption(Name), - IndentLevel, NewName, O); - } - - void onForwardValue (const DagInit& Dag, - unsigned IndentLevel, raw_ostream& O) const - { - CheckNumberOfArguments(Dag, 1); - const std::string& Name = InitPtrToString(Dag.getArg(0)); - const OptionDescription& D = OptDescs.FindParameterListOrParameter(Name); - - if (D.isSwitchList()) { - throw std::runtime_error - ("forward_value is not allowed with switch_list"); - } - - if (D.isParameter()) { - O.indent(IndentLevel) << "vec.push_back(std::make_pair(" - << D.GenVariableName() << ".getPosition(), " - << D.GenVariableName() << "));\n"; - } - else { - O.indent(IndentLevel) << "for (" << D.GenTypeDeclaration() - << "::iterator B = " << D.GenVariableName() - << ".begin(), \n"; - O.indent(IndentLevel + Indent1) << " E = " << D.GenVariableName() - << ".end(); B != E; ++B)\n"; - O.indent(IndentLevel) << "{\n"; - O.indent(IndentLevel + Indent1) - << "unsigned pos = " << D.GenVariableName() - << ".getPosition(B - " << D.GenVariableName() - << ".begin());\n"; - O.indent(IndentLevel + Indent1) - << "vec.push_back(std::make_pair(pos, *B));\n"; - O.indent(IndentLevel) << "}\n"; - } - } - - void onForwardTransformedValue (const DagInit& Dag, - unsigned IndentLevel, raw_ostream& O) const - { - CheckNumberOfArguments(Dag, 2); - const std::string& Name = InitPtrToString(Dag.getArg(0)); - const std::string& Hook = InitPtrToString(Dag.getArg(1)); - const OptionDescription& D = OptDescs.FindParameterListOrParameter(Name); - - O.indent(IndentLevel) << "vec.push_back(std::make_pair(" - << D.GenVariableName() << ".getPosition(" - << (D.isList() ? "0" : "") << "), " - << "hooks::" << Hook << "(" << D.GenVariableName() - << (D.isParameter() ? ".c_str()" : "") << ")));\n"; - } - - void onNoOutFile (const DagInit& Dag, - unsigned IndentLevel, raw_ostream& O) const - { - CheckNumberOfArguments(Dag, 0); - O.indent(IndentLevel) << "no_out_file = true;\n"; - } - - void onOutputSuffix (const DagInit& Dag, - unsigned IndentLevel, raw_ostream& O) const - { - CheckNumberOfArguments(Dag, 1); - this->EmitHookInvocation(InitPtrToString(Dag.getArg(0)), - "output_suffix = ", ";\n", IndentLevel, O); - } - - void onStopCompilation (const DagInit& Dag, - unsigned IndentLevel, raw_ostream& O) const - { - O.indent(IndentLevel) << "stop_compilation = true;\n"; - } - - - void onUnpackValues (const DagInit& Dag, - unsigned IndentLevel, raw_ostream& O) const - { - throw "'unpack_values' is deprecated. " - "Use 'comma_separated' + 'forward_value' instead!"; - } - - public: - - explicit EmitActionHandlersCallback(const OptionDescriptions& OD) - : OptDescs(OD) - { - if (!staticMembersInitialized_) { - AddHandler("error", &EmitActionHandlersCallback::onErrorDag); - AddHandler("warning", &EmitActionHandlersCallback::onWarningDag); - AddHandler("append_cmd", &EmitActionHandlersCallback::onAppendCmd); - AddHandler("forward", &EmitActionHandlersCallback::onForward); - AddHandler("forward_as", &EmitActionHandlersCallback::onForwardAs); - AddHandler("forward_value", &EmitActionHandlersCallback::onForwardValue); - AddHandler("forward_transformed_value", - &EmitActionHandlersCallback::onForwardTransformedValue); - AddHandler("no_out_file", - &EmitActionHandlersCallback::onNoOutFile); - AddHandler("output_suffix", &EmitActionHandlersCallback::onOutputSuffix); - AddHandler("stop_compilation", - &EmitActionHandlersCallback::onStopCompilation); - AddHandler("unpack_values", - &EmitActionHandlersCallback::onUnpackValues); - - - staticMembersInitialized_ = true; - } - } - - void operator()(const Init* I, - unsigned IndentLevel, raw_ostream& O) const - { - InvokeDagInitHandler(this, I, IndentLevel, O); - } -}; - -void EmitGenerateActionMethodHeader(const ToolDescription& D, - bool IsJoin, bool Naked, - raw_ostream& O) -{ - O.indent(Indent1) << "int GenerateAction(Action& Out,\n"; - - if (IsJoin) - O.indent(Indent2) << "const PathVector& inFiles,\n"; - else - O.indent(Indent2) << "const sys::Path& inFile,\n"; - - O.indent(Indent2) << "const bool HasChildren,\n"; - O.indent(Indent2) << "const llvm::sys::Path& TempDir,\n"; - O.indent(Indent2) << "const InputLanguagesSet& InLangs,\n"; - O.indent(Indent2) << "const LanguageMap& LangMap) const\n"; - O.indent(Indent1) << "{\n"; - - if (!Naked) { - O.indent(Indent2) << "std::string cmd;\n"; - O.indent(Indent2) << "std::string out_file;\n"; - O.indent(Indent2) - << "std::vector<std::pair<unsigned, std::string> > vec;\n"; - O.indent(Indent2) << "bool stop_compilation = !HasChildren;\n"; - O.indent(Indent2) << "bool no_out_file = false;\n"; - O.indent(Indent2) << "std::string output_suffix(\"" - << D.OutputSuffix << "\");\n"; - } -} - -// EmitGenerateActionMethod - Emit either a normal or a "join" version of the -// Tool::GenerateAction() method. -void EmitGenerateActionMethod (const ToolDescription& D, - const OptionDescriptions& OptDescs, - bool IsJoin, raw_ostream& O) { - - EmitGenerateActionMethodHeader(D, IsJoin, /* Naked = */ false, O); - - if (!D.CmdLine) - throw "Tool " + D.Name + " has no cmd_line property!"; - - // Process the 'command' property. - O << '\n'; - EmitCmdLineVecFill(D.CmdLine, D.Name, IsJoin, Indent2, O); - O << '\n'; - - // Process the 'actions' list of this tool. - if (D.Actions) - EmitCaseConstructHandler(D.Actions, Indent2, - EmitActionHandlersCallback(OptDescs), - false, OptDescs, O); - O << '\n'; - - // Input file (s) - if (!D.InFileOption.empty()) { - O.indent(Indent2) - << "vec.push_back(std::make_pair(InputFilenames.getPosition(0), \"" - << D.InFileOption << "\");\n"; - } - - if (IsJoin) { - O.indent(Indent2) - << "for (PathVector::const_iterator B = inFiles.begin(),\n"; - O.indent(Indent3) << "E = inFiles.end(); B != E; ++B)\n"; - O.indent(Indent2) << "{\n"; - O.indent(Indent3) << "vec.push_back(std::make_pair(" - << "InputFilenames.getPosition(B - inFiles.begin()), " - << "B->str()));\n"; - O.indent(Indent2) << "}\n"; - } - else { - O.indent(Indent2) << "vec.push_back(std::make_pair(" - << "InputFilenames.getPosition(0), inFile.str()));\n"; - } - - // Output file - O.indent(Indent2) << "if (!no_out_file) {\n"; - if (!D.OutFileOption.empty()) - O.indent(Indent3) << "vec.push_back(std::make_pair(65536, \"" - << D.OutFileOption << "\"));\n"; - - O.indent(Indent3) << "out_file = this->OutFilename(" - << (IsJoin ? "sys::Path(),\n" : "inFile,\n"); - O.indent(Indent4) << - "TempDir, stop_compilation, output_suffix.c_str()).str();\n\n"; - O.indent(Indent3) << "vec.push_back(std::make_pair(65536, out_file));\n"; - - O.indent(Indent2) << "}\n\n"; - - // Handle the Sink property. - std::string SinkOption("autogenerated::"); - SinkOption += SinkOptionName; - if (D.isSink()) { - O.indent(Indent2) << "if (!" << SinkOption << ".empty()) {\n"; - O.indent(Indent3) << "for (cl::list<std::string>::iterator B = " - << SinkOption << ".begin(), E = " << SinkOption - << ".end(); B != E; ++B)\n"; - O.indent(Indent4) << "vec.push_back(std::make_pair(" << SinkOption - << ".getPosition(B - " << SinkOption - << ".begin()), *B));\n"; - O.indent(Indent2) << "}\n"; - } - - O.indent(Indent2) << "Out.Construct(cmd, this->SortArgs(vec), " - << "stop_compilation, out_file);\n"; - O.indent(Indent2) << "return 0;\n"; - O.indent(Indent1) << "}\n\n"; -} - -/// EmitGenerateActionMethods - Emit two GenerateAction() methods for -/// a given Tool class. -void EmitGenerateActionMethods (const ToolDescription& ToolDesc, - const OptionDescriptions& OptDescs, - raw_ostream& O) { - if (!ToolDesc.isJoin()) { - EmitGenerateActionMethodHeader(ToolDesc, /* IsJoin = */ true, - /* Naked = */ true, O); - O.indent(Indent2) << "PrintError(\"" << ToolDesc.Name - << " is not a Join tool!\");\n"; - O.indent(Indent2) << "return -1;\n"; - O.indent(Indent1) << "}\n\n"; - } - else { - EmitGenerateActionMethod(ToolDesc, OptDescs, true, O); - } - - EmitGenerateActionMethod(ToolDesc, OptDescs, false, O); -} - -/// EmitInOutLanguageMethods - Emit the [Input,Output]Language() -/// methods for a given Tool class. -void EmitInOutLanguageMethods (const ToolDescription& D, raw_ostream& O) { - O.indent(Indent1) << "const char** InputLanguages() const {\n"; - O.indent(Indent2) << "return InputLanguages_;\n"; - O.indent(Indent1) << "}\n\n"; - - O.indent(Indent1) << "const char** OutputLanguages() const {\n"; - O.indent(Indent2) << "return OutputLanguages_;\n"; - O.indent(Indent1) << "}\n\n"; -} - -/// EmitNameMethod - Emit the Name() method for a given Tool class. -void EmitNameMethod (const ToolDescription& D, raw_ostream& O) { - O.indent(Indent1) << "const char* Name() const {\n"; - O.indent(Indent2) << "return \"" << D.Name << "\";\n"; - O.indent(Indent1) << "}\n\n"; -} - -/// EmitIsJoinMethod - Emit the IsJoin() method for a given Tool -/// class. -void EmitIsJoinMethod (const ToolDescription& D, raw_ostream& O) { - O.indent(Indent1) << "bool IsJoin() const {\n"; - if (D.isJoin()) - O.indent(Indent2) << "return true;\n"; - else - O.indent(Indent2) << "return false;\n"; - O.indent(Indent1) << "}\n\n"; -} - -/// EmitWorksOnEmptyCallback - Callback used by EmitWorksOnEmptyMethod in -/// conjunction with EmitCaseConstructHandler. -void EmitWorksOnEmptyCallback (const Init* Value, - unsigned IndentLevel, raw_ostream& O) { - CheckBooleanConstant(Value); - O.indent(IndentLevel) << "return " << Value->getAsString() << ";\n"; -} - -/// EmitWorksOnEmptyMethod - Emit the WorksOnEmpty() method for a given Tool -/// class. -void EmitWorksOnEmptyMethod (const ToolDescription& D, - const OptionDescriptions& OptDescs, - raw_ostream& O) -{ - O.indent(Indent1) << "bool WorksOnEmpty() const {\n"; - if (D.OnEmpty == 0) - O.indent(Indent2) << "return false;\n"; - else - EmitCaseConstructHandler(D.OnEmpty, Indent2, EmitWorksOnEmptyCallback, - /*EmitElseIf = */ true, OptDescs, O); - O.indent(Indent1) << "}\n\n"; -} - -/// EmitStrArray - Emit definition of a 'const char**' static member -/// variable. Helper used by EmitStaticMemberDefinitions(); -void EmitStrArray(const std::string& Name, const std::string& VarName, - const StrVector& StrVec, raw_ostream& O) { - O << "const char* " << Name << "::" << VarName << "[] = {"; - for (StrVector::const_iterator B = StrVec.begin(), E = StrVec.end(); - B != E; ++B) - O << '\"' << *B << "\", "; - O << "0};\n"; -} - -/// EmitStaticMemberDefinitions - Emit static member definitions for a -/// given Tool class. -void EmitStaticMemberDefinitions(const ToolDescription& D, raw_ostream& O) { - if (D.InLanguage.empty()) - throw "Tool " + D.Name + " has no 'in_language' property!"; - if (D.OutLanguage.empty()) - throw "Tool " + D.Name + " has no 'out_language' property!"; - - EmitStrArray(D.Name, "InputLanguages_", D.InLanguage, O); - EmitStrArray(D.Name, "OutputLanguages_", D.OutLanguage, O); - O << '\n'; -} - -/// EmitToolClassDefinition - Emit a Tool class definition. -void EmitToolClassDefinition (const ToolDescription& D, - const OptionDescriptions& OptDescs, - raw_ostream& O) { - if (D.Name == "root") - return; - - // Header - O << "class " << D.Name << " : public "; - if (D.isJoin()) - O << "JoinTool"; - else - O << "Tool"; - - O << " {\nprivate:\n"; - O.indent(Indent1) << "static const char* InputLanguages_[];\n"; - O.indent(Indent1) << "static const char* OutputLanguages_[];\n\n"; - - O << "public:\n"; - EmitNameMethod(D, O); - EmitInOutLanguageMethods(D, O); - EmitIsJoinMethod(D, O); - EmitWorksOnEmptyMethod(D, OptDescs, O); - EmitGenerateActionMethods(D, OptDescs, O); - - // Close class definition - O << "};\n"; - - EmitStaticMemberDefinitions(D, O); - -} - -/// EmitOptionDefinitions - Iterate over a list of option descriptions -/// and emit registration code. -void EmitOptionDefinitions (const OptionDescriptions& descs, - bool HasSink, raw_ostream& O) -{ - std::vector<OptionDescription> Aliases; - - // Emit static cl::Option variables. - for (OptionDescriptions::const_iterator B = descs.begin(), - E = descs.end(); B!=E; ++B) { - const OptionDescription& val = B->second; - - if (val.Type == OptionType::Alias) { - Aliases.push_back(val); - continue; - } - - O << val.GenTypeDeclaration() << ' ' - << val.GenPlainVariableName(); - - O << "(\"" << val.Name << "\"\n"; - - if (val.Type == OptionType::Prefix || val.Type == OptionType::PrefixList) - O << ", cl::Prefix"; - - if (val.isRequired()) { - if (val.isList() && !val.isMultiVal()) - O << ", cl::OneOrMore"; - else - O << ", cl::Required"; - } - - if (val.isOptional()) - O << ", cl::Optional"; - - if (val.isOneOrMore()) - O << ", cl::OneOrMore"; - - if (val.isZeroOrMore()) - O << ", cl::ZeroOrMore"; - - if (val.isReallyHidden()) - O << ", cl::ReallyHidden"; - else if (val.isHidden()) - O << ", cl::Hidden"; - - if (val.isCommaSeparated()) - O << ", cl::CommaSeparated"; - - if (val.MultiVal > 1) - O << ", cl::multi_val(" << val.MultiVal << ')'; - - if (val.InitVal) { - const std::string& str = val.InitVal->getAsString(); - O << ", cl::init(" << str << ')'; - } - - if (!val.Help.empty()) - O << ", cl::desc(\"" << val.Help << "\")"; - - O << ");\n\n"; - } - - // Emit the aliases (they should go after all the 'proper' options). - for (std::vector<OptionDescription>::const_iterator - B = Aliases.begin(), E = Aliases.end(); B != E; ++B) { - const OptionDescription& val = *B; - - O << val.GenTypeDeclaration() << ' ' - << val.GenPlainVariableName() - << "(\"" << val.Name << '\"'; - - const OptionDescription& D = descs.FindOption(val.Help); - O << ", cl::aliasopt(" << D.GenVariableName() << ")"; - - O << ", cl::desc(\"" << "An alias for -" + val.Help << "\"));\n"; - } - - // Emit the sink option. - if (HasSink) - O << "cl::list<std::string> " << SinkOptionName << "(cl::Sink);\n"; - - O << '\n'; -} - -/// EmitPreprocessOptionsCallback - Helper function passed to -/// EmitCaseConstructHandler() by EmitPreprocessOptions(). - -class EmitPreprocessOptionsCallback; - -typedef void -(EmitPreprocessOptionsCallback::* EmitPreprocessOptionsCallbackHandler) -(const DagInit&, unsigned, raw_ostream&) const; - -class EmitPreprocessOptionsCallback : - public ActionHandlingCallbackBase, - public HandlerTable<EmitPreprocessOptionsCallbackHandler> -{ - typedef EmitPreprocessOptionsCallbackHandler Handler; - typedef void - (EmitPreprocessOptionsCallback::* HandlerImpl) - (const Init*, unsigned, raw_ostream&) const; - - const OptionDescriptions& OptDescs_; - - void onEachArgument(const DagInit& d, HandlerImpl h, - unsigned IndentLevel, raw_ostream& O) const - { - CheckNumberOfArguments(d, 1); - - for (unsigned i = 0, NumArgs = d.getNumArgs(); i < NumArgs; ++i) { - ((this)->*(h))(d.getArg(i), IndentLevel, O); - } - } - - void onUnsetOptionImpl(const Init* I, - unsigned IndentLevel, raw_ostream& O) const - { - const std::string& OptName = InitPtrToString(I); - const OptionDescription& OptDesc = OptDescs_.FindOption(OptName); - - if (OptDesc.isSwitch()) { - O.indent(IndentLevel) << OptDesc.GenVariableName() << " = false;\n"; - } - else if (OptDesc.isParameter()) { - O.indent(IndentLevel) << OptDesc.GenVariableName() << " = \"\";\n"; - } - else if (OptDesc.isList()) { - O.indent(IndentLevel) << OptDesc.GenVariableName() << ".clear();\n"; - } - else { - throw "Can't apply 'unset_option' to alias option '" + OptName + "'!"; - } - } - - void onUnsetOption(const DagInit& d, - unsigned IndentLevel, raw_ostream& O) const - { - this->onEachArgument(d, &EmitPreprocessOptionsCallback::onUnsetOptionImpl, - IndentLevel, O); - } - - void onSetOptionImpl(const DagInit& D, - unsigned IndentLevel, raw_ostream& O) const { - CheckNumberOfArguments(D, 2); - - const std::string& OptName = InitPtrToString(D.getArg(0)); - const OptionDescription& OptDesc = OptDescs_.FindOption(OptName); - const Init* Value = D.getArg(1); - - if (OptDesc.isList()) { - const ListInit& List = InitPtrToList(Value); - - O.indent(IndentLevel) << OptDesc.GenVariableName() << ".clear();\n"; - for (ListInit::const_iterator B = List.begin(), E = List.end(); - B != E; ++B) { - const Init* CurElem = *B; - if (OptDesc.isSwitchList()) - CheckBooleanConstant(CurElem); - - O.indent(IndentLevel) - << OptDesc.GenVariableName() << ".push_back(\"" - << (OptDesc.isSwitchList() ? CurElem->getAsString() - : InitPtrToString(CurElem)) - << "\");\n"; - } - } - else if (OptDesc.isSwitch()) { - CheckBooleanConstant(Value); - O.indent(IndentLevel) << OptDesc.GenVariableName() - << " = " << Value->getAsString() << ";\n"; - } - else if (OptDesc.isParameter()) { - const std::string& Str = InitPtrToString(Value); - O.indent(IndentLevel) << OptDesc.GenVariableName() - << " = \"" << Str << "\";\n"; - } - else { - throw "Can't apply 'set_option' to alias option '" + OptName + "'!"; - } - } - - void onSetSwitch(const Init* I, - unsigned IndentLevel, raw_ostream& O) const { - const std::string& OptName = InitPtrToString(I); - const OptionDescription& OptDesc = OptDescs_.FindOption(OptName); - - if (OptDesc.isSwitch()) - O.indent(IndentLevel) << OptDesc.GenVariableName() << " = true;\n"; - else - throw "set_option: -" + OptName + " is not a switch option!"; - } - - void onSetOption(const DagInit& d, - unsigned IndentLevel, raw_ostream& O) const - { - CheckNumberOfArguments(d, 1); - - // 2-argument form: (set_option "A", true), (set_option "B", "C"), - // (set_option "D", ["E", "F"]) - if (d.getNumArgs() == 2) { - const OptionDescription& OptDesc = - OptDescs_.FindOption(InitPtrToString(d.getArg(0))); - const Init* Opt2 = d.getArg(1); - - if (!OptDesc.isSwitch() || typeid(*Opt2) != typeid(StringInit)) { - this->onSetOptionImpl(d, IndentLevel, O); - return; - } - } - - // Multiple argument form: (set_option "A"), (set_option "B", "C", "D") - this->onEachArgument(d, &EmitPreprocessOptionsCallback::onSetSwitch, - IndentLevel, O); - } - -public: - - EmitPreprocessOptionsCallback(const OptionDescriptions& OptDescs) - : OptDescs_(OptDescs) - { - if (!staticMembersInitialized_) { - AddHandler("error", &EmitPreprocessOptionsCallback::onErrorDag); - AddHandler("warning", &EmitPreprocessOptionsCallback::onWarningDag); - AddHandler("unset_option", &EmitPreprocessOptionsCallback::onUnsetOption); - AddHandler("set_option", &EmitPreprocessOptionsCallback::onSetOption); - - staticMembersInitialized_ = true; - } - } - - void operator()(const Init* I, - unsigned IndentLevel, raw_ostream& O) const - { - InvokeDagInitHandler(this, I, IndentLevel, O); - } - -}; - -/// EmitPreprocessOptions - Emit the PreprocessOptions() function. -void EmitPreprocessOptions (const RecordKeeper& Records, - const OptionDescriptions& OptDecs, raw_ostream& O) -{ - O << "int PreprocessOptions () {\n"; - - const RecordVector& OptionPreprocessors = - Records.getAllDerivedDefinitions("OptionPreprocessor"); - - for (RecordVector::const_iterator B = OptionPreprocessors.begin(), - E = OptionPreprocessors.end(); B!=E; ++B) { - DagInit* Case = (*B)->getValueAsDag("preprocessor"); - EmitCaseConstructHandler(Case, Indent1, - EmitPreprocessOptionsCallback(OptDecs), - false, OptDecs, O); - } - - O << '\n'; - O.indent(Indent1) << "return 0;\n"; - O << "}\n\n"; -} - -class DoEmitPopulateLanguageMap; -typedef void (DoEmitPopulateLanguageMap::* DoEmitPopulateLanguageMapHandler) -(const DagInit& D); - -class DoEmitPopulateLanguageMap -: public HandlerTable<DoEmitPopulateLanguageMapHandler> -{ -private: - raw_ostream& O_; - -public: - - explicit DoEmitPopulateLanguageMap (raw_ostream& O) : O_(O) { - if (!staticMembersInitialized_) { - AddHandler("lang_to_suffixes", - &DoEmitPopulateLanguageMap::onLangToSuffixes); - - staticMembersInitialized_ = true; - } - } - - void operator() (Init* I) { - InvokeDagInitHandler(this, I); - } - -private: - - void onLangToSuffixes (const DagInit& d) { - CheckNumberOfArguments(d, 2); - - const std::string& Lang = InitPtrToString(d.getArg(0)); - Init* Suffixes = d.getArg(1); - - // Second argument to lang_to_suffixes is either a single string... - if (typeid(*Suffixes) == typeid(StringInit)) { - O_.indent(Indent1) << "langMap[\"" << InitPtrToString(Suffixes) - << "\"] = \"" << Lang << "\";\n"; - } - // ...or a list of strings. - else { - const ListInit& Lst = InitPtrToList(Suffixes); - assert(Lst.size() != 0); - for (ListInit::const_iterator B = Lst.begin(), E = Lst.end(); - B != E; ++B) { - O_.indent(Indent1) << "langMap[\"" << InitPtrToString(*B) - << "\"] = \"" << Lang << "\";\n"; - } - } - } - -}; - -/// EmitPopulateLanguageMap - Emit the PopulateLanguageMap() function. -void EmitPopulateLanguageMap (const RecordKeeper& Records, raw_ostream& O) -{ - O << "int PopulateLanguageMap (LanguageMap& langMap) {\n"; - - // For each LanguageMap: - const RecordVector& LangMaps = - Records.getAllDerivedDefinitions("LanguageMap"); - - // Call DoEmitPopulateLanguageMap. - for (RecordVector::const_iterator B = LangMaps.begin(), - E = LangMaps.end(); B!=E; ++B) { - ListInit* LangMap = (*B)->getValueAsListInit("map"); - std::for_each(LangMap->begin(), LangMap->end(), - DoEmitPopulateLanguageMap(O)); - } - - O << '\n'; - O.indent(Indent1) << "return 0;\n"; - O << "}\n\n"; -} - -/// EmitEdgePropertyHandlerCallback - Emits code that handles edge -/// properties. Helper function passed to EmitCaseConstructHandler() by -/// EmitEdgeClass(). -void EmitEdgePropertyHandlerCallback (const Init* i, unsigned IndentLevel, - raw_ostream& O) { - const DagInit& d = InitPtrToDag(i); - const std::string& OpName = GetOperatorName(d); - - if (OpName == "inc_weight") { - O.indent(IndentLevel) << "ret += "; - } - else if (OpName == "error") { - CheckNumberOfArguments(d, 1); - O.indent(IndentLevel) << "PrintError(\"" - << InitPtrToString(d.getArg(0)) - << "\");\n"; - O.indent(IndentLevel) << "return -1;\n"; - return; - } - else { - throw "Unknown operator in edge properties list: '" + OpName + "'!" - "\nOnly 'inc_weight', 'dec_weight' and 'error' are allowed."; - } - - if (d.getNumArgs() > 0) - O << InitPtrToInt(d.getArg(0)) << ";\n"; - else - O << "2;\n"; - -} - -/// EmitEdgeClass - Emit a single Edge# class. -void EmitEdgeClass (unsigned N, const std::string& Target, - const DagInit& Case, const OptionDescriptions& OptDescs, - raw_ostream& O) { - - // Class constructor. - O << "class Edge" << N << ": public Edge {\n" - << "public:\n"; - O.indent(Indent1) << "Edge" << N << "() : Edge(\"" << Target - << "\") {}\n\n"; - - // Function Weight(). - O.indent(Indent1) - << "int Weight(const InputLanguagesSet& InLangs) const {\n"; - O.indent(Indent2) << "unsigned ret = 0;\n"; - - // Handle the 'case' construct. - EmitCaseConstructHandler(&Case, Indent2, EmitEdgePropertyHandlerCallback, - false, OptDescs, O); - - O.indent(Indent2) << "return ret;\n"; - O.indent(Indent1) << "}\n\n};\n\n"; -} - -/// EmitEdgeClasses - Emit Edge* classes that represent graph edges. -void EmitEdgeClasses (const DagVector& EdgeVector, - const OptionDescriptions& OptDescs, - raw_ostream& O) { - int i = 0; - for (DagVector::const_iterator B = EdgeVector.begin(), - E = EdgeVector.end(); B != E; ++B) { - const DagInit& Edge = **B; - const std::string& Name = GetOperatorName(Edge); - - if (Name == "optional_edge") { - assert(IsOptionalEdge(Edge)); - const std::string& NodeB = InitPtrToString(Edge.getArg(1)); - - const DagInit& Weight = InitPtrToDag(Edge.getArg(2)); - EmitEdgeClass(i, NodeB, Weight, OptDescs, O); - } - else if (Name != "edge") { - throw "Unknown edge class: '" + Name + "'!"; - } - - ++i; - } -} - -/// EmitPopulateCompilationGraph - Emit the PopulateCompilationGraph() function. -void EmitPopulateCompilationGraph (const DagVector& EdgeVector, - const ToolDescriptions& ToolDescs, - raw_ostream& O) -{ - O << "int PopulateCompilationGraph (CompilationGraph& G) {\n"; - - for (ToolDescriptions::const_iterator B = ToolDescs.begin(), - E = ToolDescs.end(); B != E; ++B) - O.indent(Indent1) << "G.insertNode(new " << (*B)->Name << "());\n"; - - O << '\n'; - - // Insert edges. - - int i = 0; - for (DagVector::const_iterator B = EdgeVector.begin(), - E = EdgeVector.end(); B != E; ++B) { - const DagInit& Edge = **B; - const std::string& NodeA = InitPtrToString(Edge.getArg(0)); - const std::string& NodeB = InitPtrToString(Edge.getArg(1)); - - O.indent(Indent1) << "if (int ret = G.insertEdge(\"" << NodeA << "\", "; - - if (IsOptionalEdge(Edge)) - O << "new Edge" << i << "()"; - else - O << "new SimpleEdge(\"" << NodeB << "\")"; - - O << "))\n"; - O.indent(Indent2) << "return ret;\n"; - - ++i; - } - - O << '\n'; - O.indent(Indent1) << "return 0;\n"; - O << "}\n\n"; -} - -/// HookInfo - Information about the hook type and number of arguments. -struct HookInfo { - - // A hook can either have a single parameter of type std::vector<std::string>, - // or NumArgs parameters of type const char*. - enum HookType { ListHook, ArgHook }; - - HookType Type; - unsigned NumArgs; - - HookInfo() : Type(ArgHook), NumArgs(1) - {} - - HookInfo(HookType T) : Type(T), NumArgs(1) - {} - - HookInfo(unsigned N) : Type(ArgHook), NumArgs(N) - {} -}; - -typedef llvm::StringMap<HookInfo> HookInfoMap; - -/// ExtractHookNames - Extract the hook names from all instances of -/// $CALL(HookName) in the provided command line string/action. Helper -/// function used by FillInHookNames(). -class ExtractHookNames { - HookInfoMap& HookNames_; - const OptionDescriptions& OptDescs_; -public: - ExtractHookNames(HookInfoMap& HookNames, const OptionDescriptions& OptDescs) - : HookNames_(HookNames), OptDescs_(OptDescs) - {} - - void onAction (const DagInit& Dag) { - const std::string& Name = GetOperatorName(Dag); - - if (Name == "forward_transformed_value") { - CheckNumberOfArguments(Dag, 2); - const std::string& OptName = InitPtrToString(Dag.getArg(0)); - const std::string& HookName = InitPtrToString(Dag.getArg(1)); - const OptionDescription& D = - OptDescs_.FindParameterListOrParameter(OptName); - - HookNames_[HookName] = HookInfo(D.isList() ? HookInfo::ListHook - : HookInfo::ArgHook); - } - else if (Name == "append_cmd" || Name == "output_suffix") { - CheckNumberOfArguments(Dag, 1); - this->onCmdLine(InitPtrToString(Dag.getArg(0))); - } - } - - void onCmdLine(const std::string& Cmd) { - StrVector cmds; - TokenizeCmdLine(Cmd, cmds); - - for (StrVector::const_iterator B = cmds.begin(), E = cmds.end(); - B != E; ++B) { - const std::string& cmd = *B; - - if (cmd == "$CALL") { - unsigned NumArgs = 0; - CheckedIncrement(B, E, "Syntax error in $CALL invocation!"); - const std::string& HookName = *B; - - if (HookName.at(0) == ')') - throw "$CALL invoked with no arguments!"; - - while (++B != E && B->at(0) != ')') { - ++NumArgs; - } - - HookInfoMap::const_iterator H = HookNames_.find(HookName); - - if (H != HookNames_.end() && H->second.NumArgs != NumArgs && - H->second.Type != HookInfo::ArgHook) - throw "Overloading of hooks is not allowed. Overloaded hook: " - + HookName; - else - HookNames_[HookName] = HookInfo(NumArgs); - } - } - } - - void operator()(const Init* Arg) { - - // We're invoked on an action (either a dag or a dag list). - if (typeid(*Arg) == typeid(DagInit)) { - const DagInit& Dag = InitPtrToDag(Arg); - this->onAction(Dag); - return; - } - else if (typeid(*Arg) == typeid(ListInit)) { - const ListInit& List = InitPtrToList(Arg); - for (ListInit::const_iterator B = List.begin(), E = List.end(); B != E; - ++B) { - const DagInit& Dag = InitPtrToDag(*B); - this->onAction(Dag); - } - return; - } - - // We're invoked on a command line string. - this->onCmdLine(InitPtrToString(Arg)); - } - - void operator()(const Init* Statement, unsigned) { - this->operator()(Statement); - } -}; - -/// FillInHookNames - Actually extract the hook names from all command -/// line strings. Helper function used by EmitHookDeclarations(). -void FillInHookNames(const ToolDescriptions& ToolDescs, - const OptionDescriptions& OptDescs, - HookInfoMap& HookNames) -{ - // For all tool descriptions: - for (ToolDescriptions::const_iterator B = ToolDescs.begin(), - E = ToolDescs.end(); B != E; ++B) { - const ToolDescription& D = *(*B); - - // Look for 'forward_transformed_value' in 'actions'. - if (D.Actions) - WalkCase(D.Actions, Id(), ExtractHookNames(HookNames, OptDescs)); - - // Look for hook invocations in 'cmd_line'. - if (!D.CmdLine) - continue; - if (dynamic_cast<StringInit*>(D.CmdLine)) - // This is a string. - ExtractHookNames(HookNames, OptDescs).operator()(D.CmdLine); - else - // This is a 'case' construct. - WalkCase(D.CmdLine, Id(), ExtractHookNames(HookNames, OptDescs)); - } -} - -/// EmitHookDeclarations - Parse CmdLine fields of all the tool -/// property records and emit hook function declaration for each -/// instance of $CALL(HookName). -void EmitHookDeclarations(const ToolDescriptions& ToolDescs, - const OptionDescriptions& OptDescs, raw_ostream& O) { - HookInfoMap HookNames; - - FillInHookNames(ToolDescs, OptDescs, HookNames); - if (HookNames.empty()) - return; - - for (HookInfoMap::const_iterator B = HookNames.begin(), - E = HookNames.end(); B != E; ++B) { - StringRef HookName = B->first(); - const HookInfo &Info = B->second; - - O.indent(Indent1) << "std::string " << HookName << "("; - - if (Info.Type == HookInfo::ArgHook) { - for (unsigned i = 0, j = Info.NumArgs; i < j; ++i) { - O << "const char* Arg" << i << (i+1 == j ? "" : ", "); - } - } - else { - O << "const std::vector<std::string>& Arg"; - } - - O <<");\n"; - } -} - -/// EmitIncludes - Emit necessary #include directives and some -/// additional declarations. -void EmitIncludes(raw_ostream& O) { - O << "#include \"llvm/CompilerDriver/BuiltinOptions.h\"\n" - << "#include \"llvm/CompilerDriver/CompilationGraph.h\"\n" - << "#include \"llvm/CompilerDriver/Error.h\"\n" - << "#include \"llvm/CompilerDriver/Tool.h\"\n\n" - - << "#include \"llvm/Support/CommandLine.h\"\n" - << "#include \"llvm/Support/raw_ostream.h\"\n\n" - - << "#include <algorithm>\n" - << "#include <cstdlib>\n" - << "#include <iterator>\n" - << "#include <stdexcept>\n\n" - - << "using namespace llvm;\n" - << "using namespace llvmc;\n\n" - - << "inline const char* checkCString(const char* s)\n" - << "{ return s == NULL ? \"\" : s; }\n\n"; -} - - -/// DriverData - Holds all information about the driver. -struct DriverData { - OptionDescriptions OptDescs; - ToolDescriptions ToolDescs; - DagVector Edges; - bool HasSink; -}; - -/// HasSink - Go through the list of tool descriptions and check if -/// there are any with the 'sink' property set. -bool HasSink(const ToolDescriptions& ToolDescs) { - for (ToolDescriptions::const_iterator B = ToolDescs.begin(), - E = ToolDescs.end(); B != E; ++B) - if ((*B)->isSink()) - return true; - - return false; -} - -/// CollectDriverData - Collect compilation graph edges, tool properties and -/// option properties from the parse tree. -void CollectDriverData (const RecordKeeper& Records, DriverData& Data) { - // Collect option properties. - const RecordVector& OptionLists = - Records.getAllDerivedDefinitions("OptionList"); - CollectOptionDescriptions(OptionLists, Data.OptDescs); - - // Collect tool properties. - const RecordVector& Tools = Records.getAllDerivedDefinitions("Tool"); - CollectToolDescriptions(Tools, Data.ToolDescs); - Data.HasSink = HasSink(Data.ToolDescs); - - // Collect compilation graph edges. - const RecordVector& CompilationGraphs = - Records.getAllDerivedDefinitions("CompilationGraph"); - FillInEdgeVector(CompilationGraphs, Data.Edges); -} - -/// CheckDriverData - Perform some sanity checks on the collected data. -void CheckDriverData(DriverData& Data) { - // Filter out all tools not mentioned in the compilation graph. - FilterNotInGraph(Data.Edges, Data.ToolDescs); - - // Typecheck the compilation graph. - // TODO: use a genuine graph representation instead of a vector and check for - // multiple edges. - TypecheckGraph(Data.Edges, Data.ToolDescs); - - // Check that there are no options without side effects (specified - // only in the OptionList). - CheckForSuperfluousOptions(Data.Edges, Data.ToolDescs, Data.OptDescs); -} - -void EmitDriverCode(const DriverData& Data, - raw_ostream& O, RecordKeeper &Records) { - // Emit file header. - EmitIncludes(O); - - // Emit global option registration code. - O << "namespace llvmc {\n" - << "namespace autogenerated {\n\n"; - EmitOptionDefinitions(Data.OptDescs, Data.HasSink, O); - O << "} // End namespace autogenerated.\n" - << "} // End namespace llvmc.\n\n"; - - // Emit hook declarations. - O << "namespace hooks {\n"; - EmitHookDeclarations(Data.ToolDescs, Data.OptDescs, O); - O << "} // End namespace hooks.\n\n"; - - O << "namespace {\n\n"; - O << "using namespace llvmc::autogenerated;\n\n"; - - // Emit Tool classes. - for (ToolDescriptions::const_iterator B = Data.ToolDescs.begin(), - E = Data.ToolDescs.end(); B!=E; ++B) - EmitToolClassDefinition(*(*B), Data.OptDescs, O); - - // Emit Edge# classes. - EmitEdgeClasses(Data.Edges, Data.OptDescs, O); - - O << "} // End anonymous namespace.\n\n"; - - O << "namespace llvmc {\n"; - O << "namespace autogenerated {\n\n"; - - // Emit PreprocessOptions() function. - EmitPreprocessOptions(Records, Data.OptDescs, O); - - // Emit PopulateLanguageMap() function - // (language map maps from file extensions to language names). - EmitPopulateLanguageMap(Records, O); - - // Emit PopulateCompilationGraph() function. - EmitPopulateCompilationGraph(Data.Edges, Data.ToolDescs, O); - - O << "} // End namespace autogenerated.\n"; - O << "} // End namespace llvmc.\n\n"; - - // EOF -} - - -// End of anonymous namespace -} - -/// run - The back-end entry point. -void LLVMCConfigurationEmitter::run (raw_ostream &O) { - try { - DriverData Data; - - CollectDriverData(Records, Data); - CheckDriverData(Data); - - this->EmitSourceFileHeader("llvmc-based driver: auto-generated code", O); - EmitDriverCode(Data, O, Records); - - } catch (std::exception& Error) { - throw Error.what() + std::string(" - usually this means a syntax error."); - } -} diff --git a/utils/TableGen/LLVMCConfigurationEmitter.h b/utils/TableGen/LLVMCConfigurationEmitter.h deleted file mode 100644 index 0f2ff37..0000000 --- a/utils/TableGen/LLVMCConfigurationEmitter.h +++ /dev/null @@ -1,34 +0,0 @@ -//===- LLVMCConfigurationEmitter.cpp - Generate LLVMCC config ---*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open -// Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This tablegen backend is responsible for emitting LLVMCC configuration code. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_UTILS_TABLEGEN_LLVMCCONF_EMITTER_H -#define LLVM_UTILS_TABLEGEN_LLVMCCONF_EMITTER_H - -#include "TableGenBackend.h" - -namespace llvm { - - /// LLVMCConfigurationEmitter - TableGen backend that generates - /// configuration code for LLVMC. - class LLVMCConfigurationEmitter : public TableGenBackend { - RecordKeeper &Records; - public: - explicit LLVMCConfigurationEmitter(RecordKeeper &records) : - Records(records) {} - - // run - Output the asmwriter, returning true on failure. - void run(raw_ostream &o); - }; -} - -#endif //LLVM_UTILS_TABLEGEN_LLVMCCONF_EMITTER_H diff --git a/utils/TableGen/Makefile b/utils/TableGen/Makefile index c01b660..0c4619d 100644 --- a/utils/TableGen/Makefile +++ b/utils/TableGen/Makefile @@ -8,8 +8,8 @@ ##===----------------------------------------------------------------------===## LEVEL = ../.. -TOOLNAME = tblgen -USEDLIBS = LLVMSupport.a +TOOLNAME = llvm-tblgen +USEDLIBS = LLVMTableGen.a LLVMSupport.a REQUIRES_EH := 1 REQUIRES_RTTI := 1 diff --git a/utils/TableGen/NeonEmitter.cpp b/utils/TableGen/NeonEmitter.cpp deleted file mode 100644 index ef977443..0000000 --- a/utils/TableGen/NeonEmitter.cpp +++ /dev/null @@ -1,1558 +0,0 @@ -//===- NeonEmitter.cpp - Generate arm_neon.h for use with clang -*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This tablegen backend is responsible for emitting arm_neon.h, which includes -// a declaration and definition of each function specified by the ARM NEON -// compiler interface. See ARM document DUI0348B. -// -// Each NEON instruction is implemented in terms of 1 or more functions which -// are suffixed with the element type of the input vectors. Functions may be -// implemented in terms of generic vector operations such as +, *, -, etc. or -// by calling a __builtin_-prefixed function which will be handled by clang's -// CodeGen library. -// -// Additional validation code can be generated by this file when runHeader() is -// called, rather than the normal run() entry point. A complete set of tests -// for Neon intrinsics can be generated by calling the runTests() entry point. -// -//===----------------------------------------------------------------------===// - -#include "NeonEmitter.h" -#include "Error.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/StringExtras.h" -#include <string> - -using namespace llvm; - -/// ParseTypes - break down a string such as "fQf" into a vector of StringRefs, -/// which each StringRef representing a single type declared in the string. -/// for "fQf" we would end up with 2 StringRefs, "f", and "Qf", representing -/// 2xfloat and 4xfloat respectively. -static void ParseTypes(Record *r, std::string &s, - SmallVectorImpl<StringRef> &TV) { - const char *data = s.data(); - int len = 0; - - for (unsigned i = 0, e = s.size(); i != e; ++i, ++len) { - if (data[len] == 'P' || data[len] == 'Q' || data[len] == 'U') - continue; - - switch (data[len]) { - case 'c': - case 's': - case 'i': - case 'l': - case 'h': - case 'f': - break; - default: - throw TGError(r->getLoc(), - "Unexpected letter: " + std::string(data + len, 1)); - break; - } - TV.push_back(StringRef(data, len + 1)); - data += len + 1; - len = -1; - } -} - -/// Widen - Convert a type code into the next wider type. char -> short, -/// short -> int, etc. -static char Widen(const char t) { - switch (t) { - case 'c': - return 's'; - case 's': - return 'i'; - case 'i': - return 'l'; - case 'h': - return 'f'; - default: throw "unhandled type in widen!"; - } - return '\0'; -} - -/// Narrow - Convert a type code into the next smaller type. short -> char, -/// float -> half float, etc. -static char Narrow(const char t) { - switch (t) { - case 's': - return 'c'; - case 'i': - return 's'; - case 'l': - return 'i'; - case 'f': - return 'h'; - default: throw "unhandled type in narrow!"; - } - return '\0'; -} - -/// For a particular StringRef, return the base type code, and whether it has -/// the quad-vector, polynomial, or unsigned modifiers set. -static char ClassifyType(StringRef ty, bool &quad, bool &poly, bool &usgn) { - unsigned off = 0; - - // remember quad. - if (ty[off] == 'Q') { - quad = true; - ++off; - } - - // remember poly. - if (ty[off] == 'P') { - poly = true; - ++off; - } - - // remember unsigned. - if (ty[off] == 'U') { - usgn = true; - ++off; - } - - // base type to get the type string for. - return ty[off]; -} - -/// ModType - Transform a type code and its modifiers based on a mod code. The -/// mod code definitions may be found at the top of arm_neon.td. -static char ModType(const char mod, char type, bool &quad, bool &poly, - bool &usgn, bool &scal, bool &cnst, bool &pntr) { - switch (mod) { - case 't': - if (poly) { - poly = false; - usgn = true; - } - break; - case 'u': - usgn = true; - poly = false; - if (type == 'f') - type = 'i'; - break; - case 'x': - usgn = false; - poly = false; - if (type == 'f') - type = 'i'; - break; - case 'f': - if (type == 'h') - quad = true; - type = 'f'; - usgn = false; - break; - case 'g': - quad = false; - break; - case 'w': - type = Widen(type); - quad = true; - break; - case 'n': - type = Widen(type); - break; - case 'i': - type = 'i'; - scal = true; - break; - case 'l': - type = 'l'; - scal = true; - usgn = true; - break; - case 's': - case 'a': - scal = true; - break; - case 'k': - quad = true; - break; - case 'c': - cnst = true; - case 'p': - pntr = true; - scal = true; - break; - case 'h': - type = Narrow(type); - if (type == 'h') - quad = false; - break; - case 'e': - type = Narrow(type); - usgn = true; - break; - default: - break; - } - return type; -} - -/// TypeString - for a modifier and type, generate the name of the typedef for -/// that type. QUc -> uint8x8_t. -static std::string TypeString(const char mod, StringRef typestr) { - bool quad = false; - bool poly = false; - bool usgn = false; - bool scal = false; - bool cnst = false; - bool pntr = false; - - if (mod == 'v') - return "void"; - if (mod == 'i') - return "int"; - - // base type to get the type string for. - char type = ClassifyType(typestr, quad, poly, usgn); - - // Based on the modifying character, change the type and width if necessary. - type = ModType(mod, type, quad, poly, usgn, scal, cnst, pntr); - - SmallString<128> s; - - if (usgn) - s.push_back('u'); - - switch (type) { - case 'c': - s += poly ? "poly8" : "int8"; - if (scal) - break; - s += quad ? "x16" : "x8"; - break; - case 's': - s += poly ? "poly16" : "int16"; - if (scal) - break; - s += quad ? "x8" : "x4"; - break; - case 'i': - s += "int32"; - if (scal) - break; - s += quad ? "x4" : "x2"; - break; - case 'l': - s += "int64"; - if (scal) - break; - s += quad ? "x2" : "x1"; - break; - case 'h': - s += "float16"; - if (scal) - break; - s += quad ? "x8" : "x4"; - break; - case 'f': - s += "float32"; - if (scal) - break; - s += quad ? "x4" : "x2"; - break; - default: - throw "unhandled type!"; - break; - } - - if (mod == '2') - s += "x2"; - if (mod == '3') - s += "x3"; - if (mod == '4') - s += "x4"; - - // Append _t, finishing the type string typedef type. - s += "_t"; - - if (cnst) - s += " const"; - - if (pntr) - s += " *"; - - return s.str(); -} - -/// BuiltinTypeString - for a modifier and type, generate the clang -/// BuiltinsARM.def prototype code for the function. See the top of clang's -/// Builtins.def for a description of the type strings. -static std::string BuiltinTypeString(const char mod, StringRef typestr, - ClassKind ck, bool ret) { - bool quad = false; - bool poly = false; - bool usgn = false; - bool scal = false; - bool cnst = false; - bool pntr = false; - - if (mod == 'v') - return "v"; // void - if (mod == 'i') - return "i"; // int - - // base type to get the type string for. - char type = ClassifyType(typestr, quad, poly, usgn); - - // Based on the modifying character, change the type and width if necessary. - type = ModType(mod, type, quad, poly, usgn, scal, cnst, pntr); - - // All pointers are void* pointers. Change type to 'v' now. - if (pntr) { - usgn = false; - poly = false; - type = 'v'; - } - // Treat half-float ('h') types as unsigned short ('s') types. - if (type == 'h') { - type = 's'; - usgn = true; - } - usgn = usgn | poly | ((ck == ClassI || ck == ClassW) && scal && type != 'f'); - - if (scal) { - SmallString<128> s; - - if (usgn) - s.push_back('U'); - else if (type == 'c') - s.push_back('S'); // make chars explicitly signed - - if (type == 'l') // 64-bit long - s += "LLi"; - else - s.push_back(type); - - if (cnst) - s.push_back('C'); - if (pntr) - s.push_back('*'); - return s.str(); - } - - // Since the return value must be one type, return a vector type of the - // appropriate width which we will bitcast. An exception is made for - // returning structs of 2, 3, or 4 vectors which are returned in a sret-like - // fashion, storing them to a pointer arg. - if (ret) { - if (mod >= '2' && mod <= '4') - return "vv*"; // void result with void* first argument - if (mod == 'f' || (ck != ClassB && type == 'f')) - return quad ? "V4f" : "V2f"; - if (ck != ClassB && type == 's') - return quad ? "V8s" : "V4s"; - if (ck != ClassB && type == 'i') - return quad ? "V4i" : "V2i"; - if (ck != ClassB && type == 'l') - return quad ? "V2LLi" : "V1LLi"; - - return quad ? "V16Sc" : "V8Sc"; - } - - // Non-return array types are passed as individual vectors. - if (mod == '2') - return quad ? "V16ScV16Sc" : "V8ScV8Sc"; - if (mod == '3') - return quad ? "V16ScV16ScV16Sc" : "V8ScV8ScV8Sc"; - if (mod == '4') - return quad ? "V16ScV16ScV16ScV16Sc" : "V8ScV8ScV8ScV8Sc"; - - if (mod == 'f' || (ck != ClassB && type == 'f')) - return quad ? "V4f" : "V2f"; - if (ck != ClassB && type == 's') - return quad ? "V8s" : "V4s"; - if (ck != ClassB && type == 'i') - return quad ? "V4i" : "V2i"; - if (ck != ClassB && type == 'l') - return quad ? "V2LLi" : "V1LLi"; - - return quad ? "V16Sc" : "V8Sc"; -} - -/// MangleName - Append a type or width suffix to a base neon function name, -/// and insert a 'q' in the appropriate location if the operation works on -/// 128b rather than 64b. E.g. turn "vst2_lane" into "vst2q_lane_f32", etc. -static std::string MangleName(const std::string &name, StringRef typestr, - ClassKind ck) { - if (name == "vcvt_f32_f16") - return name; - - bool quad = false; - bool poly = false; - bool usgn = false; - char type = ClassifyType(typestr, quad, poly, usgn); - - std::string s = name; - - switch (type) { - case 'c': - switch (ck) { - case ClassS: s += poly ? "_p8" : usgn ? "_u8" : "_s8"; break; - case ClassI: s += "_i8"; break; - case ClassW: s += "_8"; break; - default: break; - } - break; - case 's': - switch (ck) { - case ClassS: s += poly ? "_p16" : usgn ? "_u16" : "_s16"; break; - case ClassI: s += "_i16"; break; - case ClassW: s += "_16"; break; - default: break; - } - break; - case 'i': - switch (ck) { - case ClassS: s += usgn ? "_u32" : "_s32"; break; - case ClassI: s += "_i32"; break; - case ClassW: s += "_32"; break; - default: break; - } - break; - case 'l': - switch (ck) { - case ClassS: s += usgn ? "_u64" : "_s64"; break; - case ClassI: s += "_i64"; break; - case ClassW: s += "_64"; break; - default: break; - } - break; - case 'h': - switch (ck) { - case ClassS: - case ClassI: s += "_f16"; break; - case ClassW: s += "_16"; break; - default: break; - } - break; - case 'f': - switch (ck) { - case ClassS: - case ClassI: s += "_f32"; break; - case ClassW: s += "_32"; break; - default: break; - } - break; - default: - throw "unhandled type!"; - break; - } - if (ck == ClassB) - s += "_v"; - - // Insert a 'q' before the first '_' character so that it ends up before - // _lane or _n on vector-scalar operations. - if (quad) { - size_t pos = s.find('_'); - s = s.insert(pos, "q"); - } - return s; -} - -/// UseMacro - Examine the prototype string to determine if the intrinsic -/// should be defined as a preprocessor macro instead of an inline function. -static bool UseMacro(const std::string &proto) { - // If this builtin takes an immediate argument, we need to #define it rather - // than use a standard declaration, so that SemaChecking can range check - // the immediate passed by the user. - if (proto.find('i') != std::string::npos) - return true; - - // Pointer arguments need to use macros to avoid hiding aligned attributes - // from the pointer type. - if (proto.find('p') != std::string::npos || - proto.find('c') != std::string::npos) - return true; - - return false; -} - -/// MacroArgUsedDirectly - Return true if argument i for an intrinsic that is -/// defined as a macro should be accessed directly instead of being first -/// assigned to a local temporary. -static bool MacroArgUsedDirectly(const std::string &proto, unsigned i) { - return (proto[i] == 'i' || proto[i] == 'p' || proto[i] == 'c'); -} - -// Generate the string "(argtype a, argtype b, ...)" -static std::string GenArgs(const std::string &proto, StringRef typestr) { - bool define = UseMacro(proto); - char arg = 'a'; - - std::string s; - s += "("; - - for (unsigned i = 1, e = proto.size(); i != e; ++i, ++arg) { - if (define) { - // Some macro arguments are used directly instead of being assigned - // to local temporaries; prepend an underscore prefix to make their - // names consistent with the local temporaries. - if (MacroArgUsedDirectly(proto, i)) - s += "__"; - } else { - s += TypeString(proto[i], typestr) + " __"; - } - s.push_back(arg); - if ((i + 1) < e) - s += ", "; - } - - s += ")"; - return s; -} - -// Macro arguments are not type-checked like inline function arguments, so -// assign them to local temporaries to get the right type checking. -static std::string GenMacroLocals(const std::string &proto, StringRef typestr) { - char arg = 'a'; - std::string s; - bool generatedLocal = false; - - for (unsigned i = 1, e = proto.size(); i != e; ++i, ++arg) { - // Do not create a temporary for an immediate argument. - // That would defeat the whole point of using a macro! - if (proto[i] == 'i') - continue; - generatedLocal = true; - - // For other (non-immediate) arguments that are used directly, a local - // temporary is still needed to get the correct type checking, even though - // that temporary is not used for anything. - if (MacroArgUsedDirectly(proto, i)) { - s += TypeString(proto[i], typestr) + " __"; - s.push_back(arg); - s += "_ = (__"; - s.push_back(arg); - s += "); (void)__"; - s.push_back(arg); - s += "_; "; - continue; - } - - s += TypeString(proto[i], typestr) + " __"; - s.push_back(arg); - s += " = ("; - s.push_back(arg); - s += "); "; - } - - if (generatedLocal) - s += "\\\n "; - return s; -} - -// Use the vmovl builtin to sign-extend or zero-extend a vector. -static std::string Extend(StringRef typestr, const std::string &a) { - std::string s; - s = MangleName("vmovl", typestr, ClassS); - s += "(" + a + ")"; - return s; -} - -static std::string Duplicate(unsigned nElts, StringRef typestr, - const std::string &a) { - std::string s; - - s = "(" + TypeString('d', typestr) + "){ "; - for (unsigned i = 0; i != nElts; ++i) { - s += a; - if ((i + 1) < nElts) - s += ", "; - } - s += " }"; - - return s; -} - -static std::string SplatLane(unsigned nElts, const std::string &vec, - const std::string &lane) { - std::string s = "__builtin_shufflevector(" + vec + ", " + vec; - for (unsigned i = 0; i < nElts; ++i) - s += ", " + lane; - s += ")"; - return s; -} - -static unsigned GetNumElements(StringRef typestr, bool &quad) { - quad = false; - bool dummy = false; - char type = ClassifyType(typestr, quad, dummy, dummy); - unsigned nElts = 0; - switch (type) { - case 'c': nElts = 8; break; - case 's': nElts = 4; break; - case 'i': nElts = 2; break; - case 'l': nElts = 1; break; - case 'h': nElts = 4; break; - case 'f': nElts = 2; break; - default: - throw "unhandled type!"; - break; - } - if (quad) nElts <<= 1; - return nElts; -} - -// Generate the definition for this intrinsic, e.g. "a + b" for OpAdd. -static std::string GenOpString(OpKind op, const std::string &proto, - StringRef typestr) { - bool quad; - unsigned nElts = GetNumElements(typestr, quad); - bool define = UseMacro(proto); - - std::string ts = TypeString(proto[0], typestr); - std::string s; - if (!define) { - s = "return "; - } - - switch(op) { - case OpAdd: - s += "__a + __b;"; - break; - case OpAddl: - s += Extend(typestr, "__a") + " + " + Extend(typestr, "__b") + ";"; - break; - case OpAddw: - s += "__a + " + Extend(typestr, "__b") + ";"; - break; - case OpSub: - s += "__a - __b;"; - break; - case OpSubl: - s += Extend(typestr, "__a") + " - " + Extend(typestr, "__b") + ";"; - break; - case OpSubw: - s += "__a - " + Extend(typestr, "__b") + ";"; - break; - case OpMulN: - s += "__a * " + Duplicate(nElts, typestr, "__b") + ";"; - break; - case OpMulLane: - s += "__a * " + SplatLane(nElts, "__b", "__c") + ";"; - break; - case OpMul: - s += "__a * __b;"; - break; - case OpMullLane: - s += MangleName("vmull", typestr, ClassS) + "(__a, " + - SplatLane(nElts, "__b", "__c") + ");"; - break; - case OpMlaN: - s += "__a + (__b * " + Duplicate(nElts, typestr, "__c") + ");"; - break; - case OpMlaLane: - s += "__a + (__b * " + SplatLane(nElts, "__c", "__d") + ");"; - break; - case OpMla: - s += "__a + (__b * __c);"; - break; - case OpMlalN: - s += "__a + " + MangleName("vmull", typestr, ClassS) + "(__b, " + - Duplicate(nElts, typestr, "__c") + ");"; - break; - case OpMlalLane: - s += "__a + " + MangleName("vmull", typestr, ClassS) + "(__b, " + - SplatLane(nElts, "__c", "__d") + ");"; - break; - case OpMlal: - s += "__a + " + MangleName("vmull", typestr, ClassS) + "(__b, __c);"; - break; - case OpMlsN: - s += "__a - (__b * " + Duplicate(nElts, typestr, "__c") + ");"; - break; - case OpMlsLane: - s += "__a - (__b * " + SplatLane(nElts, "__c", "__d") + ");"; - break; - case OpMls: - s += "__a - (__b * __c);"; - break; - case OpMlslN: - s += "__a - " + MangleName("vmull", typestr, ClassS) + "(__b, " + - Duplicate(nElts, typestr, "__c") + ");"; - break; - case OpMlslLane: - s += "__a - " + MangleName("vmull", typestr, ClassS) + "(__b, " + - SplatLane(nElts, "__c", "__d") + ");"; - break; - case OpMlsl: - s += "__a - " + MangleName("vmull", typestr, ClassS) + "(__b, __c);"; - break; - case OpQDMullLane: - s += MangleName("vqdmull", typestr, ClassS) + "(__a, " + - SplatLane(nElts, "__b", "__c") + ");"; - break; - case OpQDMlalLane: - s += MangleName("vqdmlal", typestr, ClassS) + "(__a, __b, " + - SplatLane(nElts, "__c", "__d") + ");"; - break; - case OpQDMlslLane: - s += MangleName("vqdmlsl", typestr, ClassS) + "(__a, __b, " + - SplatLane(nElts, "__c", "__d") + ");"; - break; - case OpQDMulhLane: - s += MangleName("vqdmulh", typestr, ClassS) + "(__a, " + - SplatLane(nElts, "__b", "__c") + ");"; - break; - case OpQRDMulhLane: - s += MangleName("vqrdmulh", typestr, ClassS) + "(__a, " + - SplatLane(nElts, "__b", "__c") + ");"; - break; - case OpEq: - s += "(" + ts + ")(__a == __b);"; - break; - case OpGe: - s += "(" + ts + ")(__a >= __b);"; - break; - case OpLe: - s += "(" + ts + ")(__a <= __b);"; - break; - case OpGt: - s += "(" + ts + ")(__a > __b);"; - break; - case OpLt: - s += "(" + ts + ")(__a < __b);"; - break; - case OpNeg: - s += " -__a;"; - break; - case OpNot: - s += " ~__a;"; - break; - case OpAnd: - s += "__a & __b;"; - break; - case OpOr: - s += "__a | __b;"; - break; - case OpXor: - s += "__a ^ __b;"; - break; - case OpAndNot: - s += "__a & ~__b;"; - break; - case OpOrNot: - s += "__a | ~__b;"; - break; - case OpCast: - s += "(" + ts + ")__a;"; - break; - case OpConcat: - s += "(" + ts + ")__builtin_shufflevector((int64x1_t)__a"; - s += ", (int64x1_t)__b, 0, 1);"; - break; - case OpHi: - s += "(" + ts + - ")__builtin_shufflevector((int64x2_t)__a, (int64x2_t)__a, 1);"; - break; - case OpLo: - s += "(" + ts + - ")__builtin_shufflevector((int64x2_t)__a, (int64x2_t)__a, 0);"; - break; - case OpDup: - s += Duplicate(nElts, typestr, "__a") + ";"; - break; - case OpDupLane: - s += SplatLane(nElts, "__a", "__b") + ";"; - break; - case OpSelect: - // ((0 & 1) | (~0 & 2)) - s += "(" + ts + ")"; - ts = TypeString(proto[1], typestr); - s += "((__a & (" + ts + ")__b) | "; - s += "(~__a & (" + ts + ")__c));"; - break; - case OpRev16: - s += "__builtin_shufflevector(__a, __a"; - for (unsigned i = 2; i <= nElts; i += 2) - for (unsigned j = 0; j != 2; ++j) - s += ", " + utostr(i - j - 1); - s += ");"; - break; - case OpRev32: { - unsigned WordElts = nElts >> (1 + (int)quad); - s += "__builtin_shufflevector(__a, __a"; - for (unsigned i = WordElts; i <= nElts; i += WordElts) - for (unsigned j = 0; j != WordElts; ++j) - s += ", " + utostr(i - j - 1); - s += ");"; - break; - } - case OpRev64: { - unsigned DblWordElts = nElts >> (int)quad; - s += "__builtin_shufflevector(__a, __a"; - for (unsigned i = DblWordElts; i <= nElts; i += DblWordElts) - for (unsigned j = 0; j != DblWordElts; ++j) - s += ", " + utostr(i - j - 1); - s += ");"; - break; - } - case OpAbdl: { - std::string abd = MangleName("vabd", typestr, ClassS) + "(__a, __b)"; - if (typestr[0] != 'U') { - // vabd results are always unsigned and must be zero-extended. - std::string utype = "U" + typestr.str(); - s += "(" + TypeString(proto[0], typestr) + ")"; - abd = "(" + TypeString('d', utype) + ")" + abd; - s += Extend(utype, abd) + ";"; - } else { - s += Extend(typestr, abd) + ";"; - } - break; - } - case OpAba: - s += "__a + " + MangleName("vabd", typestr, ClassS) + "(__b, __c);"; - break; - case OpAbal: { - s += "__a + "; - std::string abd = MangleName("vabd", typestr, ClassS) + "(__b, __c)"; - if (typestr[0] != 'U') { - // vabd results are always unsigned and must be zero-extended. - std::string utype = "U" + typestr.str(); - s += "(" + TypeString(proto[0], typestr) + ")"; - abd = "(" + TypeString('d', utype) + ")" + abd; - s += Extend(utype, abd) + ";"; - } else { - s += Extend(typestr, abd) + ";"; - } - break; - } - default: - throw "unknown OpKind!"; - break; - } - return s; -} - -static unsigned GetNeonEnum(const std::string &proto, StringRef typestr) { - unsigned mod = proto[0]; - unsigned ret = 0; - - if (mod == 'v' || mod == 'f') - mod = proto[1]; - - bool quad = false; - bool poly = false; - bool usgn = false; - bool scal = false; - bool cnst = false; - bool pntr = false; - - // Base type to get the type string for. - char type = ClassifyType(typestr, quad, poly, usgn); - - // Based on the modifying character, change the type and width if necessary. - type = ModType(mod, type, quad, poly, usgn, scal, cnst, pntr); - - if (usgn) - ret |= 0x08; - if (quad && proto[1] != 'g') - ret |= 0x10; - - switch (type) { - case 'c': - ret |= poly ? 5 : 0; - break; - case 's': - ret |= poly ? 6 : 1; - break; - case 'i': - ret |= 2; - break; - case 'l': - ret |= 3; - break; - case 'h': - ret |= 7; - break; - case 'f': - ret |= 4; - break; - default: - throw "unhandled type!"; - break; - } - return ret; -} - -// Generate the definition for this intrinsic, e.g. __builtin_neon_cls(a) -static std::string GenBuiltin(const std::string &name, const std::string &proto, - StringRef typestr, ClassKind ck) { - std::string s; - - // If this builtin returns a struct 2, 3, or 4 vectors, pass it as an implicit - // sret-like argument. - bool sret = (proto[0] >= '2' && proto[0] <= '4'); - - bool define = UseMacro(proto); - - // Check if the prototype has a scalar operand with the type of the vector - // elements. If not, bitcasting the args will take care of arg checking. - // The actual signedness etc. will be taken care of with special enums. - if (proto.find('s') == std::string::npos) - ck = ClassB; - - if (proto[0] != 'v') { - std::string ts = TypeString(proto[0], typestr); - - if (define) { - if (sret) - s += ts + " r; "; - else - s += "(" + ts + ")"; - } else if (sret) { - s += ts + " r; "; - } else { - s += "return (" + ts + ")"; - } - } - - bool splat = proto.find('a') != std::string::npos; - - s += "__builtin_neon_"; - if (splat) { - // Call the non-splat builtin: chop off the "_n" suffix from the name. - std::string vname(name, 0, name.size()-2); - s += MangleName(vname, typestr, ck); - } else { - s += MangleName(name, typestr, ck); - } - s += "("; - - // Pass the address of the return variable as the first argument to sret-like - // builtins. - if (sret) - s += "&r, "; - - char arg = 'a'; - for (unsigned i = 1, e = proto.size(); i != e; ++i, ++arg) { - std::string args = std::string(&arg, 1); - - // Use the local temporaries instead of the macro arguments. - args = "__" + args; - - bool argQuad = false; - bool argPoly = false; - bool argUsgn = false; - bool argScalar = false; - bool dummy = false; - char argType = ClassifyType(typestr, argQuad, argPoly, argUsgn); - argType = ModType(proto[i], argType, argQuad, argPoly, argUsgn, argScalar, - dummy, dummy); - - // Handle multiple-vector values specially, emitting each subvector as an - // argument to the __builtin. - if (proto[i] >= '2' && proto[i] <= '4') { - // Check if an explicit cast is needed. - if (argType != 'c' || argPoly || argUsgn) - args = (argQuad ? "(int8x16_t)" : "(int8x8_t)") + args; - - for (unsigned vi = 0, ve = proto[i] - '0'; vi != ve; ++vi) { - s += args + ".val[" + utostr(vi) + "]"; - if ((vi + 1) < ve) - s += ", "; - } - if ((i + 1) < e) - s += ", "; - - continue; - } - - if (splat && (i + 1) == e) - args = Duplicate(GetNumElements(typestr, argQuad), typestr, args); - - // Check if an explicit cast is needed. - if ((splat || !argScalar) && - ((ck == ClassB && argType != 'c') || argPoly || argUsgn)) { - std::string argTypeStr = "c"; - if (ck != ClassB) - argTypeStr = argType; - if (argQuad) - argTypeStr = "Q" + argTypeStr; - args = "(" + TypeString('d', argTypeStr) + ")" + args; - } - - s += args; - if ((i + 1) < e) - s += ", "; - } - - // Extra constant integer to hold type class enum for this function, e.g. s8 - if (ck == ClassB) - s += ", " + utostr(GetNeonEnum(proto, typestr)); - - s += ");"; - - if (proto[0] != 'v' && sret) { - if (define) - s += " r;"; - else - s += " return r;"; - } - return s; -} - -static std::string GenBuiltinDef(const std::string &name, - const std::string &proto, - StringRef typestr, ClassKind ck) { - std::string s("BUILTIN(__builtin_neon_"); - - // If all types are the same size, bitcasting the args will take care - // of arg checking. The actual signedness etc. will be taken care of with - // special enums. - if (proto.find('s') == std::string::npos) - ck = ClassB; - - s += MangleName(name, typestr, ck); - s += ", \""; - - for (unsigned i = 0, e = proto.size(); i != e; ++i) - s += BuiltinTypeString(proto[i], typestr, ck, i == 0); - - // Extra constant integer to hold type class enum for this function, e.g. s8 - if (ck == ClassB) - s += "i"; - - s += "\", \"n\")"; - return s; -} - -static std::string GenIntrinsic(const std::string &name, - const std::string &proto, - StringRef outTypeStr, StringRef inTypeStr, - OpKind kind, ClassKind classKind) { - assert(!proto.empty() && ""); - bool define = UseMacro(proto); - std::string s; - - // static always inline + return type - if (define) - s += "#define "; - else - s += "__ai " + TypeString(proto[0], outTypeStr) + " "; - - // Function name with type suffix - std::string mangledName = MangleName(name, outTypeStr, ClassS); - if (outTypeStr != inTypeStr) { - // If the input type is different (e.g., for vreinterpret), append a suffix - // for the input type. String off a "Q" (quad) prefix so that MangleName - // does not insert another "q" in the name. - unsigned typeStrOff = (inTypeStr[0] == 'Q' ? 1 : 0); - StringRef inTypeNoQuad = inTypeStr.substr(typeStrOff); - mangledName = MangleName(mangledName, inTypeNoQuad, ClassS); - } - s += mangledName; - - // Function arguments - s += GenArgs(proto, inTypeStr); - - // Definition. - if (define) { - s += " __extension__ ({ \\\n "; - s += GenMacroLocals(proto, inTypeStr); - } else { - s += " { \\\n "; - } - - if (kind != OpNone) - s += GenOpString(kind, proto, outTypeStr); - else - s += GenBuiltin(name, proto, outTypeStr, classKind); - if (define) - s += " })"; - else - s += " }"; - s += "\n"; - return s; -} - -/// run - Read the records in arm_neon.td and output arm_neon.h. arm_neon.h -/// is comprised of type definitions and function declarations. -void NeonEmitter::run(raw_ostream &OS) { - OS << - "/*===---- arm_neon.h - ARM Neon intrinsics ------------------------------" - "---===\n" - " *\n" - " * Permission is hereby granted, free of charge, to any person obtaining " - "a copy\n" - " * of this software and associated documentation files (the \"Software\")," - " to deal\n" - " * in the Software without restriction, including without limitation the " - "rights\n" - " * to use, copy, modify, merge, publish, distribute, sublicense, " - "and/or sell\n" - " * copies of the Software, and to permit persons to whom the Software is\n" - " * furnished to do so, subject to the following conditions:\n" - " *\n" - " * The above copyright notice and this permission notice shall be " - "included in\n" - " * all copies or substantial portions of the Software.\n" - " *\n" - " * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, " - "EXPRESS OR\n" - " * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF " - "MERCHANTABILITY,\n" - " * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT " - "SHALL THE\n" - " * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR " - "OTHER\n" - " * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, " - "ARISING FROM,\n" - " * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER " - "DEALINGS IN\n" - " * THE SOFTWARE.\n" - " *\n" - " *===--------------------------------------------------------------------" - "---===\n" - " */\n\n"; - - OS << "#ifndef __ARM_NEON_H\n"; - OS << "#define __ARM_NEON_H\n\n"; - - OS << "#ifndef __ARM_NEON__\n"; - OS << "#error \"NEON support not enabled\"\n"; - OS << "#endif\n\n"; - - OS << "#include <stdint.h>\n\n"; - - // Emit NEON-specific scalar typedefs. - OS << "typedef float float32_t;\n"; - OS << "typedef int8_t poly8_t;\n"; - OS << "typedef int16_t poly16_t;\n"; - OS << "typedef uint16_t float16_t;\n"; - - // Emit Neon vector typedefs. - std::string TypedefTypes("cQcsQsiQilQlUcQUcUsQUsUiQUiUlQUlhQhfQfPcQPcPsQPs"); - SmallVector<StringRef, 24> TDTypeVec; - ParseTypes(0, TypedefTypes, TDTypeVec); - - // Emit vector typedefs. - for (unsigned i = 0, e = TDTypeVec.size(); i != e; ++i) { - bool dummy, quad = false, poly = false; - (void) ClassifyType(TDTypeVec[i], quad, poly, dummy); - if (poly) - OS << "typedef __attribute__((neon_polyvector_type("; - else - OS << "typedef __attribute__((neon_vector_type("; - - unsigned nElts = GetNumElements(TDTypeVec[i], quad); - OS << utostr(nElts) << "))) "; - if (nElts < 10) - OS << " "; - - OS << TypeString('s', TDTypeVec[i]); - OS << " " << TypeString('d', TDTypeVec[i]) << ";\n"; - } - OS << "\n"; - - // Emit struct typedefs. - for (unsigned vi = 2; vi != 5; ++vi) { - for (unsigned i = 0, e = TDTypeVec.size(); i != e; ++i) { - std::string ts = TypeString('d', TDTypeVec[i]); - std::string vs = TypeString('0' + vi, TDTypeVec[i]); - OS << "typedef struct " << vs << " {\n"; - OS << " " << ts << " val"; - OS << "[" << utostr(vi) << "]"; - OS << ";\n} "; - OS << vs << ";\n\n"; - } - } - - OS << "#define __ai static __attribute__((__always_inline__))\n\n"; - - std::vector<Record*> RV = Records.getAllDerivedDefinitions("Inst"); - - // Emit vmovl, vmull and vabd intrinsics first so they can be used by other - // intrinsics. (Some of the saturating multiply instructions are also - // used to implement the corresponding "_lane" variants, but tablegen - // sorts the records into alphabetical order so that the "_lane" variants - // come after the intrinsics they use.) - emitIntrinsic(OS, Records.getDef("VMOVL")); - emitIntrinsic(OS, Records.getDef("VMULL")); - emitIntrinsic(OS, Records.getDef("VABD")); - - for (unsigned i = 0, e = RV.size(); i != e; ++i) { - Record *R = RV[i]; - if (R->getName() != "VMOVL" && - R->getName() != "VMULL" && - R->getName() != "VABD") - emitIntrinsic(OS, R); - } - - OS << "#undef __ai\n\n"; - OS << "#endif /* __ARM_NEON_H */\n"; -} - -/// emitIntrinsic - Write out the arm_neon.h header file definitions for the -/// intrinsics specified by record R. -void NeonEmitter::emitIntrinsic(raw_ostream &OS, Record *R) { - std::string name = R->getValueAsString("Name"); - std::string Proto = R->getValueAsString("Prototype"); - std::string Types = R->getValueAsString("Types"); - - SmallVector<StringRef, 16> TypeVec; - ParseTypes(R, Types, TypeVec); - - OpKind kind = OpMap[R->getValueAsDef("Operand")->getName()]; - - ClassKind classKind = ClassNone; - if (R->getSuperClasses().size() >= 2) - classKind = ClassMap[R->getSuperClasses()[1]]; - if (classKind == ClassNone && kind == OpNone) - throw TGError(R->getLoc(), "Builtin has no class kind"); - - for (unsigned ti = 0, te = TypeVec.size(); ti != te; ++ti) { - if (kind == OpReinterpret) { - bool outQuad = false; - bool dummy = false; - (void)ClassifyType(TypeVec[ti], outQuad, dummy, dummy); - for (unsigned srcti = 0, srcte = TypeVec.size(); - srcti != srcte; ++srcti) { - bool inQuad = false; - (void)ClassifyType(TypeVec[srcti], inQuad, dummy, dummy); - if (srcti == ti || inQuad != outQuad) - continue; - OS << GenIntrinsic(name, Proto, TypeVec[ti], TypeVec[srcti], - OpCast, ClassS); - } - } else { - OS << GenIntrinsic(name, Proto, TypeVec[ti], TypeVec[ti], - kind, classKind); - } - } - OS << "\n"; -} - -static unsigned RangeFromType(const char mod, StringRef typestr) { - // base type to get the type string for. - bool quad = false, dummy = false; - char type = ClassifyType(typestr, quad, dummy, dummy); - type = ModType(mod, type, quad, dummy, dummy, dummy, dummy, dummy); - - switch (type) { - case 'c': - return (8 << (int)quad) - 1; - case 'h': - case 's': - return (4 << (int)quad) - 1; - case 'f': - case 'i': - return (2 << (int)quad) - 1; - case 'l': - return (1 << (int)quad) - 1; - default: - throw "unhandled type!"; - break; - } - assert(0 && "unreachable"); - return 0; -} - -/// runHeader - Emit a file with sections defining: -/// 1. the NEON section of BuiltinsARM.def. -/// 2. the SemaChecking code for the type overload checking. -/// 3. the SemaChecking code for validation of intrinsic immedate arguments. -void NeonEmitter::runHeader(raw_ostream &OS) { - std::vector<Record*> RV = Records.getAllDerivedDefinitions("Inst"); - - StringMap<OpKind> EmittedMap; - - // Generate BuiltinsARM.def for NEON - OS << "#ifdef GET_NEON_BUILTINS\n"; - for (unsigned i = 0, e = RV.size(); i != e; ++i) { - Record *R = RV[i]; - OpKind k = OpMap[R->getValueAsDef("Operand")->getName()]; - if (k != OpNone) - continue; - - std::string Proto = R->getValueAsString("Prototype"); - - // Functions with 'a' (the splat code) in the type prototype should not get - // their own builtin as they use the non-splat variant. - if (Proto.find('a') != std::string::npos) - continue; - - std::string Types = R->getValueAsString("Types"); - SmallVector<StringRef, 16> TypeVec; - ParseTypes(R, Types, TypeVec); - - if (R->getSuperClasses().size() < 2) - throw TGError(R->getLoc(), "Builtin has no class kind"); - - std::string name = R->getValueAsString("Name"); - ClassKind ck = ClassMap[R->getSuperClasses()[1]]; - - for (unsigned ti = 0, te = TypeVec.size(); ti != te; ++ti) { - // Generate the BuiltinsARM.def declaration for this builtin, ensuring - // that each unique BUILTIN() macro appears only once in the output - // stream. - std::string bd = GenBuiltinDef(name, Proto, TypeVec[ti], ck); - if (EmittedMap.count(bd)) - continue; - - EmittedMap[bd] = OpNone; - OS << bd << "\n"; - } - } - OS << "#endif\n\n"; - - // Generate the overloaded type checking code for SemaChecking.cpp - OS << "#ifdef GET_NEON_OVERLOAD_CHECK\n"; - for (unsigned i = 0, e = RV.size(); i != e; ++i) { - Record *R = RV[i]; - OpKind k = OpMap[R->getValueAsDef("Operand")->getName()]; - if (k != OpNone) - continue; - - std::string Proto = R->getValueAsString("Prototype"); - std::string Types = R->getValueAsString("Types"); - std::string name = R->getValueAsString("Name"); - - // Functions with 'a' (the splat code) in the type prototype should not get - // their own builtin as they use the non-splat variant. - if (Proto.find('a') != std::string::npos) - continue; - - // Functions which have a scalar argument cannot be overloaded, no need to - // check them if we are emitting the type checking code. - if (Proto.find('s') != std::string::npos) - continue; - - SmallVector<StringRef, 16> TypeVec; - ParseTypes(R, Types, TypeVec); - - if (R->getSuperClasses().size() < 2) - throw TGError(R->getLoc(), "Builtin has no class kind"); - - int si = -1, qi = -1; - unsigned mask = 0, qmask = 0; - for (unsigned ti = 0, te = TypeVec.size(); ti != te; ++ti) { - // Generate the switch case(s) for this builtin for the type validation. - bool quad = false, poly = false, usgn = false; - (void) ClassifyType(TypeVec[ti], quad, poly, usgn); - - if (quad) { - qi = ti; - qmask |= 1 << GetNeonEnum(Proto, TypeVec[ti]); - } else { - si = ti; - mask |= 1 << GetNeonEnum(Proto, TypeVec[ti]); - } - } - if (mask) - OS << "case ARM::BI__builtin_neon_" - << MangleName(name, TypeVec[si], ClassB) - << ": mask = " << "0x" << utohexstr(mask) << "; break;\n"; - if (qmask) - OS << "case ARM::BI__builtin_neon_" - << MangleName(name, TypeVec[qi], ClassB) - << ": mask = " << "0x" << utohexstr(qmask) << "; break;\n"; - } - OS << "#endif\n\n"; - - // Generate the intrinsic range checking code for shift/lane immediates. - OS << "#ifdef GET_NEON_IMMEDIATE_CHECK\n"; - for (unsigned i = 0, e = RV.size(); i != e; ++i) { - Record *R = RV[i]; - - OpKind k = OpMap[R->getValueAsDef("Operand")->getName()]; - if (k != OpNone) - continue; - - std::string name = R->getValueAsString("Name"); - std::string Proto = R->getValueAsString("Prototype"); - std::string Types = R->getValueAsString("Types"); - - // Functions with 'a' (the splat code) in the type prototype should not get - // their own builtin as they use the non-splat variant. - if (Proto.find('a') != std::string::npos) - continue; - - // Functions which do not have an immediate do not need to have range - // checking code emitted. - size_t immPos = Proto.find('i'); - if (immPos == std::string::npos) - continue; - - SmallVector<StringRef, 16> TypeVec; - ParseTypes(R, Types, TypeVec); - - if (R->getSuperClasses().size() < 2) - throw TGError(R->getLoc(), "Builtin has no class kind"); - - ClassKind ck = ClassMap[R->getSuperClasses()[1]]; - - for (unsigned ti = 0, te = TypeVec.size(); ti != te; ++ti) { - std::string namestr, shiftstr, rangestr; - - if (R->getValueAsBit("isVCVT_N")) { - // VCVT between floating- and fixed-point values takes an immediate - // in the range 1 to 32. - ck = ClassB; - rangestr = "l = 1; u = 31"; // upper bound = l + u - } else if (Proto.find('s') == std::string::npos) { - // Builtins which are overloaded by type will need to have their upper - // bound computed at Sema time based on the type constant. - ck = ClassB; - if (R->getValueAsBit("isShift")) { - shiftstr = ", true"; - - // Right shifts have an 'r' in the name, left shifts do not. - if (name.find('r') != std::string::npos) - rangestr = "l = 1; "; - } - rangestr += "u = RFT(TV" + shiftstr + ")"; - } else { - // The immediate generally refers to a lane in the preceding argument. - assert(immPos > 0 && "unexpected immediate operand"); - rangestr = "u = " + utostr(RangeFromType(Proto[immPos-1], TypeVec[ti])); - } - // Make sure cases appear only once by uniquing them in a string map. - namestr = MangleName(name, TypeVec[ti], ck); - if (EmittedMap.count(namestr)) - continue; - EmittedMap[namestr] = OpNone; - - // Calculate the index of the immediate that should be range checked. - unsigned immidx = 0; - - // Builtins that return a struct of multiple vectors have an extra - // leading arg for the struct return. - if (Proto[0] >= '2' && Proto[0] <= '4') - ++immidx; - - // Add one to the index for each argument until we reach the immediate - // to be checked. Structs of vectors are passed as multiple arguments. - for (unsigned ii = 1, ie = Proto.size(); ii != ie; ++ii) { - switch (Proto[ii]) { - default: immidx += 1; break; - case '2': immidx += 2; break; - case '3': immidx += 3; break; - case '4': immidx += 4; break; - case 'i': ie = ii + 1; break; - } - } - OS << "case ARM::BI__builtin_neon_" << MangleName(name, TypeVec[ti], ck) - << ": i = " << immidx << "; " << rangestr << "; break;\n"; - } - } - OS << "#endif\n\n"; -} - -/// GenTest - Write out a test for the intrinsic specified by the name and -/// type strings, including the embedded patterns for FileCheck to match. -static std::string GenTest(const std::string &name, - const std::string &proto, - StringRef outTypeStr, StringRef inTypeStr, - bool isShift) { - assert(!proto.empty() && ""); - std::string s; - - // Function name with type suffix - std::string mangledName = MangleName(name, outTypeStr, ClassS); - if (outTypeStr != inTypeStr) { - // If the input type is different (e.g., for vreinterpret), append a suffix - // for the input type. String off a "Q" (quad) prefix so that MangleName - // does not insert another "q" in the name. - unsigned typeStrOff = (inTypeStr[0] == 'Q' ? 1 : 0); - StringRef inTypeNoQuad = inTypeStr.substr(typeStrOff); - mangledName = MangleName(mangledName, inTypeNoQuad, ClassS); - } - - // Emit the FileCheck patterns. - s += "// CHECK: test_" + mangledName + "\n"; - // s += "// CHECK: \n"; // FIXME: + expected instruction opcode. - - // Emit the start of the test function. - s += TypeString(proto[0], outTypeStr) + " test_" + mangledName + "("; - char arg = 'a'; - std::string comma; - for (unsigned i = 1, e = proto.size(); i != e; ++i, ++arg) { - // Do not create arguments for values that must be immediate constants. - if (proto[i] == 'i') - continue; - s += comma + TypeString(proto[i], inTypeStr) + " "; - s.push_back(arg); - comma = ", "; - } - s += ") { \\\n "; - - if (proto[0] != 'v') - s += "return "; - s += mangledName + "("; - arg = 'a'; - for (unsigned i = 1, e = proto.size(); i != e; ++i, ++arg) { - if (proto[i] == 'i') { - // For immediate operands, test the maximum value. - if (isShift) - s += "1"; // FIXME - else - // The immediate generally refers to a lane in the preceding argument. - s += utostr(RangeFromType(proto[i-1], inTypeStr)); - } else { - s.push_back(arg); - } - if ((i + 1) < e) - s += ", "; - } - s += ");\n}\n\n"; - return s; -} - -/// runTests - Write out a complete set of tests for all of the Neon -/// intrinsics. -void NeonEmitter::runTests(raw_ostream &OS) { - OS << - "// RUN: %clang_cc1 -triple thumbv7-apple-darwin \\\n" - "// RUN: -target-cpu cortex-a9 -ffreestanding -S -o - %s | FileCheck %s\n" - "\n" - "#include <arm_neon.h>\n" - "\n"; - - std::vector<Record*> RV = Records.getAllDerivedDefinitions("Inst"); - for (unsigned i = 0, e = RV.size(); i != e; ++i) { - Record *R = RV[i]; - std::string name = R->getValueAsString("Name"); - std::string Proto = R->getValueAsString("Prototype"); - std::string Types = R->getValueAsString("Types"); - bool isShift = R->getValueAsBit("isShift"); - - SmallVector<StringRef, 16> TypeVec; - ParseTypes(R, Types, TypeVec); - - OpKind kind = OpMap[R->getValueAsDef("Operand")->getName()]; - for (unsigned ti = 0, te = TypeVec.size(); ti != te; ++ti) { - if (kind == OpReinterpret) { - bool outQuad = false; - bool dummy = false; - (void)ClassifyType(TypeVec[ti], outQuad, dummy, dummy); - for (unsigned srcti = 0, srcte = TypeVec.size(); - srcti != srcte; ++srcti) { - bool inQuad = false; - (void)ClassifyType(TypeVec[srcti], inQuad, dummy, dummy); - if (srcti == ti || inQuad != outQuad) - continue; - OS << GenTest(name, Proto, TypeVec[ti], TypeVec[srcti], isShift); - } - } else { - OS << GenTest(name, Proto, TypeVec[ti], TypeVec[ti], isShift); - } - } - OS << "\n"; - } -} - diff --git a/utils/TableGen/NeonEmitter.h b/utils/TableGen/NeonEmitter.h deleted file mode 100644 index 12e4e86..0000000 --- a/utils/TableGen/NeonEmitter.h +++ /dev/null @@ -1,176 +0,0 @@ -//===- NeonEmitter.h - Generate arm_neon.h for use with clang ---*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This tablegen backend is responsible for emitting arm_neon.h, which includes -// a declaration and definition of each function specified by the ARM NEON -// compiler interface. See ARM document DUI0348B. -// -//===----------------------------------------------------------------------===// - -#ifndef NEON_EMITTER_H -#define NEON_EMITTER_H - -#include "Record.h" -#include "TableGenBackend.h" -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/StringMap.h" - -enum OpKind { - OpNone, - OpAdd, - OpAddl, - OpAddw, - OpSub, - OpSubl, - OpSubw, - OpMul, - OpMla, - OpMlal, - OpMls, - OpMlsl, - OpMulN, - OpMlaN, - OpMlsN, - OpMlalN, - OpMlslN, - OpMulLane, - OpMullLane, - OpMlaLane, - OpMlsLane, - OpMlalLane, - OpMlslLane, - OpQDMullLane, - OpQDMlalLane, - OpQDMlslLane, - OpQDMulhLane, - OpQRDMulhLane, - OpEq, - OpGe, - OpLe, - OpGt, - OpLt, - OpNeg, - OpNot, - OpAnd, - OpOr, - OpXor, - OpAndNot, - OpOrNot, - OpCast, - OpConcat, - OpDup, - OpDupLane, - OpHi, - OpLo, - OpSelect, - OpRev16, - OpRev32, - OpRev64, - OpReinterpret, - OpAbdl, - OpAba, - OpAbal -}; - -enum ClassKind { - ClassNone, - ClassI, // generic integer instruction, e.g., "i8" suffix - ClassS, // signed/unsigned/poly, e.g., "s8", "u8" or "p8" suffix - ClassW, // width-specific instruction, e.g., "8" suffix - ClassB // bitcast arguments with enum argument to specify type -}; - -namespace llvm { - - class NeonEmitter : public TableGenBackend { - RecordKeeper &Records; - StringMap<OpKind> OpMap; - DenseMap<Record*, ClassKind> ClassMap; - - public: - NeonEmitter(RecordKeeper &R) : Records(R) { - OpMap["OP_NONE"] = OpNone; - OpMap["OP_ADD"] = OpAdd; - OpMap["OP_ADDL"] = OpAddl; - OpMap["OP_ADDW"] = OpAddw; - OpMap["OP_SUB"] = OpSub; - OpMap["OP_SUBL"] = OpSubl; - OpMap["OP_SUBW"] = OpSubw; - OpMap["OP_MUL"] = OpMul; - OpMap["OP_MLA"] = OpMla; - OpMap["OP_MLAL"] = OpMlal; - OpMap["OP_MLS"] = OpMls; - OpMap["OP_MLSL"] = OpMlsl; - OpMap["OP_MUL_N"] = OpMulN; - OpMap["OP_MLA_N"] = OpMlaN; - OpMap["OP_MLS_N"] = OpMlsN; - OpMap["OP_MLAL_N"] = OpMlalN; - OpMap["OP_MLSL_N"] = OpMlslN; - OpMap["OP_MUL_LN"]= OpMulLane; - OpMap["OP_MULL_LN"] = OpMullLane; - OpMap["OP_MLA_LN"]= OpMlaLane; - OpMap["OP_MLS_LN"]= OpMlsLane; - OpMap["OP_MLAL_LN"] = OpMlalLane; - OpMap["OP_MLSL_LN"] = OpMlslLane; - OpMap["OP_QDMULL_LN"] = OpQDMullLane; - OpMap["OP_QDMLAL_LN"] = OpQDMlalLane; - OpMap["OP_QDMLSL_LN"] = OpQDMlslLane; - OpMap["OP_QDMULH_LN"] = OpQDMulhLane; - OpMap["OP_QRDMULH_LN"] = OpQRDMulhLane; - OpMap["OP_EQ"] = OpEq; - OpMap["OP_GE"] = OpGe; - OpMap["OP_LE"] = OpLe; - OpMap["OP_GT"] = OpGt; - OpMap["OP_LT"] = OpLt; - OpMap["OP_NEG"] = OpNeg; - OpMap["OP_NOT"] = OpNot; - OpMap["OP_AND"] = OpAnd; - OpMap["OP_OR"] = OpOr; - OpMap["OP_XOR"] = OpXor; - OpMap["OP_ANDN"] = OpAndNot; - OpMap["OP_ORN"] = OpOrNot; - OpMap["OP_CAST"] = OpCast; - OpMap["OP_CONC"] = OpConcat; - OpMap["OP_HI"] = OpHi; - OpMap["OP_LO"] = OpLo; - OpMap["OP_DUP"] = OpDup; - OpMap["OP_DUP_LN"] = OpDupLane; - OpMap["OP_SEL"] = OpSelect; - OpMap["OP_REV16"] = OpRev16; - OpMap["OP_REV32"] = OpRev32; - OpMap["OP_REV64"] = OpRev64; - OpMap["OP_REINT"] = OpReinterpret; - OpMap["OP_ABDL"] = OpAbdl; - OpMap["OP_ABA"] = OpAba; - OpMap["OP_ABAL"] = OpAbal; - - Record *SI = R.getClass("SInst"); - Record *II = R.getClass("IInst"); - Record *WI = R.getClass("WInst"); - ClassMap[SI] = ClassS; - ClassMap[II] = ClassI; - ClassMap[WI] = ClassW; - } - - // run - Emit arm_neon.h.inc - void run(raw_ostream &o); - - // runHeader - Emit all the __builtin prototypes used in arm_neon.h - void runHeader(raw_ostream &o); - - // runTests - Emit tests for all the Neon intrinsics. - void runTests(raw_ostream &o); - - private: - void emitIntrinsic(raw_ostream &OS, Record *R); - }; - -} // End llvm namespace - -#endif diff --git a/utils/TableGen/OptParserEmitter.cpp b/utils/TableGen/OptParserEmitter.cpp deleted file mode 100644 index 431026c..0000000 --- a/utils/TableGen/OptParserEmitter.cpp +++ /dev/null @@ -1,194 +0,0 @@ -//===- OptParserEmitter.cpp - Table Driven Command Line Parsing -----------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "OptParserEmitter.h" -#include "Record.h" -#include "llvm/ADT/STLExtras.h" -using namespace llvm; - -static int StrCmpOptionName(const char *A, const char *B) { - char a = *A, b = *B; - while (a == b) { - if (a == '\0') - return 0; - - a = *++A; - b = *++B; - } - - if (a == '\0') // A is a prefix of B. - return 1; - if (b == '\0') // B is a prefix of A. - return -1; - - // Otherwise lexicographic. - return (a < b) ? -1 : 1; -} - -static int CompareOptionRecords(const void *Av, const void *Bv) { - const Record *A = *(Record**) Av; - const Record *B = *(Record**) Bv; - - // Sentinel options precede all others and are only ordered by precedence. - bool ASent = A->getValueAsDef("Kind")->getValueAsBit("Sentinel"); - bool BSent = B->getValueAsDef("Kind")->getValueAsBit("Sentinel"); - if (ASent != BSent) - return ASent ? -1 : 1; - - // Compare options by name, unless they are sentinels. - if (!ASent) - if (int Cmp = StrCmpOptionName(A->getValueAsString("Name").c_str(), - B->getValueAsString("Name").c_str())) - return Cmp; - - // Then by the kind precedence; - int APrec = A->getValueAsDef("Kind")->getValueAsInt("Precedence"); - int BPrec = B->getValueAsDef("Kind")->getValueAsInt("Precedence"); - assert(APrec != BPrec && "Options are equivalent!"); - return APrec < BPrec ? -1 : 1; -} - -static const std::string getOptionName(const Record &R) { - // Use the record name unless EnumName is defined. - if (dynamic_cast<UnsetInit*>(R.getValueInit("EnumName"))) - return R.getName(); - - return R.getValueAsString("EnumName"); -} - -static raw_ostream &write_cstring(raw_ostream &OS, llvm::StringRef Str) { - OS << '"'; - OS.write_escaped(Str); - OS << '"'; - return OS; -} - -void OptParserEmitter::run(raw_ostream &OS) { - // Get the option groups and options. - const std::vector<Record*> &Groups = - Records.getAllDerivedDefinitions("OptionGroup"); - std::vector<Record*> Opts = Records.getAllDerivedDefinitions("Option"); - - if (GenDefs) - EmitSourceFileHeader("Option Parsing Definitions", OS); - else - EmitSourceFileHeader("Option Parsing Table", OS); - - array_pod_sort(Opts.begin(), Opts.end(), CompareOptionRecords); - if (GenDefs) { - OS << "#ifndef OPTION\n"; - OS << "#error \"Define OPTION prior to including this file!\"\n"; - OS << "#endif\n\n"; - - OS << "/////////\n"; - OS << "// Groups\n\n"; - for (unsigned i = 0, e = Groups.size(); i != e; ++i) { - const Record &R = *Groups[i]; - - // Start a single option entry. - OS << "OPTION("; - - // The option string. - OS << '"' << R.getValueAsString("Name") << '"'; - - // The option identifier name. - OS << ", "<< getOptionName(R); - - // The option kind. - OS << ", Group"; - - // The containing option group (if any). - OS << ", "; - if (const DefInit *DI = dynamic_cast<DefInit*>(R.getValueInit("Group"))) - OS << getOptionName(*DI->getDef()); - else - OS << "INVALID"; - - // The other option arguments (unused for groups). - OS << ", INVALID, 0, 0"; - - // The option help text. - if (!dynamic_cast<UnsetInit*>(R.getValueInit("HelpText"))) { - OS << ",\n"; - OS << " "; - write_cstring(OS, R.getValueAsString("HelpText")); - } else - OS << ", 0"; - - // The option meta-variable name (unused). - OS << ", 0)\n"; - } - OS << "\n"; - - OS << "//////////\n"; - OS << "// Options\n\n"; - for (unsigned i = 0, e = Opts.size(); i != e; ++i) { - const Record &R = *Opts[i]; - - // Start a single option entry. - OS << "OPTION("; - - // The option string. - write_cstring(OS, R.getValueAsString("Name")); - - // The option identifier name. - OS << ", "<< getOptionName(R); - - // The option kind. - OS << ", " << R.getValueAsDef("Kind")->getValueAsString("Name"); - - // The containing option group (if any). - OS << ", "; - if (const DefInit *DI = dynamic_cast<DefInit*>(R.getValueInit("Group"))) - OS << getOptionName(*DI->getDef()); - else - OS << "INVALID"; - - // The option alias (if any). - OS << ", "; - if (const DefInit *DI = dynamic_cast<DefInit*>(R.getValueInit("Alias"))) - OS << getOptionName(*DI->getDef()); - else - OS << "INVALID"; - - // The option flags. - const ListInit *LI = R.getValueAsListInit("Flags"); - if (LI->empty()) { - OS << ", 0"; - } else { - OS << ", "; - for (unsigned i = 0, e = LI->size(); i != e; ++i) { - if (i) - OS << " | "; - OS << dynamic_cast<DefInit*>(LI->getElement(i))->getDef()->getName(); - } - } - - // The option parameter field. - OS << ", " << R.getValueAsInt("NumArgs"); - - // The option help text. - if (!dynamic_cast<UnsetInit*>(R.getValueInit("HelpText"))) { - OS << ",\n"; - OS << " "; - write_cstring(OS, R.getValueAsString("HelpText")); - } else - OS << ", 0"; - - // The option meta-variable name. - OS << ", "; - if (!dynamic_cast<UnsetInit*>(R.getValueInit("MetaVarName"))) - write_cstring(OS, R.getValueAsString("MetaVarName")); - else - OS << "0"; - - OS << ")\n"; - } - } -} diff --git a/utils/TableGen/OptParserEmitter.h b/utils/TableGen/OptParserEmitter.h deleted file mode 100644 index 241a3f2..0000000 --- a/utils/TableGen/OptParserEmitter.h +++ /dev/null @@ -1,34 +0,0 @@ -//===- OptParserEmitter.h - Table Driven Command Line Parsing ---*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef UTILS_TABLEGEN_OPTPARSEREMITTER_H -#define UTILS_TABLEGEN_OPTPARSEREMITTER_H - -#include "TableGenBackend.h" - -namespace llvm { - /// OptParserEmitter - This tablegen backend takes an input .td file - /// describing a list of options and emits a data structure for parsing and - /// working with those options when given an input command line. - class OptParserEmitter : public TableGenBackend { - RecordKeeper &Records; - bool GenDefs; - - public: - OptParserEmitter(RecordKeeper &R, bool _GenDefs) - : Records(R), GenDefs(_GenDefs) {} - - /// run - Output the option parsing information. - /// - /// \param GenHeader - Generate the header describing the option IDs.x - void run(raw_ostream &OS); - }; -} - -#endif diff --git a/utils/TableGen/PseudoLoweringEmitter.cpp b/utils/TableGen/PseudoLoweringEmitter.cpp index db33c1f..c685527 100644 --- a/utils/TableGen/PseudoLoweringEmitter.cpp +++ b/utils/TableGen/PseudoLoweringEmitter.cpp @@ -8,10 +8,10 @@ //===----------------------------------------------------------------------===// #define DEBUG_TYPE "pseudo-lowering" -#include "Error.h" #include "CodeGenInstruction.h" #include "PseudoLoweringEmitter.h" -#include "Record.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" #include "llvm/ADT/IndexedMap.h" #include "llvm/ADT/StringMap.h" #include "llvm/Support/ErrorHandling.h" diff --git a/utils/TableGen/PseudoLoweringEmitter.h b/utils/TableGen/PseudoLoweringEmitter.h index 2749280..325bc8b 100644 --- a/utils/TableGen/PseudoLoweringEmitter.h +++ b/utils/TableGen/PseudoLoweringEmitter.h @@ -12,7 +12,7 @@ #include "CodeGenInstruction.h" #include "CodeGenTarget.h" -#include "TableGenBackend.h" +#include "llvm/TableGen/TableGenBackend.h" #include "llvm/ADT/IndexedMap.h" #include "llvm/ADT/SmallVector.h" diff --git a/utils/TableGen/Record.cpp b/utils/TableGen/Record.cpp deleted file mode 100644 index 730eca1..0000000 --- a/utils/TableGen/Record.cpp +++ /dev/null @@ -1,1575 +0,0 @@ -//===- Record.cpp - Record implementation ---------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Implement the tablegen record classes. -// -//===----------------------------------------------------------------------===// - -#include "Record.h" -#include "Error.h" -#include "llvm/Support/DataTypes.h" -#include "llvm/Support/Format.h" -#include "llvm/ADT/StringExtras.h" - -using namespace llvm; - -//===----------------------------------------------------------------------===// -// Type implementations -//===----------------------------------------------------------------------===// - -void RecTy::dump() const { print(errs()); } - -Init *BitRecTy::convertValue(BitsInit *BI) { - if (BI->getNumBits() != 1) return 0; // Only accept if just one bit! - return BI->getBit(0); -} - -bool BitRecTy::baseClassOf(const BitsRecTy *RHS) const { - return RHS->getNumBits() == 1; -} - -Init *BitRecTy::convertValue(IntInit *II) { - int64_t Val = II->getValue(); - if (Val != 0 && Val != 1) return 0; // Only accept 0 or 1 for a bit! - - return new BitInit(Val != 0); -} - -Init *BitRecTy::convertValue(TypedInit *VI) { - if (dynamic_cast<BitRecTy*>(VI->getType())) - return VI; // Accept variable if it is already of bit type! - return 0; -} - -std::string BitsRecTy::getAsString() const { - return "bits<" + utostr(Size) + ">"; -} - -Init *BitsRecTy::convertValue(UnsetInit *UI) { - BitsInit *Ret = new BitsInit(Size); - - for (unsigned i = 0; i != Size; ++i) - Ret->setBit(i, new UnsetInit()); - return Ret; -} - -Init *BitsRecTy::convertValue(BitInit *UI) { - if (Size != 1) return 0; // Can only convert single bit. - BitsInit *Ret = new BitsInit(1); - Ret->setBit(0, UI); - return Ret; -} - -/// canFitInBitfield - Return true if the number of bits is large enough to hold -/// the integer value. -static bool canFitInBitfield(int64_t Value, unsigned NumBits) { - // For example, with NumBits == 4, we permit Values from [-7 .. 15]. - return (NumBits >= sizeof(Value) * 8) || - (Value >> NumBits == 0) || (Value >> (NumBits-1) == -1); -} - -/// convertValue from Int initializer to bits type: Split the integer up into the -/// appropriate bits. -/// -Init *BitsRecTy::convertValue(IntInit *II) { - int64_t Value = II->getValue(); - // Make sure this bitfield is large enough to hold the integer value. - if (!canFitInBitfield(Value, Size)) - return 0; - - BitsInit *Ret = new BitsInit(Size); - for (unsigned i = 0; i != Size; ++i) - Ret->setBit(i, new BitInit(Value & (1LL << i))); - - return Ret; -} - -Init *BitsRecTy::convertValue(BitsInit *BI) { - // If the number of bits is right, return it. Otherwise we need to expand or - // truncate. - if (BI->getNumBits() == Size) return BI; - return 0; -} - -Init *BitsRecTy::convertValue(TypedInit *VI) { - if (BitsRecTy *BRT = dynamic_cast<BitsRecTy*>(VI->getType())) - if (BRT->Size == Size) { - BitsInit *Ret = new BitsInit(Size); - for (unsigned i = 0; i != Size; ++i) - Ret->setBit(i, new VarBitInit(VI, i)); - return Ret; - } - - if (Size == 1 && dynamic_cast<BitRecTy*>(VI->getType())) { - BitsInit *Ret = new BitsInit(1); - Ret->setBit(0, VI); - return Ret; - } - - if (TernOpInit *Tern = dynamic_cast<TernOpInit*>(VI)) { - if (Tern->getOpcode() == TernOpInit::IF) { - Init *LHS = Tern->getLHS(); - Init *MHS = Tern->getMHS(); - Init *RHS = Tern->getRHS(); - - IntInit *MHSi = dynamic_cast<IntInit*>(MHS); - IntInit *RHSi = dynamic_cast<IntInit*>(RHS); - - if (MHSi && RHSi) { - int64_t MHSVal = MHSi->getValue(); - int64_t RHSVal = RHSi->getValue(); - - if (canFitInBitfield(MHSVal, Size) && canFitInBitfield(RHSVal, Size)) { - BitsInit *Ret = new BitsInit(Size); - - for (unsigned i = 0; i != Size; ++i) - Ret->setBit(i, new TernOpInit(TernOpInit::IF, LHS, - new IntInit((MHSVal & (1LL << i)) ? 1 : 0), - new IntInit((RHSVal & (1LL << i)) ? 1 : 0), - VI->getType())); - - return Ret; - } - } else { - BitsInit *MHSbs = dynamic_cast<BitsInit*>(MHS); - BitsInit *RHSbs = dynamic_cast<BitsInit*>(RHS); - - if (MHSbs && RHSbs) { - BitsInit *Ret = new BitsInit(Size); - - for (unsigned i = 0; i != Size; ++i) - Ret->setBit(i, new TernOpInit(TernOpInit::IF, LHS, - MHSbs->getBit(i), - RHSbs->getBit(i), - VI->getType())); - - return Ret; - } - } - } - } - - return 0; -} - -Init *IntRecTy::convertValue(BitInit *BI) { - return new IntInit(BI->getValue()); -} - -Init *IntRecTy::convertValue(BitsInit *BI) { - int64_t Result = 0; - for (unsigned i = 0, e = BI->getNumBits(); i != e; ++i) - if (BitInit *Bit = dynamic_cast<BitInit*>(BI->getBit(i))) { - Result |= Bit->getValue() << i; - } else { - return 0; - } - return new IntInit(Result); -} - -Init *IntRecTy::convertValue(TypedInit *TI) { - if (TI->getType()->typeIsConvertibleTo(this)) - return TI; // Accept variable if already of the right type! - return 0; -} - -Init *StringRecTy::convertValue(UnOpInit *BO) { - if (BO->getOpcode() == UnOpInit::CAST) { - Init *L = BO->getOperand()->convertInitializerTo(this); - if (L == 0) return 0; - if (L != BO->getOperand()) - return new UnOpInit(UnOpInit::CAST, L, new StringRecTy); - return BO; - } - - return convertValue((TypedInit*)BO); -} - -Init *StringRecTy::convertValue(BinOpInit *BO) { - if (BO->getOpcode() == BinOpInit::STRCONCAT) { - Init *L = BO->getLHS()->convertInitializerTo(this); - Init *R = BO->getRHS()->convertInitializerTo(this); - if (L == 0 || R == 0) return 0; - if (L != BO->getLHS() || R != BO->getRHS()) - return new BinOpInit(BinOpInit::STRCONCAT, L, R, new StringRecTy); - return BO; - } - - return convertValue((TypedInit*)BO); -} - - -Init *StringRecTy::convertValue(TypedInit *TI) { - if (dynamic_cast<StringRecTy*>(TI->getType())) - return TI; // Accept variable if already of the right type! - return 0; -} - -std::string ListRecTy::getAsString() const { - return "list<" + Ty->getAsString() + ">"; -} - -Init *ListRecTy::convertValue(ListInit *LI) { - std::vector<Init*> Elements; - - // Verify that all of the elements of the list are subclasses of the - // appropriate class! - for (unsigned i = 0, e = LI->getSize(); i != e; ++i) - if (Init *CI = LI->getElement(i)->convertInitializerTo(Ty)) - Elements.push_back(CI); - else - return 0; - - ListRecTy *LType = dynamic_cast<ListRecTy*>(LI->getType()); - if (LType == 0) { - return 0; - } - - return new ListInit(Elements, new ListRecTy(Ty)); -} - -Init *ListRecTy::convertValue(TypedInit *TI) { - // Ensure that TI is compatible with our class. - if (ListRecTy *LRT = dynamic_cast<ListRecTy*>(TI->getType())) - if (LRT->getElementType()->typeIsConvertibleTo(getElementType())) - return TI; - return 0; -} - -Init *CodeRecTy::convertValue(TypedInit *TI) { - if (TI->getType()->typeIsConvertibleTo(this)) - return TI; - return 0; -} - -Init *DagRecTy::convertValue(TypedInit *TI) { - if (TI->getType()->typeIsConvertibleTo(this)) - return TI; - return 0; -} - -Init *DagRecTy::convertValue(UnOpInit *BO) { - if (BO->getOpcode() == UnOpInit::CAST) { - Init *L = BO->getOperand()->convertInitializerTo(this); - if (L == 0) return 0; - if (L != BO->getOperand()) - return new UnOpInit(UnOpInit::CAST, L, new DagRecTy); - return BO; - } - return 0; -} - -Init *DagRecTy::convertValue(BinOpInit *BO) { - if (BO->getOpcode() == BinOpInit::CONCAT) { - Init *L = BO->getLHS()->convertInitializerTo(this); - Init *R = BO->getRHS()->convertInitializerTo(this); - if (L == 0 || R == 0) return 0; - if (L != BO->getLHS() || R != BO->getRHS()) - return new BinOpInit(BinOpInit::CONCAT, L, R, new DagRecTy); - return BO; - } - return 0; -} - -std::string RecordRecTy::getAsString() const { - return Rec->getName(); -} - -Init *RecordRecTy::convertValue(DefInit *DI) { - // Ensure that DI is a subclass of Rec. - if (!DI->getDef()->isSubClassOf(Rec)) - return 0; - return DI; -} - -Init *RecordRecTy::convertValue(TypedInit *TI) { - // Ensure that TI is compatible with Rec. - if (RecordRecTy *RRT = dynamic_cast<RecordRecTy*>(TI->getType())) - if (RRT->getRecord()->isSubClassOf(getRecord()) || - RRT->getRecord() == getRecord()) - return TI; - return 0; -} - -bool RecordRecTy::baseClassOf(const RecordRecTy *RHS) const { - if (Rec == RHS->getRecord() || RHS->getRecord()->isSubClassOf(Rec)) - return true; - - const std::vector<Record*> &SC = Rec->getSuperClasses(); - for (unsigned i = 0, e = SC.size(); i != e; ++i) - if (RHS->getRecord()->isSubClassOf(SC[i])) - return true; - - return false; -} - - -/// resolveTypes - Find a common type that T1 and T2 convert to. -/// Return 0 if no such type exists. -/// -RecTy *llvm::resolveTypes(RecTy *T1, RecTy *T2) { - if (!T1->typeIsConvertibleTo(T2)) { - if (!T2->typeIsConvertibleTo(T1)) { - // If one is a Record type, check superclasses - RecordRecTy *RecTy1 = dynamic_cast<RecordRecTy*>(T1); - if (RecTy1) { - // See if T2 inherits from a type T1 also inherits from - const std::vector<Record *> &T1SuperClasses = - RecTy1->getRecord()->getSuperClasses(); - for(std::vector<Record *>::const_iterator i = T1SuperClasses.begin(), - iend = T1SuperClasses.end(); - i != iend; - ++i) { - RecordRecTy *SuperRecTy1 = new RecordRecTy(*i); - RecTy *NewType1 = resolveTypes(SuperRecTy1, T2); - if (NewType1 != 0) { - if (NewType1 != SuperRecTy1) { - delete SuperRecTy1; - } - return NewType1; - } - } - } - RecordRecTy *RecTy2 = dynamic_cast<RecordRecTy*>(T2); - if (RecTy2) { - // See if T1 inherits from a type T2 also inherits from - const std::vector<Record *> &T2SuperClasses = - RecTy2->getRecord()->getSuperClasses(); - for (std::vector<Record *>::const_iterator i = T2SuperClasses.begin(), - iend = T2SuperClasses.end(); - i != iend; - ++i) { - RecordRecTy *SuperRecTy2 = new RecordRecTy(*i); - RecTy *NewType2 = resolveTypes(T1, SuperRecTy2); - if (NewType2 != 0) { - if (NewType2 != SuperRecTy2) { - delete SuperRecTy2; - } - return NewType2; - } - } - } - return 0; - } - return T2; - } - return T1; -} - - -//===----------------------------------------------------------------------===// -// Initializer implementations -//===----------------------------------------------------------------------===// - -void Init::dump() const { return print(errs()); } - -Init *BitsInit::convertInitializerBitRange(const std::vector<unsigned> &Bits) { - BitsInit *BI = new BitsInit(Bits.size()); - for (unsigned i = 0, e = Bits.size(); i != e; ++i) { - if (Bits[i] >= getNumBits()) { - delete BI; - return 0; - } - BI->setBit(i, getBit(Bits[i])); - } - return BI; -} - -std::string BitsInit::getAsString() const { - std::string Result = "{ "; - for (unsigned i = 0, e = getNumBits(); i != e; ++i) { - if (i) Result += ", "; - if (Init *Bit = getBit(e-i-1)) - Result += Bit->getAsString(); - else - Result += "*"; - } - return Result + " }"; -} - -// resolveReferences - If there are any field references that refer to fields -// that have been filled in, we can propagate the values now. -// -Init *BitsInit::resolveReferences(Record &R, const RecordVal *RV) { - bool Changed = false; - BitsInit *New = new BitsInit(getNumBits()); - - for (unsigned i = 0, e = Bits.size(); i != e; ++i) { - Init *B; - Init *CurBit = getBit(i); - - do { - B = CurBit; - CurBit = CurBit->resolveReferences(R, RV); - Changed |= B != CurBit; - } while (B != CurBit); - New->setBit(i, CurBit); - } - - if (Changed) - return New; - delete New; - return this; -} - -std::string IntInit::getAsString() const { - return itostr(Value); -} - -Init *IntInit::convertInitializerBitRange(const std::vector<unsigned> &Bits) { - BitsInit *BI = new BitsInit(Bits.size()); - - for (unsigned i = 0, e = Bits.size(); i != e; ++i) { - if (Bits[i] >= 64) { - delete BI; - return 0; - } - BI->setBit(i, new BitInit(Value & (INT64_C(1) << Bits[i]))); - } - return BI; -} - -Init *ListInit::convertInitListSlice(const std::vector<unsigned> &Elements) { - std::vector<Init*> Vals; - for (unsigned i = 0, e = Elements.size(); i != e; ++i) { - if (Elements[i] >= getSize()) - return 0; - Vals.push_back(getElement(Elements[i])); - } - return new ListInit(Vals, getType()); -} - -Record *ListInit::getElementAsRecord(unsigned i) const { - assert(i < Values.size() && "List element index out of range!"); - DefInit *DI = dynamic_cast<DefInit*>(Values[i]); - if (DI == 0) throw "Expected record in list!"; - return DI->getDef(); -} - -Init *ListInit::resolveReferences(Record &R, const RecordVal *RV) { - std::vector<Init*> Resolved; - Resolved.reserve(getSize()); - bool Changed = false; - - for (unsigned i = 0, e = getSize(); i != e; ++i) { - Init *E; - Init *CurElt = getElement(i); - - do { - E = CurElt; - CurElt = CurElt->resolveReferences(R, RV); - Changed |= E != CurElt; - } while (E != CurElt); - Resolved.push_back(E); - } - - if (Changed) - return new ListInit(Resolved, getType()); - return this; -} - -Init *ListInit::resolveListElementReference(Record &R, const RecordVal *IRV, - unsigned Elt) { - if (Elt >= getSize()) - return 0; // Out of range reference. - Init *E = getElement(Elt); - // If the element is set to some value, or if we are resolving a reference - // to a specific variable and that variable is explicitly unset, then - // replace the VarListElementInit with it. - if (IRV || !dynamic_cast<UnsetInit*>(E)) - return E; - return 0; -} - -std::string ListInit::getAsString() const { - std::string Result = "["; - for (unsigned i = 0, e = Values.size(); i != e; ++i) { - if (i) Result += ", "; - Result += Values[i]->getAsString(); - } - return Result + "]"; -} - -Init *OpInit::resolveBitReference(Record &R, const RecordVal *IRV, - unsigned Bit) { - Init *Folded = Fold(&R, 0); - - if (Folded != this) { - TypedInit *Typed = dynamic_cast<TypedInit *>(Folded); - if (Typed) { - return Typed->resolveBitReference(R, IRV, Bit); - } - } - - return 0; -} - -Init *OpInit::resolveListElementReference(Record &R, const RecordVal *IRV, - unsigned Elt) { - Init *Folded = Fold(&R, 0); - - if (Folded != this) { - TypedInit *Typed = dynamic_cast<TypedInit *>(Folded); - if (Typed) { - return Typed->resolveListElementReference(R, IRV, Elt); - } - } - - return 0; -} - -Init *UnOpInit::Fold(Record *CurRec, MultiClass *CurMultiClass) { - switch (getOpcode()) { - default: assert(0 && "Unknown unop"); - case CAST: { - if (getType()->getAsString() == "string") { - StringInit *LHSs = dynamic_cast<StringInit*>(LHS); - if (LHSs) { - return LHSs; - } - - DefInit *LHSd = dynamic_cast<DefInit*>(LHS); - if (LHSd) { - return new StringInit(LHSd->getDef()->getName()); - } - } else { - StringInit *LHSs = dynamic_cast<StringInit*>(LHS); - if (LHSs) { - std::string Name = LHSs->getValue(); - - // From TGParser::ParseIDValue - if (CurRec) { - if (const RecordVal *RV = CurRec->getValue(Name)) { - if (RV->getType() != getType()) - throw "type mismatch in cast"; - return new VarInit(Name, RV->getType()); - } - - std::string TemplateArgName = CurRec->getName()+":"+Name; - if (CurRec->isTemplateArg(TemplateArgName)) { - const RecordVal *RV = CurRec->getValue(TemplateArgName); - assert(RV && "Template arg doesn't exist??"); - - if (RV->getType() != getType()) - throw "type mismatch in cast"; - - return new VarInit(TemplateArgName, RV->getType()); - } - } - - if (CurMultiClass) { - std::string MCName = CurMultiClass->Rec.getName()+"::"+Name; - if (CurMultiClass->Rec.isTemplateArg(MCName)) { - const RecordVal *RV = CurMultiClass->Rec.getValue(MCName); - assert(RV && "Template arg doesn't exist??"); - - if (RV->getType() != getType()) - throw "type mismatch in cast"; - - return new VarInit(MCName, RV->getType()); - } - } - - if (Record *D = (CurRec->getRecords()).getDef(Name)) - return new DefInit(D); - - throw TGError(CurRec->getLoc(), "Undefined reference:'" + Name + "'\n"); - } - } - break; - } - case HEAD: { - ListInit *LHSl = dynamic_cast<ListInit*>(LHS); - if (LHSl) { - if (LHSl->getSize() == 0) { - assert(0 && "Empty list in car"); - return 0; - } - return LHSl->getElement(0); - } - break; - } - case TAIL: { - ListInit *LHSl = dynamic_cast<ListInit*>(LHS); - if (LHSl) { - if (LHSl->getSize() == 0) { - assert(0 && "Empty list in cdr"); - return 0; - } - ListInit *Result = new ListInit(LHSl->begin()+1, LHSl->end(), - LHSl->getType()); - return Result; - } - break; - } - case EMPTY: { - ListInit *LHSl = dynamic_cast<ListInit*>(LHS); - if (LHSl) { - if (LHSl->getSize() == 0) { - return new IntInit(1); - } else { - return new IntInit(0); - } - } - StringInit *LHSs = dynamic_cast<StringInit*>(LHS); - if (LHSs) { - if (LHSs->getValue().empty()) { - return new IntInit(1); - } else { - return new IntInit(0); - } - } - - break; - } - } - return this; -} - -Init *UnOpInit::resolveReferences(Record &R, const RecordVal *RV) { - Init *lhs = LHS->resolveReferences(R, RV); - - if (LHS != lhs) - return (new UnOpInit(getOpcode(), lhs, getType()))->Fold(&R, 0); - return Fold(&R, 0); -} - -std::string UnOpInit::getAsString() const { - std::string Result; - switch (Opc) { - case CAST: Result = "!cast<" + getType()->getAsString() + ">"; break; - case HEAD: Result = "!head"; break; - case TAIL: Result = "!tail"; break; - case EMPTY: Result = "!empty"; break; - } - return Result + "(" + LHS->getAsString() + ")"; -} - -Init *BinOpInit::Fold(Record *CurRec, MultiClass *CurMultiClass) { - switch (getOpcode()) { - default: assert(0 && "Unknown binop"); - case CONCAT: { - DagInit *LHSs = dynamic_cast<DagInit*>(LHS); - DagInit *RHSs = dynamic_cast<DagInit*>(RHS); - if (LHSs && RHSs) { - DefInit *LOp = dynamic_cast<DefInit*>(LHSs->getOperator()); - DefInit *ROp = dynamic_cast<DefInit*>(RHSs->getOperator()); - if (LOp == 0 || ROp == 0 || LOp->getDef() != ROp->getDef()) - throw "Concated Dag operators do not match!"; - std::vector<Init*> Args; - std::vector<std::string> ArgNames; - for (unsigned i = 0, e = LHSs->getNumArgs(); i != e; ++i) { - Args.push_back(LHSs->getArg(i)); - ArgNames.push_back(LHSs->getArgName(i)); - } - for (unsigned i = 0, e = RHSs->getNumArgs(); i != e; ++i) { - Args.push_back(RHSs->getArg(i)); - ArgNames.push_back(RHSs->getArgName(i)); - } - return new DagInit(LHSs->getOperator(), "", Args, ArgNames); - } - break; - } - case STRCONCAT: { - StringInit *LHSs = dynamic_cast<StringInit*>(LHS); - StringInit *RHSs = dynamic_cast<StringInit*>(RHS); - if (LHSs && RHSs) - return new StringInit(LHSs->getValue() + RHSs->getValue()); - break; - } - case EQ: { - // try to fold eq comparison for 'bit' and 'int', otherwise fallback - // to string objects. - IntInit* L = - dynamic_cast<IntInit*>(LHS->convertInitializerTo(new IntRecTy())); - IntInit* R = - dynamic_cast<IntInit*>(RHS->convertInitializerTo(new IntRecTy())); - - if (L && R) - return new IntInit(L->getValue() == R->getValue()); - - StringInit *LHSs = dynamic_cast<StringInit*>(LHS); - StringInit *RHSs = dynamic_cast<StringInit*>(RHS); - - // Make sure we've resolved - if (LHSs && RHSs) - return new IntInit(LHSs->getValue() == RHSs->getValue()); - - break; - } - case SHL: - case SRA: - case SRL: { - IntInit *LHSi = dynamic_cast<IntInit*>(LHS); - IntInit *RHSi = dynamic_cast<IntInit*>(RHS); - if (LHSi && RHSi) { - int64_t LHSv = LHSi->getValue(), RHSv = RHSi->getValue(); - int64_t Result; - switch (getOpcode()) { - default: assert(0 && "Bad opcode!"); - case SHL: Result = LHSv << RHSv; break; - case SRA: Result = LHSv >> RHSv; break; - case SRL: Result = (uint64_t)LHSv >> (uint64_t)RHSv; break; - } - return new IntInit(Result); - } - break; - } - } - return this; -} - -Init *BinOpInit::resolveReferences(Record &R, const RecordVal *RV) { - Init *lhs = LHS->resolveReferences(R, RV); - Init *rhs = RHS->resolveReferences(R, RV); - - if (LHS != lhs || RHS != rhs) - return (new BinOpInit(getOpcode(), lhs, rhs, getType()))->Fold(&R, 0); - return Fold(&R, 0); -} - -std::string BinOpInit::getAsString() const { - std::string Result; - switch (Opc) { - case CONCAT: Result = "!con"; break; - case SHL: Result = "!shl"; break; - case SRA: Result = "!sra"; break; - case SRL: Result = "!srl"; break; - case EQ: Result = "!eq"; break; - case STRCONCAT: Result = "!strconcat"; break; - } - return Result + "(" + LHS->getAsString() + ", " + RHS->getAsString() + ")"; -} - -static Init *ForeachHelper(Init *LHS, Init *MHS, Init *RHS, RecTy *Type, - Record *CurRec, MultiClass *CurMultiClass); - -static Init *EvaluateOperation(OpInit *RHSo, Init *LHS, Init *Arg, - RecTy *Type, Record *CurRec, - MultiClass *CurMultiClass) { - std::vector<Init *> NewOperands; - - TypedInit *TArg = dynamic_cast<TypedInit*>(Arg); - - // If this is a dag, recurse - if (TArg && TArg->getType()->getAsString() == "dag") { - Init *Result = ForeachHelper(LHS, Arg, RHSo, Type, - CurRec, CurMultiClass); - if (Result != 0) { - return Result; - } else { - return 0; - } - } - - for (int i = 0; i < RHSo->getNumOperands(); ++i) { - OpInit *RHSoo = dynamic_cast<OpInit*>(RHSo->getOperand(i)); - - if (RHSoo) { - Init *Result = EvaluateOperation(RHSoo, LHS, Arg, - Type, CurRec, CurMultiClass); - if (Result != 0) { - NewOperands.push_back(Result); - } else { - NewOperands.push_back(Arg); - } - } else if (LHS->getAsString() == RHSo->getOperand(i)->getAsString()) { - NewOperands.push_back(Arg); - } else { - NewOperands.push_back(RHSo->getOperand(i)); - } - } - - // Now run the operator and use its result as the new leaf - OpInit *NewOp = RHSo->clone(NewOperands); - Init *NewVal = NewOp->Fold(CurRec, CurMultiClass); - if (NewVal != NewOp) { - delete NewOp; - return NewVal; - } - return 0; -} - -static Init *ForeachHelper(Init *LHS, Init *MHS, Init *RHS, RecTy *Type, - Record *CurRec, MultiClass *CurMultiClass) { - DagInit *MHSd = dynamic_cast<DagInit*>(MHS); - ListInit *MHSl = dynamic_cast<ListInit*>(MHS); - - DagRecTy *DagType = dynamic_cast<DagRecTy*>(Type); - ListRecTy *ListType = dynamic_cast<ListRecTy*>(Type); - - OpInit *RHSo = dynamic_cast<OpInit*>(RHS); - - if (!RHSo) { - throw TGError(CurRec->getLoc(), "!foreach requires an operator\n"); - } - - TypedInit *LHSt = dynamic_cast<TypedInit*>(LHS); - - if (!LHSt) { - throw TGError(CurRec->getLoc(), "!foreach requires typed variable\n"); - } - - if ((MHSd && DagType) || (MHSl && ListType)) { - if (MHSd) { - Init *Val = MHSd->getOperator(); - Init *Result = EvaluateOperation(RHSo, LHS, Val, - Type, CurRec, CurMultiClass); - if (Result != 0) { - Val = Result; - } - - std::vector<std::pair<Init *, std::string> > args; - for (unsigned int i = 0; i < MHSd->getNumArgs(); ++i) { - Init *Arg; - std::string ArgName; - Arg = MHSd->getArg(i); - ArgName = MHSd->getArgName(i); - - // Process args - Init *Result = EvaluateOperation(RHSo, LHS, Arg, Type, - CurRec, CurMultiClass); - if (Result != 0) { - Arg = Result; - } - - // TODO: Process arg names - args.push_back(std::make_pair(Arg, ArgName)); - } - - return new DagInit(Val, "", args); - } - if (MHSl) { - std::vector<Init *> NewOperands; - std::vector<Init *> NewList(MHSl->begin(), MHSl->end()); - - for (ListInit::iterator li = NewList.begin(), - liend = NewList.end(); - li != liend; - ++li) { - Init *Item = *li; - NewOperands.clear(); - for(int i = 0; i < RHSo->getNumOperands(); ++i) { - // First, replace the foreach variable with the list item - if (LHS->getAsString() == RHSo->getOperand(i)->getAsString()) { - NewOperands.push_back(Item); - } else { - NewOperands.push_back(RHSo->getOperand(i)); - } - } - - // Now run the operator and use its result as the new list item - OpInit *NewOp = RHSo->clone(NewOperands); - Init *NewItem = NewOp->Fold(CurRec, CurMultiClass); - if (NewItem != NewOp) { - *li = NewItem; - delete NewOp; - } - } - return new ListInit(NewList, MHSl->getType()); - } - } - return 0; -} - -Init *TernOpInit::Fold(Record *CurRec, MultiClass *CurMultiClass) { - switch (getOpcode()) { - default: assert(0 && "Unknown binop"); - case SUBST: { - DefInit *LHSd = dynamic_cast<DefInit*>(LHS); - VarInit *LHSv = dynamic_cast<VarInit*>(LHS); - StringInit *LHSs = dynamic_cast<StringInit*>(LHS); - - DefInit *MHSd = dynamic_cast<DefInit*>(MHS); - VarInit *MHSv = dynamic_cast<VarInit*>(MHS); - StringInit *MHSs = dynamic_cast<StringInit*>(MHS); - - DefInit *RHSd = dynamic_cast<DefInit*>(RHS); - VarInit *RHSv = dynamic_cast<VarInit*>(RHS); - StringInit *RHSs = dynamic_cast<StringInit*>(RHS); - - if ((LHSd && MHSd && RHSd) - || (LHSv && MHSv && RHSv) - || (LHSs && MHSs && RHSs)) { - if (RHSd) { - Record *Val = RHSd->getDef(); - if (LHSd->getAsString() == RHSd->getAsString()) { - Val = MHSd->getDef(); - } - return new DefInit(Val); - } - if (RHSv) { - std::string Val = RHSv->getName(); - if (LHSv->getAsString() == RHSv->getAsString()) { - Val = MHSv->getName(); - } - return new VarInit(Val, getType()); - } - if (RHSs) { - std::string Val = RHSs->getValue(); - - std::string::size_type found; - std::string::size_type idx = 0; - do { - found = Val.find(LHSs->getValue(), idx); - if (found != std::string::npos) { - Val.replace(found, LHSs->getValue().size(), MHSs->getValue()); - } - idx = found + MHSs->getValue().size(); - } while (found != std::string::npos); - - return new StringInit(Val); - } - } - break; - } - - case FOREACH: { - Init *Result = ForeachHelper(LHS, MHS, RHS, getType(), - CurRec, CurMultiClass); - if (Result != 0) { - return Result; - } - break; - } - - case IF: { - IntInit *LHSi = dynamic_cast<IntInit*>(LHS); - if (Init *I = LHS->convertInitializerTo(new IntRecTy())) - LHSi = dynamic_cast<IntInit*>(I); - if (LHSi) { - if (LHSi->getValue()) { - return MHS; - } else { - return RHS; - } - } - break; - } - } - - return this; -} - -Init *TernOpInit::resolveReferences(Record &R, const RecordVal *RV) { - Init *lhs = LHS->resolveReferences(R, RV); - - if (Opc == IF && lhs != LHS) { - IntInit *Value = dynamic_cast<IntInit*>(lhs); - if (Init *I = lhs->convertInitializerTo(new IntRecTy())) - Value = dynamic_cast<IntInit*>(I); - if (Value != 0) { - // Short-circuit - if (Value->getValue()) { - Init *mhs = MHS->resolveReferences(R, RV); - return (new TernOpInit(getOpcode(), lhs, mhs, - RHS, getType()))->Fold(&R, 0); - } else { - Init *rhs = RHS->resolveReferences(R, RV); - return (new TernOpInit(getOpcode(), lhs, MHS, - rhs, getType()))->Fold(&R, 0); - } - } - } - - Init *mhs = MHS->resolveReferences(R, RV); - Init *rhs = RHS->resolveReferences(R, RV); - - if (LHS != lhs || MHS != mhs || RHS != rhs) - return (new TernOpInit(getOpcode(), lhs, mhs, rhs, getType()))->Fold(&R, 0); - return Fold(&R, 0); -} - -std::string TernOpInit::getAsString() const { - std::string Result; - switch (Opc) { - case SUBST: Result = "!subst"; break; - case FOREACH: Result = "!foreach"; break; - case IF: Result = "!if"; break; - } - return Result + "(" + LHS->getAsString() + ", " + MHS->getAsString() + ", " - + RHS->getAsString() + ")"; -} - -RecTy *TypedInit::getFieldType(const std::string &FieldName) const { - RecordRecTy *RecordType = dynamic_cast<RecordRecTy *>(getType()); - if (RecordType) { - RecordVal *Field = RecordType->getRecord()->getValue(FieldName); - if (Field) { - return Field->getType(); - } - } - return 0; -} - -Init *TypedInit::convertInitializerBitRange(const std::vector<unsigned> &Bits) { - BitsRecTy *T = dynamic_cast<BitsRecTy*>(getType()); - if (T == 0) return 0; // Cannot subscript a non-bits variable. - unsigned NumBits = T->getNumBits(); - - BitsInit *BI = new BitsInit(Bits.size()); - for (unsigned i = 0, e = Bits.size(); i != e; ++i) { - if (Bits[i] >= NumBits) { - delete BI; - return 0; - } - BI->setBit(i, new VarBitInit(this, Bits[i])); - } - return BI; -} - -Init *TypedInit::convertInitListSlice(const std::vector<unsigned> &Elements) { - ListRecTy *T = dynamic_cast<ListRecTy*>(getType()); - if (T == 0) return 0; // Cannot subscript a non-list variable. - - if (Elements.size() == 1) - return new VarListElementInit(this, Elements[0]); - - std::vector<Init*> ListInits; - ListInits.reserve(Elements.size()); - for (unsigned i = 0, e = Elements.size(); i != e; ++i) - ListInits.push_back(new VarListElementInit(this, Elements[i])); - return new ListInit(ListInits, T); -} - - -Init *VarInit::resolveBitReference(Record &R, const RecordVal *IRV, - unsigned Bit) { - if (R.isTemplateArg(getName())) return 0; - if (IRV && IRV->getName() != getName()) return 0; - - RecordVal *RV = R.getValue(getName()); - assert(RV && "Reference to a non-existent variable?"); - assert(dynamic_cast<BitsInit*>(RV->getValue())); - BitsInit *BI = (BitsInit*)RV->getValue(); - - assert(Bit < BI->getNumBits() && "Bit reference out of range!"); - Init *B = BI->getBit(Bit); - - // If the bit is set to some value, or if we are resolving a reference to a - // specific variable and that variable is explicitly unset, then replace the - // VarBitInit with it. - if (IRV || !dynamic_cast<UnsetInit*>(B)) - return B; - return 0; -} - -Init *VarInit::resolveListElementReference(Record &R, const RecordVal *IRV, - unsigned Elt) { - if (R.isTemplateArg(getName())) return 0; - if (IRV && IRV->getName() != getName()) return 0; - - RecordVal *RV = R.getValue(getName()); - assert(RV && "Reference to a non-existent variable?"); - ListInit *LI = dynamic_cast<ListInit*>(RV->getValue()); - if (!LI) { - VarInit *VI = dynamic_cast<VarInit*>(RV->getValue()); - assert(VI && "Invalid list element!"); - return new VarListElementInit(VI, Elt); - } - - if (Elt >= LI->getSize()) - return 0; // Out of range reference. - Init *E = LI->getElement(Elt); - // If the element is set to some value, or if we are resolving a reference - // to a specific variable and that variable is explicitly unset, then - // replace the VarListElementInit with it. - if (IRV || !dynamic_cast<UnsetInit*>(E)) - return E; - return 0; -} - - -RecTy *VarInit::getFieldType(const std::string &FieldName) const { - if (RecordRecTy *RTy = dynamic_cast<RecordRecTy*>(getType())) - if (const RecordVal *RV = RTy->getRecord()->getValue(FieldName)) - return RV->getType(); - return 0; -} - -Init *VarInit::getFieldInit(Record &R, const RecordVal *RV, - const std::string &FieldName) const { - if (dynamic_cast<RecordRecTy*>(getType())) - if (const RecordVal *Val = R.getValue(VarName)) { - if (RV != Val && (RV || dynamic_cast<UnsetInit*>(Val->getValue()))) - return 0; - Init *TheInit = Val->getValue(); - assert(TheInit != this && "Infinite loop detected!"); - if (Init *I = TheInit->getFieldInit(R, RV, FieldName)) - return I; - else - return 0; - } - return 0; -} - -/// resolveReferences - This method is used by classes that refer to other -/// variables which may not be defined at the time the expression is formed. -/// If a value is set for the variable later, this method will be called on -/// users of the value to allow the value to propagate out. -/// -Init *VarInit::resolveReferences(Record &R, const RecordVal *RV) { - if (RecordVal *Val = R.getValue(VarName)) - if (RV == Val || (RV == 0 && !dynamic_cast<UnsetInit*>(Val->getValue()))) - return Val->getValue(); - return this; -} - -std::string VarBitInit::getAsString() const { - return TI->getAsString() + "{" + utostr(Bit) + "}"; -} - -Init *VarBitInit::resolveReferences(Record &R, const RecordVal *RV) { - if (Init *I = getVariable()->resolveBitReference(R, RV, getBitNum())) - return I; - return this; -} - -std::string VarListElementInit::getAsString() const { - return TI->getAsString() + "[" + utostr(Element) + "]"; -} - -Init *VarListElementInit::resolveReferences(Record &R, const RecordVal *RV) { - if (Init *I = getVariable()->resolveListElementReference(R, RV, - getElementNum())) - return I; - return this; -} - -Init *VarListElementInit::resolveBitReference(Record &R, const RecordVal *RV, - unsigned Bit) { - // FIXME: This should be implemented, to support references like: - // bit B = AA[0]{1}; - return 0; -} - -Init *VarListElementInit:: -resolveListElementReference(Record &R, const RecordVal *RV, unsigned Elt) { - // FIXME: This should be implemented, to support references like: - // int B = AA[0][1]; - return 0; -} - -RecTy *DefInit::getFieldType(const std::string &FieldName) const { - if (const RecordVal *RV = Def->getValue(FieldName)) - return RV->getType(); - return 0; -} - -Init *DefInit::getFieldInit(Record &R, const RecordVal *RV, - const std::string &FieldName) const { - return Def->getValue(FieldName)->getValue(); -} - - -std::string DefInit::getAsString() const { - return Def->getName(); -} - -Init *FieldInit::resolveBitReference(Record &R, const RecordVal *RV, - unsigned Bit) { - if (Init *BitsVal = Rec->getFieldInit(R, RV, FieldName)) - if (BitsInit *BI = dynamic_cast<BitsInit*>(BitsVal)) { - assert(Bit < BI->getNumBits() && "Bit reference out of range!"); - Init *B = BI->getBit(Bit); - - if (dynamic_cast<BitInit*>(B)) // If the bit is set. - return B; // Replace the VarBitInit with it. - } - return 0; -} - -Init *FieldInit::resolveListElementReference(Record &R, const RecordVal *RV, - unsigned Elt) { - if (Init *ListVal = Rec->getFieldInit(R, RV, FieldName)) - if (ListInit *LI = dynamic_cast<ListInit*>(ListVal)) { - if (Elt >= LI->getSize()) return 0; - Init *E = LI->getElement(Elt); - - // If the element is set to some value, or if we are resolving a - // reference to a specific variable and that variable is explicitly - // unset, then replace the VarListElementInit with it. - if (RV || !dynamic_cast<UnsetInit*>(E)) - return E; - } - return 0; -} - -Init *FieldInit::resolveReferences(Record &R, const RecordVal *RV) { - Init *NewRec = RV ? Rec->resolveReferences(R, RV) : Rec; - - Init *BitsVal = NewRec->getFieldInit(R, RV, FieldName); - if (BitsVal) { - Init *BVR = BitsVal->resolveReferences(R, RV); - return BVR->isComplete() ? BVR : this; - } - - if (NewRec != Rec) { - return new FieldInit(NewRec, FieldName); - } - return this; -} - -Init *DagInit::resolveReferences(Record &R, const RecordVal *RV) { - std::vector<Init*> NewArgs; - for (unsigned i = 0, e = Args.size(); i != e; ++i) - NewArgs.push_back(Args[i]->resolveReferences(R, RV)); - - Init *Op = Val->resolveReferences(R, RV); - - if (Args != NewArgs || Op != Val) - return new DagInit(Op, ValName, NewArgs, ArgNames); - - return this; -} - - -std::string DagInit::getAsString() const { - std::string Result = "(" + Val->getAsString(); - if (!ValName.empty()) - Result += ":" + ValName; - if (Args.size()) { - Result += " " + Args[0]->getAsString(); - if (!ArgNames[0].empty()) Result += ":$" + ArgNames[0]; - for (unsigned i = 1, e = Args.size(); i != e; ++i) { - Result += ", " + Args[i]->getAsString(); - if (!ArgNames[i].empty()) Result += ":$" + ArgNames[i]; - } - } - return Result + ")"; -} - - -//===----------------------------------------------------------------------===// -// Other implementations -//===----------------------------------------------------------------------===// - -RecordVal::RecordVal(const std::string &N, RecTy *T, unsigned P) - : Name(N), Ty(T), Prefix(P) { - Value = Ty->convertValue(new UnsetInit()); - assert(Value && "Cannot create unset value for current type!"); -} - -void RecordVal::dump() const { errs() << *this; } - -void RecordVal::print(raw_ostream &OS, bool PrintSem) const { - if (getPrefix()) OS << "field "; - OS << *getType() << " " << getName(); - - if (getValue()) - OS << " = " << *getValue(); - - if (PrintSem) OS << ";\n"; -} - -unsigned Record::LastID = 0; - -void Record::setName(const std::string &Name) { - if (TrackedRecords.getDef(getName()) == this) { - TrackedRecords.removeDef(getName()); - this->Name = Name; - TrackedRecords.addDef(this); - } else { - TrackedRecords.removeClass(getName()); - this->Name = Name; - TrackedRecords.addClass(this); - } -} - -/// resolveReferencesTo - If anything in this record refers to RV, replace the -/// reference to RV with the RHS of RV. If RV is null, we resolve all possible -/// references. -void Record::resolveReferencesTo(const RecordVal *RV) { - for (unsigned i = 0, e = Values.size(); i != e; ++i) { - if (Init *V = Values[i].getValue()) - Values[i].setValue(V->resolveReferences(*this, RV)); - } -} - -void Record::dump() const { errs() << *this; } - -raw_ostream &llvm::operator<<(raw_ostream &OS, const Record &R) { - OS << R.getName(); - - const std::vector<std::string> &TArgs = R.getTemplateArgs(); - if (!TArgs.empty()) { - OS << "<"; - for (unsigned i = 0, e = TArgs.size(); i != e; ++i) { - if (i) OS << ", "; - const RecordVal *RV = R.getValue(TArgs[i]); - assert(RV && "Template argument record not found??"); - RV->print(OS, false); - } - OS << ">"; - } - - OS << " {"; - const std::vector<Record*> &SC = R.getSuperClasses(); - if (!SC.empty()) { - OS << "\t//"; - for (unsigned i = 0, e = SC.size(); i != e; ++i) - OS << " " << SC[i]->getName(); - } - OS << "\n"; - - const std::vector<RecordVal> &Vals = R.getValues(); - for (unsigned i = 0, e = Vals.size(); i != e; ++i) - if (Vals[i].getPrefix() && !R.isTemplateArg(Vals[i].getName())) - OS << Vals[i]; - for (unsigned i = 0, e = Vals.size(); i != e; ++i) - if (!Vals[i].getPrefix() && !R.isTemplateArg(Vals[i].getName())) - OS << Vals[i]; - - return OS << "}\n"; -} - -/// getValueInit - Return the initializer for a value with the specified name, -/// or throw an exception if the field does not exist. -/// -Init *Record::getValueInit(StringRef FieldName) const { - const RecordVal *R = getValue(FieldName); - if (R == 0 || R->getValue() == 0) - throw "Record `" + getName() + "' does not have a field named `" + - FieldName.str() + "'!\n"; - return R->getValue(); -} - - -/// getValueAsString - This method looks up the specified field and returns its -/// value as a string, throwing an exception if the field does not exist or if -/// the value is not a string. -/// -std::string Record::getValueAsString(StringRef FieldName) const { - const RecordVal *R = getValue(FieldName); - if (R == 0 || R->getValue() == 0) - throw "Record `" + getName() + "' does not have a field named `" + - FieldName.str() + "'!\n"; - - if (const StringInit *SI = dynamic_cast<const StringInit*>(R->getValue())) - return SI->getValue(); - throw "Record `" + getName() + "', field `" + FieldName.str() + - "' does not have a string initializer!"; -} - -/// getValueAsBitsInit - This method looks up the specified field and returns -/// its value as a BitsInit, throwing an exception if the field does not exist -/// or if the value is not the right type. -/// -BitsInit *Record::getValueAsBitsInit(StringRef FieldName) const { - const RecordVal *R = getValue(FieldName); - if (R == 0 || R->getValue() == 0) - throw "Record `" + getName() + "' does not have a field named `" + - FieldName.str() + "'!\n"; - - if (BitsInit *BI = dynamic_cast<BitsInit*>(R->getValue())) - return BI; - throw "Record `" + getName() + "', field `" + FieldName.str() + - "' does not have a BitsInit initializer!"; -} - -/// getValueAsListInit - This method looks up the specified field and returns -/// its value as a ListInit, throwing an exception if the field does not exist -/// or if the value is not the right type. -/// -ListInit *Record::getValueAsListInit(StringRef FieldName) const { - const RecordVal *R = getValue(FieldName); - if (R == 0 || R->getValue() == 0) - throw "Record `" + getName() + "' does not have a field named `" + - FieldName.str() + "'!\n"; - - if (ListInit *LI = dynamic_cast<ListInit*>(R->getValue())) - return LI; - throw "Record `" + getName() + "', field `" + FieldName.str() + - "' does not have a list initializer!"; -} - -/// getValueAsListOfDefs - This method looks up the specified field and returns -/// its value as a vector of records, throwing an exception if the field does -/// not exist or if the value is not the right type. -/// -std::vector<Record*> -Record::getValueAsListOfDefs(StringRef FieldName) const { - ListInit *List = getValueAsListInit(FieldName); - std::vector<Record*> Defs; - for (unsigned i = 0; i < List->getSize(); i++) { - if (DefInit *DI = dynamic_cast<DefInit*>(List->getElement(i))) { - Defs.push_back(DI->getDef()); - } else { - throw "Record `" + getName() + "', field `" + FieldName.str() + - "' list is not entirely DefInit!"; - } - } - return Defs; -} - -/// getValueAsInt - This method looks up the specified field and returns its -/// value as an int64_t, throwing an exception if the field does not exist or if -/// the value is not the right type. -/// -int64_t Record::getValueAsInt(StringRef FieldName) const { - const RecordVal *R = getValue(FieldName); - if (R == 0 || R->getValue() == 0) - throw "Record `" + getName() + "' does not have a field named `" + - FieldName.str() + "'!\n"; - - if (IntInit *II = dynamic_cast<IntInit*>(R->getValue())) - return II->getValue(); - throw "Record `" + getName() + "', field `" + FieldName.str() + - "' does not have an int initializer!"; -} - -/// getValueAsListOfInts - This method looks up the specified field and returns -/// its value as a vector of integers, throwing an exception if the field does -/// not exist or if the value is not the right type. -/// -std::vector<int64_t> -Record::getValueAsListOfInts(StringRef FieldName) const { - ListInit *List = getValueAsListInit(FieldName); - std::vector<int64_t> Ints; - for (unsigned i = 0; i < List->getSize(); i++) { - if (IntInit *II = dynamic_cast<IntInit*>(List->getElement(i))) { - Ints.push_back(II->getValue()); - } else { - throw "Record `" + getName() + "', field `" + FieldName.str() + - "' does not have a list of ints initializer!"; - } - } - return Ints; -} - -/// getValueAsListOfStrings - This method looks up the specified field and -/// returns its value as a vector of strings, throwing an exception if the -/// field does not exist or if the value is not the right type. -/// -std::vector<std::string> -Record::getValueAsListOfStrings(StringRef FieldName) const { - ListInit *List = getValueAsListInit(FieldName); - std::vector<std::string> Strings; - for (unsigned i = 0; i < List->getSize(); i++) { - if (StringInit *II = dynamic_cast<StringInit*>(List->getElement(i))) { - Strings.push_back(II->getValue()); - } else { - throw "Record `" + getName() + "', field `" + FieldName.str() + - "' does not have a list of strings initializer!"; - } - } - return Strings; -} - -/// getValueAsDef - This method looks up the specified field and returns its -/// value as a Record, throwing an exception if the field does not exist or if -/// the value is not the right type. -/// -Record *Record::getValueAsDef(StringRef FieldName) const { - const RecordVal *R = getValue(FieldName); - if (R == 0 || R->getValue() == 0) - throw "Record `" + getName() + "' does not have a field named `" + - FieldName.str() + "'!\n"; - - if (DefInit *DI = dynamic_cast<DefInit*>(R->getValue())) - return DI->getDef(); - throw "Record `" + getName() + "', field `" + FieldName.str() + - "' does not have a def initializer!"; -} - -/// getValueAsBit - This method looks up the specified field and returns its -/// value as a bit, throwing an exception if the field does not exist or if -/// the value is not the right type. -/// -bool Record::getValueAsBit(StringRef FieldName) const { - const RecordVal *R = getValue(FieldName); - if (R == 0 || R->getValue() == 0) - throw "Record `" + getName() + "' does not have a field named `" + - FieldName.str() + "'!\n"; - - if (BitInit *BI = dynamic_cast<BitInit*>(R->getValue())) - return BI->getValue(); - throw "Record `" + getName() + "', field `" + FieldName.str() + - "' does not have a bit initializer!"; -} - -/// getValueAsDag - This method looks up the specified field and returns its -/// value as an Dag, throwing an exception if the field does not exist or if -/// the value is not the right type. -/// -DagInit *Record::getValueAsDag(StringRef FieldName) const { - const RecordVal *R = getValue(FieldName); - if (R == 0 || R->getValue() == 0) - throw "Record `" + getName() + "' does not have a field named `" + - FieldName.str() + "'!\n"; - - if (DagInit *DI = dynamic_cast<DagInit*>(R->getValue())) - return DI; - throw "Record `" + getName() + "', field `" + FieldName.str() + - "' does not have a dag initializer!"; -} - -std::string Record::getValueAsCode(StringRef FieldName) const { - const RecordVal *R = getValue(FieldName); - if (R == 0 || R->getValue() == 0) - throw "Record `" + getName() + "' does not have a field named `" + - FieldName.str() + "'!\n"; - - if (const CodeInit *CI = dynamic_cast<const CodeInit*>(R->getValue())) - return CI->getValue(); - throw "Record `" + getName() + "', field `" + FieldName.str() + - "' does not have a code initializer!"; -} - - -void MultiClass::dump() const { - errs() << "Record:\n"; - Rec.dump(); - - errs() << "Defs:\n"; - for (RecordVector::const_iterator r = DefPrototypes.begin(), - rend = DefPrototypes.end(); - r != rend; - ++r) { - (*r)->dump(); - } -} - - -void RecordKeeper::dump() const { errs() << *this; } - -raw_ostream &llvm::operator<<(raw_ostream &OS, const RecordKeeper &RK) { - OS << "------------- Classes -----------------\n"; - const std::map<std::string, Record*> &Classes = RK.getClasses(); - for (std::map<std::string, Record*>::const_iterator I = Classes.begin(), - E = Classes.end(); I != E; ++I) - OS << "class " << *I->second; - - OS << "------------- Defs -----------------\n"; - const std::map<std::string, Record*> &Defs = RK.getDefs(); - for (std::map<std::string, Record*>::const_iterator I = Defs.begin(), - E = Defs.end(); I != E; ++I) - OS << "def " << *I->second; - return OS; -} - - -/// getAllDerivedDefinitions - This method returns all concrete definitions -/// that derive from the specified class name. If a class with the specified -/// name does not exist, an error is printed and true is returned. -std::vector<Record*> -RecordKeeper::getAllDerivedDefinitions(const std::string &ClassName) const { - Record *Class = getClass(ClassName); - if (!Class) - throw "ERROR: Couldn't find the `" + ClassName + "' class!\n"; - - std::vector<Record*> Defs; - for (std::map<std::string, Record*>::const_iterator I = getDefs().begin(), - E = getDefs().end(); I != E; ++I) - if (I->second->isSubClassOf(Class)) - Defs.push_back(I->second); - - return Defs; -} - diff --git a/utils/TableGen/Record.h b/utils/TableGen/Record.h deleted file mode 100644 index 2f4080b..0000000 --- a/utils/TableGen/Record.h +++ /dev/null @@ -1,1500 +0,0 @@ -//===- Record.h - Classes to represent Table Records ------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines the main TableGen data structures, including the TableGen -// types, values, and high-level data structures. -// -//===----------------------------------------------------------------------===// - -#ifndef RECORD_H -#define RECORD_H - -#include "llvm/Support/SourceMgr.h" -#include "llvm/Support/DataTypes.h" -#include "llvm/Support/raw_ostream.h" -#include <map> - -namespace llvm { -class raw_ostream; - -// RecTy subclasses. -class BitRecTy; -class BitsRecTy; -class IntRecTy; -class StringRecTy; -class ListRecTy; -class CodeRecTy; -class DagRecTy; -class RecordRecTy; - -// Init subclasses. -class Init; -class UnsetInit; -class BitInit; -class BitsInit; -class IntInit; -class StringInit; -class CodeInit; -class ListInit; -class UnOpInit; -class BinOpInit; -class TernOpInit; -class DefInit; -class DagInit; -class TypedInit; -class VarInit; -class FieldInit; -class VarBitInit; -class VarListElementInit; - -// Other classes. -class Record; -class RecordVal; -struct MultiClass; -class RecordKeeper; - -//===----------------------------------------------------------------------===// -// Type Classes -//===----------------------------------------------------------------------===// - -struct RecTy { - virtual ~RecTy() {} - - virtual std::string getAsString() const = 0; - void print(raw_ostream &OS) const { OS << getAsString(); } - void dump() const; - - /// typeIsConvertibleTo - Return true if all values of 'this' type can be - /// converted to the specified type. - virtual bool typeIsConvertibleTo(const RecTy *RHS) const = 0; - -public: // These methods should only be called from subclasses of Init - virtual Init *convertValue( UnsetInit *UI) { return 0; } - virtual Init *convertValue( BitInit *BI) { return 0; } - virtual Init *convertValue( BitsInit *BI) { return 0; } - virtual Init *convertValue( IntInit *II) { return 0; } - virtual Init *convertValue(StringInit *SI) { return 0; } - virtual Init *convertValue( ListInit *LI) { return 0; } - virtual Init *convertValue( UnOpInit *UI) { - return convertValue((TypedInit*)UI); - } - virtual Init *convertValue( BinOpInit *UI) { - return convertValue((TypedInit*)UI); - } - virtual Init *convertValue( TernOpInit *UI) { - return convertValue((TypedInit*)UI); - } - virtual Init *convertValue( CodeInit *CI) { return 0; } - virtual Init *convertValue(VarBitInit *VB) { return 0; } - virtual Init *convertValue( DefInit *DI) { return 0; } - virtual Init *convertValue( DagInit *DI) { return 0; } - virtual Init *convertValue( TypedInit *TI) { return 0; } - virtual Init *convertValue( VarInit *VI) { - return convertValue((TypedInit*)VI); - } - virtual Init *convertValue( FieldInit *FI) { - return convertValue((TypedInit*)FI); - } - -public: // These methods should only be called by subclasses of RecTy. - // baseClassOf - These virtual methods should be overloaded to return true iff - // all values of type 'RHS' can be converted to the 'this' type. - virtual bool baseClassOf(const BitRecTy *RHS) const { return false; } - virtual bool baseClassOf(const BitsRecTy *RHS) const { return false; } - virtual bool baseClassOf(const IntRecTy *RHS) const { return false; } - virtual bool baseClassOf(const StringRecTy *RHS) const { return false; } - virtual bool baseClassOf(const ListRecTy *RHS) const { return false; } - virtual bool baseClassOf(const CodeRecTy *RHS) const { return false; } - virtual bool baseClassOf(const DagRecTy *RHS) const { return false; } - virtual bool baseClassOf(const RecordRecTy *RHS) const { return false; } -}; - -inline raw_ostream &operator<<(raw_ostream &OS, const RecTy &Ty) { - Ty.print(OS); - return OS; -} - - -/// BitRecTy - 'bit' - Represent a single bit -/// -class BitRecTy : public RecTy { -public: - virtual Init *convertValue( UnsetInit *UI) { return (Init*)UI; } - virtual Init *convertValue( BitInit *BI) { return (Init*)BI; } - virtual Init *convertValue( BitsInit *BI); - virtual Init *convertValue( IntInit *II); - virtual Init *convertValue(StringInit *SI) { return 0; } - virtual Init *convertValue( ListInit *LI) { return 0; } - virtual Init *convertValue( CodeInit *CI) { return 0; } - virtual Init *convertValue(VarBitInit *VB) { return (Init*)VB; } - virtual Init *convertValue( DefInit *DI) { return 0; } - virtual Init *convertValue( DagInit *DI) { return 0; } - virtual Init *convertValue( UnOpInit *UI) { return RecTy::convertValue(UI);} - virtual Init *convertValue( BinOpInit *UI) { return RecTy::convertValue(UI);} - virtual Init *convertValue( TernOpInit *UI) { return RecTy::convertValue(UI);} - virtual Init *convertValue( TypedInit *TI); - virtual Init *convertValue( VarInit *VI) { return RecTy::convertValue(VI);} - virtual Init *convertValue( FieldInit *FI) { return RecTy::convertValue(FI);} - - std::string getAsString() const { return "bit"; } - - bool typeIsConvertibleTo(const RecTy *RHS) const { - return RHS->baseClassOf(this); - } - virtual bool baseClassOf(const BitRecTy *RHS) const { return true; } - virtual bool baseClassOf(const BitsRecTy *RHS) const; - virtual bool baseClassOf(const IntRecTy *RHS) const { return true; } - virtual bool baseClassOf(const StringRecTy *RHS) const { return false; } - virtual bool baseClassOf(const ListRecTy *RHS) const { return false; } - virtual bool baseClassOf(const CodeRecTy *RHS) const { return false; } - virtual bool baseClassOf(const DagRecTy *RHS) const { return false; } - virtual bool baseClassOf(const RecordRecTy *RHS) const { return false; } - -}; - - -// BitsRecTy - 'bits<n>' - Represent a fixed number of bits -/// BitsRecTy - 'bits<n>' - Represent a fixed number of bits -/// -class BitsRecTy : public RecTy { - unsigned Size; -public: - explicit BitsRecTy(unsigned Sz) : Size(Sz) {} - - unsigned getNumBits() const { return Size; } - - virtual Init *convertValue( UnsetInit *UI); - virtual Init *convertValue( BitInit *UI); - virtual Init *convertValue( BitsInit *BI); - virtual Init *convertValue( IntInit *II); - virtual Init *convertValue(StringInit *SI) { return 0; } - virtual Init *convertValue( ListInit *LI) { return 0; } - virtual Init *convertValue( CodeInit *CI) { return 0; } - virtual Init *convertValue(VarBitInit *VB) { return 0; } - virtual Init *convertValue( DefInit *DI) { return 0; } - virtual Init *convertValue( DagInit *DI) { return 0; } - virtual Init *convertValue( UnOpInit *UI) { return RecTy::convertValue(UI);} - virtual Init *convertValue( BinOpInit *UI) { return RecTy::convertValue(UI);} - virtual Init *convertValue( TernOpInit *UI) { return RecTy::convertValue(UI);} - virtual Init *convertValue( TypedInit *TI); - virtual Init *convertValue( VarInit *VI) { return RecTy::convertValue(VI);} - virtual Init *convertValue( FieldInit *FI) { return RecTy::convertValue(FI);} - - std::string getAsString() const; - - bool typeIsConvertibleTo(const RecTy *RHS) const { - return RHS->baseClassOf(this); - } - virtual bool baseClassOf(const BitRecTy *RHS) const { return Size == 1; } - virtual bool baseClassOf(const BitsRecTy *RHS) const { - return RHS->Size == Size; - } - virtual bool baseClassOf(const IntRecTy *RHS) const { return true; } - virtual bool baseClassOf(const StringRecTy *RHS) const { return false; } - virtual bool baseClassOf(const ListRecTy *RHS) const { return false; } - virtual bool baseClassOf(const CodeRecTy *RHS) const { return false; } - virtual bool baseClassOf(const DagRecTy *RHS) const { return false; } - virtual bool baseClassOf(const RecordRecTy *RHS) const { return false; } - -}; - - -/// IntRecTy - 'int' - Represent an integer value of no particular size -/// -class IntRecTy : public RecTy { -public: - virtual Init *convertValue( UnsetInit *UI) { return (Init*)UI; } - virtual Init *convertValue( BitInit *BI); - virtual Init *convertValue( BitsInit *BI); - virtual Init *convertValue( IntInit *II) { return (Init*)II; } - virtual Init *convertValue(StringInit *SI) { return 0; } - virtual Init *convertValue( ListInit *LI) { return 0; } - virtual Init *convertValue( CodeInit *CI) { return 0; } - virtual Init *convertValue(VarBitInit *VB) { return 0; } - virtual Init *convertValue( DefInit *DI) { return 0; } - virtual Init *convertValue( DagInit *DI) { return 0; } - virtual Init *convertValue( UnOpInit *UI) { return RecTy::convertValue(UI);} - virtual Init *convertValue( BinOpInit *UI) { return RecTy::convertValue(UI);} - virtual Init *convertValue( TernOpInit *UI) { return RecTy::convertValue(UI);} - virtual Init *convertValue( TypedInit *TI); - virtual Init *convertValue( VarInit *VI) { return RecTy::convertValue(VI);} - virtual Init *convertValue( FieldInit *FI) { return RecTy::convertValue(FI);} - - std::string getAsString() const { return "int"; } - - bool typeIsConvertibleTo(const RecTy *RHS) const { - return RHS->baseClassOf(this); - } - - virtual bool baseClassOf(const BitRecTy *RHS) const { return true; } - virtual bool baseClassOf(const BitsRecTy *RHS) const { return true; } - virtual bool baseClassOf(const IntRecTy *RHS) const { return true; } - virtual bool baseClassOf(const StringRecTy *RHS) const { return false; } - virtual bool baseClassOf(const ListRecTy *RHS) const { return false; } - virtual bool baseClassOf(const CodeRecTy *RHS) const { return false; } - virtual bool baseClassOf(const DagRecTy *RHS) const { return false; } - virtual bool baseClassOf(const RecordRecTy *RHS) const { return false; } - -}; - -/// StringRecTy - 'string' - Represent an string value -/// -class StringRecTy : public RecTy { -public: - virtual Init *convertValue( UnsetInit *UI) { return (Init*)UI; } - virtual Init *convertValue( BitInit *BI) { return 0; } - virtual Init *convertValue( BitsInit *BI) { return 0; } - virtual Init *convertValue( IntInit *II) { return 0; } - virtual Init *convertValue(StringInit *SI) { return (Init*)SI; } - virtual Init *convertValue( ListInit *LI) { return 0; } - virtual Init *convertValue( UnOpInit *BO); - virtual Init *convertValue( BinOpInit *BO); - virtual Init *convertValue( TernOpInit *BO) { return RecTy::convertValue(BO);} - - virtual Init *convertValue( CodeInit *CI) { return 0; } - virtual Init *convertValue(VarBitInit *VB) { return 0; } - virtual Init *convertValue( DefInit *DI) { return 0; } - virtual Init *convertValue( DagInit *DI) { return 0; } - virtual Init *convertValue( TypedInit *TI); - virtual Init *convertValue( VarInit *VI) { return RecTy::convertValue(VI);} - virtual Init *convertValue( FieldInit *FI) { return RecTy::convertValue(FI);} - - std::string getAsString() const { return "string"; } - - bool typeIsConvertibleTo(const RecTy *RHS) const { - return RHS->baseClassOf(this); - } - - virtual bool baseClassOf(const BitRecTy *RHS) const { return false; } - virtual bool baseClassOf(const BitsRecTy *RHS) const { return false; } - virtual bool baseClassOf(const IntRecTy *RHS) const { return false; } - virtual bool baseClassOf(const StringRecTy *RHS) const { return true; } - virtual bool baseClassOf(const ListRecTy *RHS) const { return false; } - virtual bool baseClassOf(const CodeRecTy *RHS) const { return false; } - virtual bool baseClassOf(const DagRecTy *RHS) const { return false; } - virtual bool baseClassOf(const RecordRecTy *RHS) const { return false; } -}; - -// ListRecTy - 'list<Ty>' - Represent a list of values, all of which must be of -// the specified type. -/// ListRecTy - 'list<Ty>' - Represent a list of values, all of which must -/// be of the specified type. -/// -class ListRecTy : public RecTy { - RecTy *Ty; -public: - explicit ListRecTy(RecTy *T) : Ty(T) {} - - RecTy *getElementType() const { return Ty; } - - virtual Init *convertValue( UnsetInit *UI) { return (Init*)UI; } - virtual Init *convertValue( BitInit *BI) { return 0; } - virtual Init *convertValue( BitsInit *BI) { return 0; } - virtual Init *convertValue( IntInit *II) { return 0; } - virtual Init *convertValue(StringInit *SI) { return 0; } - virtual Init *convertValue( ListInit *LI); - virtual Init *convertValue( CodeInit *CI) { return 0; } - virtual Init *convertValue(VarBitInit *VB) { return 0; } - virtual Init *convertValue( DefInit *DI) { return 0; } - virtual Init *convertValue( DagInit *DI) { return 0; } - virtual Init *convertValue( UnOpInit *UI) { return RecTy::convertValue(UI);} - virtual Init *convertValue( BinOpInit *UI) { return RecTy::convertValue(UI);} - virtual Init *convertValue( TernOpInit *UI) { return RecTy::convertValue(UI);} - virtual Init *convertValue( TypedInit *TI); - virtual Init *convertValue( VarInit *VI) { return RecTy::convertValue(VI);} - virtual Init *convertValue( FieldInit *FI) { return RecTy::convertValue(FI);} - - std::string getAsString() const; - - bool typeIsConvertibleTo(const RecTy *RHS) const { - return RHS->baseClassOf(this); - } - - virtual bool baseClassOf(const BitRecTy *RHS) const { return false; } - virtual bool baseClassOf(const BitsRecTy *RHS) const { return false; } - virtual bool baseClassOf(const IntRecTy *RHS) const { return false; } - virtual bool baseClassOf(const StringRecTy *RHS) const { return false; } - virtual bool baseClassOf(const ListRecTy *RHS) const { - return RHS->getElementType()->typeIsConvertibleTo(Ty); - } - virtual bool baseClassOf(const CodeRecTy *RHS) const { return false; } - virtual bool baseClassOf(const DagRecTy *RHS) const { return false; } - virtual bool baseClassOf(const RecordRecTy *RHS) const { return false; } -}; - -/// CodeRecTy - 'code' - Represent an code fragment, function or method. -/// -class CodeRecTy : public RecTy { -public: - virtual Init *convertValue( UnsetInit *UI) { return (Init*)UI; } - virtual Init *convertValue( BitInit *BI) { return 0; } - virtual Init *convertValue( BitsInit *BI) { return 0; } - virtual Init *convertValue( IntInit *II) { return 0; } - virtual Init *convertValue(StringInit *SI) { return 0; } - virtual Init *convertValue( ListInit *LI) { return 0; } - virtual Init *convertValue( CodeInit *CI) { return (Init*)CI; } - virtual Init *convertValue(VarBitInit *VB) { return 0; } - virtual Init *convertValue( DefInit *DI) { return 0; } - virtual Init *convertValue( DagInit *DI) { return 0; } - virtual Init *convertValue( UnOpInit *UI) { return RecTy::convertValue(UI);} - virtual Init *convertValue( BinOpInit *UI) { return RecTy::convertValue(UI);} - virtual Init *convertValue( TernOpInit *UI) { return RecTy::convertValue(UI);} - virtual Init *convertValue( TypedInit *TI); - virtual Init *convertValue( VarInit *VI) { return RecTy::convertValue(VI);} - virtual Init *convertValue( FieldInit *FI) { return RecTy::convertValue(FI);} - - std::string getAsString() const { return "code"; } - - bool typeIsConvertibleTo(const RecTy *RHS) const { - return RHS->baseClassOf(this); - } - virtual bool baseClassOf(const BitRecTy *RHS) const { return false; } - virtual bool baseClassOf(const BitsRecTy *RHS) const { return false; } - virtual bool baseClassOf(const IntRecTy *RHS) const { return false; } - virtual bool baseClassOf(const StringRecTy *RHS) const { return false; } - virtual bool baseClassOf(const ListRecTy *RHS) const { return false; } - virtual bool baseClassOf(const CodeRecTy *RHS) const { return true; } - virtual bool baseClassOf(const DagRecTy *RHS) const { return false; } - virtual bool baseClassOf(const RecordRecTy *RHS) const { return false; } -}; - -/// DagRecTy - 'dag' - Represent a dag fragment -/// -class DagRecTy : public RecTy { -public: - virtual Init *convertValue( UnsetInit *UI) { return (Init*)UI; } - virtual Init *convertValue( BitInit *BI) { return 0; } - virtual Init *convertValue( BitsInit *BI) { return 0; } - virtual Init *convertValue( IntInit *II) { return 0; } - virtual Init *convertValue(StringInit *SI) { return 0; } - virtual Init *convertValue( ListInit *LI) { return 0; } - virtual Init *convertValue( CodeInit *CI) { return 0; } - virtual Init *convertValue(VarBitInit *VB) { return 0; } - virtual Init *convertValue( DefInit *DI) { return 0; } - virtual Init *convertValue( UnOpInit *BO); - virtual Init *convertValue( BinOpInit *BO); - virtual Init *convertValue( TernOpInit *BO) { return RecTy::convertValue(BO);} - virtual Init *convertValue( DagInit *CI) { return (Init*)CI; } - virtual Init *convertValue( TypedInit *TI); - virtual Init *convertValue( VarInit *VI) { return RecTy::convertValue(VI);} - virtual Init *convertValue( FieldInit *FI) { return RecTy::convertValue(FI);} - - std::string getAsString() const { return "dag"; } - - bool typeIsConvertibleTo(const RecTy *RHS) const { - return RHS->baseClassOf(this); - } - - virtual bool baseClassOf(const BitRecTy *RHS) const { return false; } - virtual bool baseClassOf(const BitsRecTy *RHS) const { return false; } - virtual bool baseClassOf(const IntRecTy *RHS) const { return false; } - virtual bool baseClassOf(const StringRecTy *RHS) const { return false; } - virtual bool baseClassOf(const ListRecTy *RHS) const { return false; } - virtual bool baseClassOf(const CodeRecTy *RHS) const { return false; } - virtual bool baseClassOf(const DagRecTy *RHS) const { return true; } - virtual bool baseClassOf(const RecordRecTy *RHS) const { return false; } -}; - - -/// RecordRecTy - '[classname]' - Represent an instance of a class, such as: -/// (R32 X = EAX). -/// -class RecordRecTy : public RecTy { - Record *Rec; -public: - explicit RecordRecTy(Record *R) : Rec(R) {} - - Record *getRecord() const { return Rec; } - - virtual Init *convertValue( UnsetInit *UI) { return (Init*)UI; } - virtual Init *convertValue( BitInit *BI) { return 0; } - virtual Init *convertValue( BitsInit *BI) { return 0; } - virtual Init *convertValue( IntInit *II) { return 0; } - virtual Init *convertValue(StringInit *SI) { return 0; } - virtual Init *convertValue( ListInit *LI) { return 0; } - virtual Init *convertValue( CodeInit *CI) { return 0; } - virtual Init *convertValue(VarBitInit *VB) { return 0; } - virtual Init *convertValue( UnOpInit *UI) { return RecTy::convertValue(UI);} - virtual Init *convertValue( BinOpInit *UI) { return RecTy::convertValue(UI);} - virtual Init *convertValue( TernOpInit *UI) { return RecTy::convertValue(UI);} - virtual Init *convertValue( DefInit *DI); - virtual Init *convertValue( DagInit *DI) { return 0; } - virtual Init *convertValue( TypedInit *VI); - virtual Init *convertValue( VarInit *VI) { return RecTy::convertValue(VI);} - virtual Init *convertValue( FieldInit *FI) { return RecTy::convertValue(FI);} - - std::string getAsString() const; - - bool typeIsConvertibleTo(const RecTy *RHS) const { - return RHS->baseClassOf(this); - } - virtual bool baseClassOf(const BitRecTy *RHS) const { return false; } - virtual bool baseClassOf(const BitsRecTy *RHS) const { return false; } - virtual bool baseClassOf(const IntRecTy *RHS) const { return false; } - virtual bool baseClassOf(const StringRecTy *RHS) const { return false; } - virtual bool baseClassOf(const ListRecTy *RHS) const { return false; } - virtual bool baseClassOf(const CodeRecTy *RHS) const { return false; } - virtual bool baseClassOf(const DagRecTy *RHS) const { return false; } - virtual bool baseClassOf(const RecordRecTy *RHS) const; -}; - -/// resolveTypes - Find a common type that T1 and T2 convert to. -/// Return 0 if no such type exists. -/// -RecTy *resolveTypes(RecTy *T1, RecTy *T2); - -//===----------------------------------------------------------------------===// -// Initializer Classes -//===----------------------------------------------------------------------===// - -class Init { -public: - virtual ~Init() {} - - /// isComplete - This virtual method should be overridden by values that may - /// not be completely specified yet. - virtual bool isComplete() const { return true; } - - /// print - Print out this value. - void print(raw_ostream &OS) const { OS << getAsString(); } - - /// getAsString - Convert this value to a string form. - virtual std::string getAsString() const = 0; - - /// dump - Debugging method that may be called through a debugger, just - /// invokes print on stderr. - void dump() const; - - /// convertInitializerTo - This virtual function is a simple call-back - /// function that should be overridden to call the appropriate - /// RecTy::convertValue method. - /// - virtual Init *convertInitializerTo(RecTy *Ty) = 0; - - /// convertInitializerBitRange - This method is used to implement the bitrange - /// selection operator. Given an initializer, it selects the specified bits - /// out, returning them as a new init of bits type. If it is not legal to use - /// the bit subscript operator on this initializer, return null. - /// - virtual Init *convertInitializerBitRange(const std::vector<unsigned> &Bits) { - return 0; - } - - /// convertInitListSlice - This method is used to implement the list slice - /// selection operator. Given an initializer, it selects the specified list - /// elements, returning them as a new init of list type. If it is not legal - /// to take a slice of this, return null. - /// - virtual Init *convertInitListSlice(const std::vector<unsigned> &Elements) { - return 0; - } - - /// getFieldType - This method is used to implement the FieldInit class. - /// Implementors of this method should return the type of the named field if - /// they are of record type. - /// - virtual RecTy *getFieldType(const std::string &FieldName) const { return 0; } - - /// getFieldInit - This method complements getFieldType to return the - /// initializer for the specified field. If getFieldType returns non-null - /// this method should return non-null, otherwise it returns null. - /// - virtual Init *getFieldInit(Record &R, const RecordVal *RV, - const std::string &FieldName) const { - return 0; - } - - /// resolveReferences - This method is used by classes that refer to other - /// variables which may not be defined at the time the expression is formed. - /// If a value is set for the variable later, this method will be called on - /// users of the value to allow the value to propagate out. - /// - virtual Init *resolveReferences(Record &R, const RecordVal *RV) { - return this; - } -}; - -inline raw_ostream &operator<<(raw_ostream &OS, const Init &I) { - I.print(OS); return OS; -} - -/// TypedInit - This is the common super-class of types that have a specific, -/// explicit, type. -/// -class TypedInit : public Init { - RecTy *Ty; -public: - explicit TypedInit(RecTy *T) : Ty(T) {} - - RecTy *getType() const { return Ty; } - - virtual Init *convertInitializerBitRange(const std::vector<unsigned> &Bits); - virtual Init *convertInitListSlice(const std::vector<unsigned> &Elements); - - /// getFieldType - This method is used to implement the FieldInit class. - /// Implementors of this method should return the type of the named field if - /// they are of record type. - /// - virtual RecTy *getFieldType(const std::string &FieldName) const; - - /// resolveBitReference - This method is used to implement - /// VarBitInit::resolveReferences. If the bit is able to be resolved, we - /// simply return the resolved value, otherwise we return null. - /// - virtual Init *resolveBitReference(Record &R, const RecordVal *RV, - unsigned Bit) = 0; - - /// resolveListElementReference - This method is used to implement - /// VarListElementInit::resolveReferences. If the list element is resolvable - /// now, we return the resolved value, otherwise we return null. - virtual Init *resolveListElementReference(Record &R, const RecordVal *RV, - unsigned Elt) = 0; -}; - - -/// UnsetInit - ? - Represents an uninitialized value -/// -class UnsetInit : public Init { -public: - virtual Init *convertInitializerTo(RecTy *Ty) { - return Ty->convertValue(this); - } - - virtual bool isComplete() const { return false; } - virtual std::string getAsString() const { return "?"; } -}; - - -/// BitInit - true/false - Represent a concrete initializer for a bit. -/// -class BitInit : public Init { - bool Value; -public: - explicit BitInit(bool V) : Value(V) {} - - bool getValue() const { return Value; } - - virtual Init *convertInitializerTo(RecTy *Ty) { - return Ty->convertValue(this); - } - - virtual std::string getAsString() const { return Value ? "1" : "0"; } -}; - -/// BitsInit - { a, b, c } - Represents an initializer for a BitsRecTy value. -/// It contains a vector of bits, whose size is determined by the type. -/// -class BitsInit : public Init { - std::vector<Init*> Bits; -public: - explicit BitsInit(unsigned Size) : Bits(Size) {} - - unsigned getNumBits() const { return Bits.size(); } - - Init *getBit(unsigned Bit) const { - assert(Bit < Bits.size() && "Bit index out of range!"); - return Bits[Bit]; - } - void setBit(unsigned Bit, Init *V) { - assert(Bit < Bits.size() && "Bit index out of range!"); - assert(Bits[Bit] == 0 && "Bit already set!"); - Bits[Bit] = V; - } - - virtual Init *convertInitializerTo(RecTy *Ty) { - return Ty->convertValue(this); - } - virtual Init *convertInitializerBitRange(const std::vector<unsigned> &Bits); - - virtual bool isComplete() const { - for (unsigned i = 0; i != getNumBits(); ++i) - if (!getBit(i)->isComplete()) return false; - return true; - } - bool allInComplete() const { - for (unsigned i = 0; i != getNumBits(); ++i) - if (getBit(i)->isComplete()) return false; - return true; - } - virtual std::string getAsString() const; - - virtual Init *resolveReferences(Record &R, const RecordVal *RV); -}; - - -/// IntInit - 7 - Represent an initalization by a literal integer value. -/// -class IntInit : public TypedInit { - int64_t Value; -public: - explicit IntInit(int64_t V) : TypedInit(new IntRecTy), Value(V) {} - - int64_t getValue() const { return Value; } - - virtual Init *convertInitializerTo(RecTy *Ty) { - return Ty->convertValue(this); - } - virtual Init *convertInitializerBitRange(const std::vector<unsigned> &Bits); - - virtual std::string getAsString() const; - - /// resolveBitReference - This method is used to implement - /// VarBitInit::resolveReferences. If the bit is able to be resolved, we - /// simply return the resolved value, otherwise we return null. - /// - virtual Init *resolveBitReference(Record &R, const RecordVal *RV, - unsigned Bit) { - assert(0 && "Illegal bit reference off int"); - return 0; - } - - /// resolveListElementReference - This method is used to implement - /// VarListElementInit::resolveReferences. If the list element is resolvable - /// now, we return the resolved value, otherwise we return null. - virtual Init *resolveListElementReference(Record &R, const RecordVal *RV, - unsigned Elt) { - assert(0 && "Illegal element reference off int"); - return 0; - } -}; - - -/// StringInit - "foo" - Represent an initialization by a string value. -/// -class StringInit : public TypedInit { - std::string Value; -public: - explicit StringInit(const std::string &V) - : TypedInit(new StringRecTy), Value(V) {} - - const std::string &getValue() const { return Value; } - - virtual Init *convertInitializerTo(RecTy *Ty) { - return Ty->convertValue(this); - } - - virtual std::string getAsString() const { return "\"" + Value + "\""; } - - /// resolveBitReference - This method is used to implement - /// VarBitInit::resolveReferences. If the bit is able to be resolved, we - /// simply return the resolved value, otherwise we return null. - /// - virtual Init *resolveBitReference(Record &R, const RecordVal *RV, - unsigned Bit) { - assert(0 && "Illegal bit reference off string"); - return 0; - } - - /// resolveListElementReference - This method is used to implement - /// VarListElementInit::resolveReferences. If the list element is resolvable - /// now, we return the resolved value, otherwise we return null. - virtual Init *resolveListElementReference(Record &R, const RecordVal *RV, - unsigned Elt) { - assert(0 && "Illegal element reference off string"); - return 0; - } -}; - -/// CodeInit - "[{...}]" - Represent a code fragment. -/// -class CodeInit : public Init { - std::string Value; -public: - explicit CodeInit(const std::string &V) : Value(V) {} - - const std::string &getValue() const { return Value; } - - virtual Init *convertInitializerTo(RecTy *Ty) { - return Ty->convertValue(this); - } - - virtual std::string getAsString() const { return "[{" + Value + "}]"; } -}; - -/// ListInit - [AL, AH, CL] - Represent a list of defs -/// -class ListInit : public TypedInit { - std::vector<Init*> Values; -public: - typedef std::vector<Init*>::iterator iterator; - typedef std::vector<Init*>::const_iterator const_iterator; - - explicit ListInit(std::vector<Init*> &Vs, RecTy *EltTy) - : TypedInit(new ListRecTy(EltTy)) { - Values.swap(Vs); - } - explicit ListInit(iterator Start, iterator End, RecTy *EltTy) - : TypedInit(new ListRecTy(EltTy)), Values(Start, End) {} - - unsigned getSize() const { return Values.size(); } - Init *getElement(unsigned i) const { - assert(i < Values.size() && "List element index out of range!"); - return Values[i]; - } - - Record *getElementAsRecord(unsigned i) const; - - Init *convertInitListSlice(const std::vector<unsigned> &Elements); - - virtual Init *convertInitializerTo(RecTy *Ty) { - return Ty->convertValue(this); - } - - /// resolveReferences - This method is used by classes that refer to other - /// variables which may not be defined at the time they expression is formed. - /// If a value is set for the variable later, this method will be called on - /// users of the value to allow the value to propagate out. - /// - virtual Init *resolveReferences(Record &R, const RecordVal *RV); - - virtual std::string getAsString() const; - - inline iterator begin() { return Values.begin(); } - inline const_iterator begin() const { return Values.begin(); } - inline iterator end () { return Values.end(); } - inline const_iterator end () const { return Values.end(); } - - inline size_t size () const { return Values.size(); } - inline bool empty() const { return Values.empty(); } - - /// resolveBitReference - This method is used to implement - /// VarBitInit::resolveReferences. If the bit is able to be resolved, we - /// simply return the resolved value, otherwise we return null. - /// - virtual Init *resolveBitReference(Record &R, const RecordVal *RV, - unsigned Bit) { - assert(0 && "Illegal bit reference off list"); - return 0; - } - - /// resolveListElementReference - This method is used to implement - /// VarListElementInit::resolveReferences. If the list element is resolvable - /// now, we return the resolved value, otherwise we return null. - virtual Init *resolveListElementReference(Record &R, const RecordVal *RV, - unsigned Elt); -}; - - -/// OpInit - Base class for operators -/// -class OpInit : public TypedInit { -public: - OpInit(RecTy *Type) : TypedInit(Type) {} - - // Clone - Clone this operator, replacing arguments with the new list - virtual OpInit *clone(std::vector<Init *> &Operands) = 0; - - virtual int getNumOperands() const = 0; - virtual Init *getOperand(int i) = 0; - - // Fold - If possible, fold this to a simpler init. Return this if not - // possible to fold. - virtual Init *Fold(Record *CurRec, MultiClass *CurMultiClass) = 0; - - virtual Init *convertInitializerTo(RecTy *Ty) { - return Ty->convertValue(this); - } - - virtual Init *resolveBitReference(Record &R, const RecordVal *RV, - unsigned Bit); - virtual Init *resolveListElementReference(Record &R, const RecordVal *RV, - unsigned Elt); -}; - - -/// UnOpInit - !op (X) - Transform an init. -/// -class UnOpInit : public OpInit { -public: - enum UnaryOp { CAST, HEAD, TAIL, EMPTY }; -private: - UnaryOp Opc; - Init *LHS; -public: - UnOpInit(UnaryOp opc, Init *lhs, RecTy *Type) : - OpInit(Type), Opc(opc), LHS(lhs) { - } - - // Clone - Clone this operator, replacing arguments with the new list - virtual OpInit *clone(std::vector<Init *> &Operands) { - assert(Operands.size() == 1 && - "Wrong number of operands for unary operation"); - return new UnOpInit(getOpcode(), *Operands.begin(), getType()); - } - - int getNumOperands() const { return 1; } - Init *getOperand(int i) { - assert(i == 0 && "Invalid operand id for unary operator"); - return getOperand(); - } - - UnaryOp getOpcode() const { return Opc; } - Init *getOperand() const { return LHS; } - - // Fold - If possible, fold this to a simpler init. Return this if not - // possible to fold. - Init *Fold(Record *CurRec, MultiClass *CurMultiClass); - - virtual Init *resolveReferences(Record &R, const RecordVal *RV); - - virtual std::string getAsString() const; -}; - -/// BinOpInit - !op (X, Y) - Combine two inits. -/// -class BinOpInit : public OpInit { -public: - enum BinaryOp { SHL, SRA, SRL, STRCONCAT, CONCAT, EQ }; -private: - BinaryOp Opc; - Init *LHS, *RHS; -public: - BinOpInit(BinaryOp opc, Init *lhs, Init *rhs, RecTy *Type) : - OpInit(Type), Opc(opc), LHS(lhs), RHS(rhs) { - } - - // Clone - Clone this operator, replacing arguments with the new list - virtual OpInit *clone(std::vector<Init *> &Operands) { - assert(Operands.size() == 2 && - "Wrong number of operands for binary operation"); - return new BinOpInit(getOpcode(), Operands[0], Operands[1], getType()); - } - - int getNumOperands() const { return 2; } - Init *getOperand(int i) { - assert((i == 0 || i == 1) && "Invalid operand id for binary operator"); - if (i == 0) { - return getLHS(); - } else { - return getRHS(); - } - } - - BinaryOp getOpcode() const { return Opc; } - Init *getLHS() const { return LHS; } - Init *getRHS() const { return RHS; } - - // Fold - If possible, fold this to a simpler init. Return this if not - // possible to fold. - Init *Fold(Record *CurRec, MultiClass *CurMultiClass); - - virtual Init *resolveReferences(Record &R, const RecordVal *RV); - - virtual std::string getAsString() const; -}; - -/// TernOpInit - !op (X, Y, Z) - Combine two inits. -/// -class TernOpInit : public OpInit { -public: - enum TernaryOp { SUBST, FOREACH, IF }; -private: - TernaryOp Opc; - Init *LHS, *MHS, *RHS; -public: - TernOpInit(TernaryOp opc, Init *lhs, Init *mhs, Init *rhs, RecTy *Type) : - OpInit(Type), Opc(opc), LHS(lhs), MHS(mhs), RHS(rhs) { - } - - // Clone - Clone this operator, replacing arguments with the new list - virtual OpInit *clone(std::vector<Init *> &Operands) { - assert(Operands.size() == 3 && - "Wrong number of operands for ternary operation"); - return new TernOpInit(getOpcode(), Operands[0], Operands[1], Operands[2], - getType()); - } - - int getNumOperands() const { return 3; } - Init *getOperand(int i) { - assert((i == 0 || i == 1 || i == 2) && - "Invalid operand id for ternary operator"); - if (i == 0) { - return getLHS(); - } else if (i == 1) { - return getMHS(); - } else { - return getRHS(); - } - } - - TernaryOp getOpcode() const { return Opc; } - Init *getLHS() const { return LHS; } - Init *getMHS() const { return MHS; } - Init *getRHS() const { return RHS; } - - // Fold - If possible, fold this to a simpler init. Return this if not - // possible to fold. - Init *Fold(Record *CurRec, MultiClass *CurMultiClass); - - virtual bool isComplete() const { return false; } - - virtual Init *resolveReferences(Record &R, const RecordVal *RV); - - virtual std::string getAsString() const; -}; - - -/// VarInit - 'Opcode' - Represent a reference to an entire variable object. -/// -class VarInit : public TypedInit { - std::string VarName; -public: - explicit VarInit(const std::string &VN, RecTy *T) - : TypedInit(T), VarName(VN) {} - - virtual Init *convertInitializerTo(RecTy *Ty) { - return Ty->convertValue(this); - } - - const std::string &getName() const { return VarName; } - - virtual Init *resolveBitReference(Record &R, const RecordVal *RV, - unsigned Bit); - virtual Init *resolveListElementReference(Record &R, const RecordVal *RV, - unsigned Elt); - - virtual RecTy *getFieldType(const std::string &FieldName) const; - virtual Init *getFieldInit(Record &R, const RecordVal *RV, - const std::string &FieldName) const; - - /// resolveReferences - This method is used by classes that refer to other - /// variables which may not be defined at the time they expression is formed. - /// If a value is set for the variable later, this method will be called on - /// users of the value to allow the value to propagate out. - /// - virtual Init *resolveReferences(Record &R, const RecordVal *RV); - - virtual std::string getAsString() const { return VarName; } -}; - - -/// VarBitInit - Opcode{0} - Represent access to one bit of a variable or field. -/// -class VarBitInit : public Init { - TypedInit *TI; - unsigned Bit; -public: - VarBitInit(TypedInit *T, unsigned B) : TI(T), Bit(B) { - assert(T->getType() && dynamic_cast<BitsRecTy*>(T->getType()) && - ((BitsRecTy*)T->getType())->getNumBits() > B && - "Illegal VarBitInit expression!"); - } - - virtual Init *convertInitializerTo(RecTy *Ty) { - return Ty->convertValue(this); - } - - TypedInit *getVariable() const { return TI; } - unsigned getBitNum() const { return Bit; } - - virtual std::string getAsString() const; - virtual Init *resolveReferences(Record &R, const RecordVal *RV); -}; - -/// VarListElementInit - List[4] - Represent access to one element of a var or -/// field. -class VarListElementInit : public TypedInit { - TypedInit *TI; - unsigned Element; -public: - VarListElementInit(TypedInit *T, unsigned E) - : TypedInit(dynamic_cast<ListRecTy*>(T->getType())->getElementType()), - TI(T), Element(E) { - assert(T->getType() && dynamic_cast<ListRecTy*>(T->getType()) && - "Illegal VarBitInit expression!"); - } - - virtual Init *convertInitializerTo(RecTy *Ty) { - return Ty->convertValue(this); - } - - TypedInit *getVariable() const { return TI; } - unsigned getElementNum() const { return Element; } - - virtual Init *resolveBitReference(Record &R, const RecordVal *RV, - unsigned Bit); - - /// resolveListElementReference - This method is used to implement - /// VarListElementInit::resolveReferences. If the list element is resolvable - /// now, we return the resolved value, otherwise we return null. - virtual Init *resolveListElementReference(Record &R, const RecordVal *RV, - unsigned Elt); - - virtual std::string getAsString() const; - virtual Init *resolveReferences(Record &R, const RecordVal *RV); -}; - -/// DefInit - AL - Represent a reference to a 'def' in the description -/// -class DefInit : public TypedInit { - Record *Def; -public: - explicit DefInit(Record *D) : TypedInit(new RecordRecTy(D)), Def(D) {} - - virtual Init *convertInitializerTo(RecTy *Ty) { - return Ty->convertValue(this); - } - - Record *getDef() const { return Def; } - - //virtual Init *convertInitializerBitRange(const std::vector<unsigned> &Bits); - - virtual RecTy *getFieldType(const std::string &FieldName) const; - virtual Init *getFieldInit(Record &R, const RecordVal *RV, - const std::string &FieldName) const; - - virtual std::string getAsString() const; - - /// resolveBitReference - This method is used to implement - /// VarBitInit::resolveReferences. If the bit is able to be resolved, we - /// simply return the resolved value, otherwise we return null. - /// - virtual Init *resolveBitReference(Record &R, const RecordVal *RV, - unsigned Bit) { - assert(0 && "Illegal bit reference off def"); - return 0; - } - - /// resolveListElementReference - This method is used to implement - /// VarListElementInit::resolveReferences. If the list element is resolvable - /// now, we return the resolved value, otherwise we return null. - virtual Init *resolveListElementReference(Record &R, const RecordVal *RV, - unsigned Elt) { - assert(0 && "Illegal element reference off def"); - return 0; - } -}; - - -/// FieldInit - X.Y - Represent a reference to a subfield of a variable -/// -class FieldInit : public TypedInit { - Init *Rec; // Record we are referring to - std::string FieldName; // Field we are accessing -public: - FieldInit(Init *R, const std::string &FN) - : TypedInit(R->getFieldType(FN)), Rec(R), FieldName(FN) { - assert(getType() && "FieldInit with non-record type!"); - } - - virtual Init *convertInitializerTo(RecTy *Ty) { - return Ty->convertValue(this); - } - - virtual Init *resolveBitReference(Record &R, const RecordVal *RV, - unsigned Bit); - virtual Init *resolveListElementReference(Record &R, const RecordVal *RV, - unsigned Elt); - - virtual Init *resolveReferences(Record &R, const RecordVal *RV); - - virtual std::string getAsString() const { - return Rec->getAsString() + "." + FieldName; - } -}; - -/// DagInit - (v a, b) - Represent a DAG tree value. DAG inits are required -/// to have at least one value then a (possibly empty) list of arguments. Each -/// argument can have a name associated with it. -/// -class DagInit : public TypedInit { - Init *Val; - std::string ValName; - std::vector<Init*> Args; - std::vector<std::string> ArgNames; -public: - DagInit(Init *V, std::string VN, - const std::vector<std::pair<Init*, std::string> > &args) - : TypedInit(new DagRecTy), Val(V), ValName(VN) { - Args.reserve(args.size()); - ArgNames.reserve(args.size()); - for (unsigned i = 0, e = args.size(); i != e; ++i) { - Args.push_back(args[i].first); - ArgNames.push_back(args[i].second); - } - } - DagInit(Init *V, std::string VN, const std::vector<Init*> &args, - const std::vector<std::string> &argNames) - : TypedInit(new DagRecTy), Val(V), ValName(VN), Args(args), - ArgNames(argNames) { } - - virtual Init *convertInitializerTo(RecTy *Ty) { - return Ty->convertValue(this); - } - - Init *getOperator() const { return Val; } - - const std::string &getName() const { return ValName; } - - unsigned getNumArgs() const { return Args.size(); } - Init *getArg(unsigned Num) const { - assert(Num < Args.size() && "Arg number out of range!"); - return Args[Num]; - } - const std::string &getArgName(unsigned Num) const { - assert(Num < ArgNames.size() && "Arg number out of range!"); - return ArgNames[Num]; - } - - void setArg(unsigned Num, Init *I) { - assert(Num < Args.size() && "Arg number out of range!"); - Args[Num] = I; - } - - virtual Init *resolveReferences(Record &R, const RecordVal *RV); - - virtual std::string getAsString() const; - - typedef std::vector<Init*>::iterator arg_iterator; - typedef std::vector<Init*>::const_iterator const_arg_iterator; - typedef std::vector<std::string>::iterator name_iterator; - typedef std::vector<std::string>::const_iterator const_name_iterator; - - inline arg_iterator arg_begin() { return Args.begin(); } - inline const_arg_iterator arg_begin() const { return Args.begin(); } - inline arg_iterator arg_end () { return Args.end(); } - inline const_arg_iterator arg_end () const { return Args.end(); } - - inline size_t arg_size () const { return Args.size(); } - inline bool arg_empty() const { return Args.empty(); } - - inline name_iterator name_begin() { return ArgNames.begin(); } - inline const_name_iterator name_begin() const { return ArgNames.begin(); } - inline name_iterator name_end () { return ArgNames.end(); } - inline const_name_iterator name_end () const { return ArgNames.end(); } - - inline size_t name_size () const { return ArgNames.size(); } - inline bool name_empty() const { return ArgNames.empty(); } - - virtual Init *resolveBitReference(Record &R, const RecordVal *RV, - unsigned Bit) { - assert(0 && "Illegal bit reference off dag"); - return 0; - } - - virtual Init *resolveListElementReference(Record &R, const RecordVal *RV, - unsigned Elt) { - assert(0 && "Illegal element reference off dag"); - return 0; - } -}; - -//===----------------------------------------------------------------------===// -// High-Level Classes -//===----------------------------------------------------------------------===// - -class RecordVal { - std::string Name; - RecTy *Ty; - unsigned Prefix; - Init *Value; -public: - RecordVal(const std::string &N, RecTy *T, unsigned P); - - const std::string &getName() const { return Name; } - - unsigned getPrefix() const { return Prefix; } - RecTy *getType() const { return Ty; } - Init *getValue() const { return Value; } - - bool setValue(Init *V) { - if (V) { - Value = V->convertInitializerTo(Ty); - return Value == 0; - } - Value = 0; - return false; - } - - void dump() const; - void print(raw_ostream &OS, bool PrintSem = true) const; -}; - -inline raw_ostream &operator<<(raw_ostream &OS, const RecordVal &RV) { - RV.print(OS << " "); - return OS; -} - -class Record { - static unsigned LastID; - - // Unique record ID. - unsigned ID; - std::string Name; - SMLoc Loc; - std::vector<std::string> TemplateArgs; - std::vector<RecordVal> Values; - std::vector<Record*> SuperClasses; - - // Tracks Record instances. Not owned by Record. - RecordKeeper &TrackedRecords; - -public: - - // Constructs a record. - explicit Record(const std::string &N, SMLoc loc, RecordKeeper &records) : - ID(LastID++), Name(N), Loc(loc), TrackedRecords(records) {} - ~Record() {} - - - static unsigned getNewUID() { return LastID++; } - - - unsigned getID() const { return ID; } - - const std::string &getName() const { return Name; } - void setName(const std::string &Name); // Also updates RecordKeeper. - - SMLoc getLoc() const { return Loc; } - - const std::vector<std::string> &getTemplateArgs() const { - return TemplateArgs; - } - const std::vector<RecordVal> &getValues() const { return Values; } - const std::vector<Record*> &getSuperClasses() const { return SuperClasses; } - - bool isTemplateArg(StringRef Name) const { - for (unsigned i = 0, e = TemplateArgs.size(); i != e; ++i) - if (TemplateArgs[i] == Name) return true; - return false; - } - - const RecordVal *getValue(StringRef Name) const { - for (unsigned i = 0, e = Values.size(); i != e; ++i) - if (Values[i].getName() == Name) return &Values[i]; - return 0; - } - RecordVal *getValue(StringRef Name) { - for (unsigned i = 0, e = Values.size(); i != e; ++i) - if (Values[i].getName() == Name) return &Values[i]; - return 0; - } - - void addTemplateArg(StringRef Name) { - assert(!isTemplateArg(Name) && "Template arg already defined!"); - TemplateArgs.push_back(Name); - } - - void addValue(const RecordVal &RV) { - assert(getValue(RV.getName()) == 0 && "Value already added!"); - Values.push_back(RV); - } - - void removeValue(StringRef Name) { - for (unsigned i = 0, e = Values.size(); i != e; ++i) - if (Values[i].getName() == Name) { - Values.erase(Values.begin()+i); - return; - } - assert(0 && "Cannot remove an entry that does not exist!"); - } - - bool isSubClassOf(const Record *R) const { - for (unsigned i = 0, e = SuperClasses.size(); i != e; ++i) - if (SuperClasses[i] == R) - return true; - return false; - } - - bool isSubClassOf(StringRef Name) const { - for (unsigned i = 0, e = SuperClasses.size(); i != e; ++i) - if (SuperClasses[i]->getName() == Name) - return true; - return false; - } - - void addSuperClass(Record *R) { - assert(!isSubClassOf(R) && "Already subclassing record!"); - SuperClasses.push_back(R); - } - - /// resolveReferences - If there are any field references that refer to fields - /// that have been filled in, we can propagate the values now. - /// - void resolveReferences() { resolveReferencesTo(0); } - - /// resolveReferencesTo - If anything in this record refers to RV, replace the - /// reference to RV with the RHS of RV. If RV is null, we resolve all - /// possible references. - void resolveReferencesTo(const RecordVal *RV); - - RecordKeeper &getRecords() const { - return TrackedRecords; - } - - void dump() const; - - //===--------------------------------------------------------------------===// - // High-level methods useful to tablegen back-ends - // - - /// getValueInit - Return the initializer for a value with the specified name, - /// or throw an exception if the field does not exist. - /// - Init *getValueInit(StringRef FieldName) const; - - /// getValueAsString - This method looks up the specified field and returns - /// its value as a string, throwing an exception if the field does not exist - /// or if the value is not a string. - /// - std::string getValueAsString(StringRef FieldName) const; - - /// getValueAsBitsInit - This method looks up the specified field and returns - /// its value as a BitsInit, throwing an exception if the field does not exist - /// or if the value is not the right type. - /// - BitsInit *getValueAsBitsInit(StringRef FieldName) const; - - /// getValueAsListInit - This method looks up the specified field and returns - /// its value as a ListInit, throwing an exception if the field does not exist - /// or if the value is not the right type. - /// - ListInit *getValueAsListInit(StringRef FieldName) const; - - /// getValueAsListOfDefs - This method looks up the specified field and - /// returns its value as a vector of records, throwing an exception if the - /// field does not exist or if the value is not the right type. - /// - std::vector<Record*> getValueAsListOfDefs(StringRef FieldName) const; - - /// getValueAsListOfInts - This method looks up the specified field and - /// returns its value as a vector of integers, throwing an exception if the - /// field does not exist or if the value is not the right type. - /// - std::vector<int64_t> getValueAsListOfInts(StringRef FieldName) const; - - /// getValueAsListOfStrings - This method looks up the specified field and - /// returns its value as a vector of strings, throwing an exception if the - /// field does not exist or if the value is not the right type. - /// - std::vector<std::string> getValueAsListOfStrings(StringRef FieldName) const; - - /// getValueAsDef - This method looks up the specified field and returns its - /// value as a Record, throwing an exception if the field does not exist or if - /// the value is not the right type. - /// - Record *getValueAsDef(StringRef FieldName) const; - - /// getValueAsBit - This method looks up the specified field and returns its - /// value as a bit, throwing an exception if the field does not exist or if - /// the value is not the right type. - /// - bool getValueAsBit(StringRef FieldName) const; - - /// getValueAsInt - This method looks up the specified field and returns its - /// value as an int64_t, throwing an exception if the field does not exist or - /// if the value is not the right type. - /// - int64_t getValueAsInt(StringRef FieldName) const; - - /// getValueAsDag - This method looks up the specified field and returns its - /// value as an Dag, throwing an exception if the field does not exist or if - /// the value is not the right type. - /// - DagInit *getValueAsDag(StringRef FieldName) const; - - /// getValueAsCode - This method looks up the specified field and returns - /// its value as the string data in a CodeInit, throwing an exception if the - /// field does not exist or if the value is not a code object. - /// - std::string getValueAsCode(StringRef FieldName) const; -}; - -raw_ostream &operator<<(raw_ostream &OS, const Record &R); - -struct MultiClass { - Record Rec; // Placeholder for template args and Name. - typedef std::vector<Record*> RecordVector; - RecordVector DefPrototypes; - - void dump() const; - - MultiClass(const std::string &Name, SMLoc Loc, RecordKeeper &Records) : - Rec(Name, Loc, Records) {} -}; - -class RecordKeeper { - std::map<std::string, Record*> Classes, Defs; -public: - ~RecordKeeper() { - for (std::map<std::string, Record*>::iterator I = Classes.begin(), - E = Classes.end(); I != E; ++I) - delete I->second; - for (std::map<std::string, Record*>::iterator I = Defs.begin(), - E = Defs.end(); I != E; ++I) - delete I->second; - } - - const std::map<std::string, Record*> &getClasses() const { return Classes; } - const std::map<std::string, Record*> &getDefs() const { return Defs; } - - Record *getClass(const std::string &Name) const { - std::map<std::string, Record*>::const_iterator I = Classes.find(Name); - return I == Classes.end() ? 0 : I->second; - } - Record *getDef(const std::string &Name) const { - std::map<std::string, Record*>::const_iterator I = Defs.find(Name); - return I == Defs.end() ? 0 : I->second; - } - void addClass(Record *R) { - assert(getClass(R->getName()) == 0 && "Class already exists!"); - Classes.insert(std::make_pair(R->getName(), R)); - } - void addDef(Record *R) { - assert(getDef(R->getName()) == 0 && "Def already exists!"); - Defs.insert(std::make_pair(R->getName(), R)); - } - - /// removeClass - Remove, but do not delete, the specified record. - /// - void removeClass(const std::string &Name) { - assert(Classes.count(Name) && "Class does not exist!"); - Classes.erase(Name); - } - /// removeDef - Remove, but do not delete, the specified record. - /// - void removeDef(const std::string &Name) { - assert(Defs.count(Name) && "Def does not exist!"); - Defs.erase(Name); - } - - //===--------------------------------------------------------------------===// - // High-level helper methods, useful for tablegen backends... - - /// getAllDerivedDefinitions - This method returns all concrete definitions - /// that derive from the specified class name. If a class with the specified - /// name does not exist, an exception is thrown. - std::vector<Record*> - getAllDerivedDefinitions(const std::string &ClassName) const; - - void dump() const; -}; - -/// LessRecord - Sorting predicate to sort record pointers by name. -/// -struct LessRecord { - bool operator()(const Record *Rec1, const Record *Rec2) const { - return StringRef(Rec1->getName()).compare_numeric(Rec2->getName()) < 0; - } -}; - -/// LessRecordFieldName - Sorting predicate to sort record pointers by their -/// name field. -/// -struct LessRecordFieldName { - bool operator()(const Record *Rec1, const Record *Rec2) const { - return Rec1->getValueAsString("Name") < Rec2->getValueAsString("Name"); - } -}; - -raw_ostream &operator<<(raw_ostream &OS, const RecordKeeper &RK); - -} // End llvm namespace - -#endif diff --git a/utils/TableGen/RegisterInfoEmitter.cpp b/utils/TableGen/RegisterInfoEmitter.cpp index 65d4a9b..b0f4ffc 100644 --- a/utils/TableGen/RegisterInfoEmitter.cpp +++ b/utils/TableGen/RegisterInfoEmitter.cpp @@ -16,7 +16,8 @@ #include "RegisterInfoEmitter.h" #include "CodeGenTarget.h" #include "CodeGenRegisters.h" -#include "Record.h" +#include "llvm/TableGen/Record.h" +#include "llvm/ADT/BitVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Format.h" @@ -39,6 +40,9 @@ RegisterInfoEmitter::runEnums(raw_ostream &OS, OS << "namespace llvm {\n\n"; + OS << "class MCRegisterClass;\n" + << "extern MCRegisterClass " << Namespace << "MCRegisterClasses[];\n\n"; + if (!Namespace.empty()) OS << "namespace " << Namespace << " {\n"; OS << "enum {\n NoRegister,\n"; @@ -53,8 +57,7 @@ RegisterInfoEmitter::runEnums(raw_ostream &OS, if (!Namespace.empty()) OS << "}\n"; - const std::vector<CodeGenRegisterClass> &RegisterClasses = - Target.getRegisterClasses(); + ArrayRef<CodeGenRegisterClass*> RegisterClasses = Bank.getRegClasses(); if (!RegisterClasses.empty()) { OS << "\n// Register classes\n"; if (!Namespace.empty()) @@ -62,7 +65,7 @@ RegisterInfoEmitter::runEnums(raw_ostream &OS, OS << "enum {\n"; for (unsigned i = 0, e = RegisterClasses.size(); i != e; ++i) { if (i) OS << ",\n"; - OS << " " << RegisterClasses[i].getName() << "RegClassID"; + OS << " " << RegisterClasses[i]->getName() << "RegClassID"; OS << " = " << i; } OS << "\n };\n"; @@ -91,6 +94,147 @@ RegisterInfoEmitter::runEnums(raw_ostream &OS, OS << "#endif // GET_REGINFO_ENUM\n\n"; } +void +RegisterInfoEmitter::EmitRegMapping(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; + DwarfRegNumsMapTy DwarfRegNums; + + // First, just pull all provided information to the map + unsigned maxLength = 0; + for (unsigned i = 0, e = Regs.size(); i != e; ++i) { + Record *Reg = Regs[i]->TheDef; + std::vector<int64_t> RegNums = Reg->getValueAsListOfInts("DwarfNumbers"); + maxLength = std::max((size_t)maxLength, RegNums.size()); + if (DwarfRegNums.count(Reg)) + errs() << "Warning: DWARF numbers for register " << getQualifiedName(Reg) + << "specified multiple times\n"; + DwarfRegNums[Reg] = RegNums; + } + + if (!maxLength) + return; + + // Now we know maximal length of number list. Append -1's, where needed + for (DwarfRegNumsMapTy::iterator + I = DwarfRegNums.begin(), E = DwarfRegNums.end(); I != E; ++I) + for (unsigned i = I->second.size(), e = maxLength; i != e; ++i) + I->second.push_back(-1); + + // Emit reverse information about the dwarf register numbers. + for (unsigned j = 0; j < 2; ++j) { + OS << " switch ("; + if (j == 0) + OS << "DwarfFlavour"; + else + OS << "EHFlavour"; + OS << ") {\n" + << " default:\n" + << " assert(0 && \"Unknown DWARF flavour\");\n" + << " break;\n"; + + for (unsigned i = 0, e = maxLength; i != e; ++i) { + OS << " case " << i << ":\n"; + for (DwarfRegNumsMapTy::iterator + I = DwarfRegNums.begin(), E = DwarfRegNums.end(); I != E; ++I) { + int DwarfRegNo = I->second[i]; + if (DwarfRegNo < 0) + continue; + OS << " "; + if (!isCtor) + OS << "RI->"; + OS << "mapDwarfRegToLLVMReg(" << DwarfRegNo << ", " + << getQualifiedName(I->first) << ", "; + if (j == 0) + OS << "false"; + else + OS << "true"; + OS << " );\n"; + } + OS << " break;\n"; + } + OS << " }\n"; + } + + for (unsigned i = 0, e = Regs.size(); i != e; ++i) { + Record *Reg = Regs[i]->TheDef; + const RecordVal *V = Reg->getValue("DwarfAlias"); + if (!V || !V->getValue()) + continue; + + DefInit *DI = dynamic_cast<DefInit*>(V->getValue()); + Record *Alias = DI->getDef(); + DwarfRegNums[Reg] = DwarfRegNums[Alias]; + } + + // Emit information about the dwarf register numbers. + for (unsigned j = 0; j < 2; ++j) { + OS << " switch ("; + if (j == 0) + OS << "DwarfFlavour"; + else + OS << "EHFlavour"; + OS << ") {\n" + << " default:\n" + << " assert(0 && \"Unknown DWARF flavour\");\n" + << " break;\n"; + + for (unsigned i = 0, e = maxLength; i != e; ++i) { + OS << " case " << i << ":\n"; + // Sort by name to get a stable order. + for (DwarfRegNumsMapTy::iterator + I = DwarfRegNums.begin(), E = DwarfRegNums.end(); I != E; ++I) { + int RegNo = I->second[i]; + OS << " "; + if (!isCtor) + OS << "RI->"; + OS << "mapLLVMRegToDwarfReg(" << getQualifiedName(I->first) << ", " + << RegNo << ", "; + if (j == 0) + OS << "false"; + else + OS << "true"; + OS << " );\n"; + } + OS << " break;\n"; + } + OS << " }\n"; + } +} + +// Print a BitVector as a sequence of hex numbers using a little-endian mapping. +// Width is the number of bits per hex number. +static void printBitVectorAsHex(raw_ostream &OS, + const BitVector &Bits, + unsigned Width) { + assert(Width <= 32 && "Width too large"); + unsigned Digits = (Width + 3) / 4; + for (unsigned i = 0, e = Bits.size(); i < e; i += Width) { + unsigned Value = 0; + for (unsigned j = 0; j != Width && i + j != e; ++j) + Value |= Bits.test(i + j) << j; + OS << format("0x%0*x, ", Digits, Value); + } +} + +// Helper to emit a set of bits into a constant byte array. +class BitVectorEmitter { + BitVector Values; +public: + void add(unsigned v) { + if (v >= Values.size()) + Values.resize(((v/8)+1)*8); // Round up to the next byte. + Values[v] = true; + } + + void print(raw_ostream &OS) { + printBitVectorAsHex(OS, Values, 8); + } +}; + // // runMCDesc - Print out MC register descriptions. // @@ -186,11 +330,73 @@ RegisterInfoEmitter::runMCDesc(raw_ostream &OS, CodeGenTarget &Target, } OS << "};\n\n"; // End of register descriptors... + ArrayRef<CodeGenRegisterClass*> RegisterClasses = RegBank.getRegClasses(); + + // Loop over all of the register classes... emitting each one. + OS << "namespace { // Register classes...\n"; + + // Emit the register enum value arrays for each RegisterClass + for (unsigned rc = 0, e = RegisterClasses.size(); rc != e; ++rc) { + const CodeGenRegisterClass &RC = *RegisterClasses[rc]; + ArrayRef<Record*> Order = RC.getOrder(); + + // Give the register class a legal C name if it's anonymous. + std::string Name = RC.getName(); + + // Emit the register list now. + OS << " // " << Name << " Register Class...\n" + << " static const unsigned " << Name + << "[] = {\n "; + for (unsigned i = 0, e = Order.size(); i != e; ++i) { + Record *Reg = Order[i]; + OS << getQualifiedName(Reg) << ", "; + } + OS << "\n };\n\n"; + + OS << " // " << Name << " Bit set.\n" + << " static const unsigned char " << Name + << "Bits[] = {\n "; + BitVectorEmitter BVE; + for (unsigned i = 0, e = Order.size(); i != e; ++i) { + Record *Reg = Order[i]; + BVE.add(Target.getRegBank().getReg(Reg)->EnumValue); + } + BVE.print(OS); + OS << "\n };\n\n"; + + } + OS << "}\n\n"; + + OS << "MCRegisterClass " << TargetName << "MCRegisterClasses[] = {\n"; + + for (unsigned rc = 0, e = RegisterClasses.size(); rc != e; ++rc) { + const CodeGenRegisterClass &RC = *RegisterClasses[rc]; + OS << " MCRegisterClass(" << RC.getQualifiedName() + "RegClassID" << ", " + << '\"' << RC.getName() << "\", " + << RC.SpillSize/8 << ", " + << RC.SpillAlignment/8 << ", " + << RC.CopyCost << ", " + << RC.Allocatable << ", " + << RC.getName() << ", " << RC.getName() << " + " + << RC.getOrder().size() << ", " + << RC.getName() << "Bits, sizeof(" << RC.getName() << "Bits)" + << "),\n"; + } + + OS << "};\n\n"; + // MCRegisterInfo initialization routine. OS << "static inline void Init" << TargetName - << "MCRegisterInfo(MCRegisterInfo *RI) {\n"; + << "MCRegisterInfo(MCRegisterInfo *RI, unsigned RA, " + << "unsigned DwarfFlavour = 0, unsigned EHFlavour = 0) {\n"; OS << " RI->InitMCRegisterInfo(" << TargetName << "RegDesc, " - << Regs.size()+1 << ");\n}\n\n"; + << Regs.size()+1 << ", RA, " << TargetName << "MCRegisterClasses, " + << RegisterClasses.size() << ");\n\n"; + + EmitRegMapping(OS, Regs, false); + + OS << "}\n\n"; + OS << "} // End llvm namespace \n"; OS << "#endif // GET_REGINFO_MC_DESC\n\n"; @@ -213,17 +419,15 @@ RegisterInfoEmitter::runTargetHeader(raw_ostream &OS, CodeGenTarget &Target, OS << "namespace llvm {\n\n"; OS << "struct " << ClassName << " : public TargetRegisterInfo {\n" - << " explicit " << ClassName << "();\n" - << " virtual int getDwarfRegNumFull(unsigned RegNum, " - << "unsigned Flavour) const;\n" - << " virtual int getLLVMRegNumFull(unsigned DwarfRegNum, " - << "unsigned Flavour) const;\n" - << " virtual int getDwarfRegNum(unsigned RegNum, bool isEH) const = 0;\n" + << " explicit " << ClassName + << "(unsigned RA, unsigned D = 0, unsigned E = 0);\n" << " virtual bool needsStackRealignment(const MachineFunction &) const\n" << " { return false; }\n" << " unsigned getSubReg(unsigned RegNo, unsigned Index) const;\n" << " unsigned getSubRegIndex(unsigned RegNo, unsigned SubRegNo) const;\n" << " unsigned composeSubRegIndices(unsigned, unsigned) const;\n" + << " const TargetRegisterClass *" + "getSubClassWithSubReg(const TargetRegisterClass*, unsigned) const;\n" << "};\n\n"; const std::vector<Record*> &SubRegIndices = RegBank.getSubRegIndices(); @@ -241,15 +445,14 @@ RegisterInfoEmitter::runTargetHeader(raw_ostream &OS, CodeGenTarget &Target, OS << "}\n"; } - const std::vector<CodeGenRegisterClass> &RegisterClasses = - Target.getRegisterClasses(); + ArrayRef<CodeGenRegisterClass*> RegisterClasses = RegBank.getRegClasses(); if (!RegisterClasses.empty()) { - OS << "namespace " << RegisterClasses[0].Namespace + OS << "namespace " << RegisterClasses[0]->Namespace << " { // Register classes\n"; for (unsigned i = 0, e = RegisterClasses.size(); i != e; ++i) { - const CodeGenRegisterClass &RC = RegisterClasses[i]; + const CodeGenRegisterClass &RC = *RegisterClasses[i]; const std::string &Name = RC.getName(); // Output the register class definition. @@ -285,42 +488,30 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target, OS << "namespace llvm {\n\n"; + // Get access to MCRegisterClass data. + OS << "extern MCRegisterClass " << Target.getName() + << "MCRegisterClasses[];\n"; + // Start out by emitting each of the register classes. - const std::vector<CodeGenRegisterClass> &RegisterClasses = - Target.getRegisterClasses(); + ArrayRef<CodeGenRegisterClass*> RegisterClasses = RegBank.getRegClasses(); // Collect all registers belonging to any allocatable class. std::set<Record*> AllocatableRegs; - // Loop over all of the register classes... emitting each one. - OS << "namespace { // Register classes...\n"; - - // Emit the register enum value arrays for each RegisterClass + // Collect allocatable registers. for (unsigned rc = 0, e = RegisterClasses.size(); rc != e; ++rc) { - const CodeGenRegisterClass &RC = RegisterClasses[rc]; + const CodeGenRegisterClass &RC = *RegisterClasses[rc]; ArrayRef<Record*> Order = RC.getOrder(); - // Collect allocatable registers. if (RC.Allocatable) AllocatableRegs.insert(Order.begin(), Order.end()); - - // Give the register class a legal C name if it's anonymous. - std::string Name = RC.getName(); - - // Emit the register list now. - OS << " // " << Name << " Register Class...\n" - << " static const unsigned " << Name - << "[] = {\n "; - for (unsigned i = 0, e = Order.size(); i != e; ++i) { - Record *Reg = Order[i]; - OS << getQualifiedName(Reg) << ", "; - } - OS << "\n };\n\n"; } + OS << "namespace { // Register classes...\n"; + // Emit the ValueType arrays for each RegisterClass for (unsigned rc = 0, e = RegisterClasses.size(); rc != e; ++rc) { - const CodeGenRegisterClass &RC = RegisterClasses[rc]; + const CodeGenRegisterClass &RC = *RegisterClasses[rc]; // Give the register class a legal C name if it's anonymous. std::string Name = RC.getName() + "VTs"; @@ -338,65 +529,39 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target, // Now that all of the structs have been emitted, emit the instances. if (!RegisterClasses.empty()) { - OS << "namespace " << RegisterClasses[0].Namespace + OS << "namespace " << RegisterClasses[0]->Namespace << " { // Register class instances\n"; for (unsigned i = 0, e = RegisterClasses.size(); i != e; ++i) - OS << " " << RegisterClasses[i].getName() << "Class\t" - << RegisterClasses[i].getName() << "RegClass;\n"; + OS << " " << RegisterClasses[i]->getName() << "Class\t" + << RegisterClasses[i]->getName() << "RegClass;\n"; - std::map<unsigned, std::set<unsigned> > SuperClassMap; std::map<unsigned, std::set<unsigned> > SuperRegClassMap; - OS << "\n"; + + OS << "\n static const TargetRegisterClass* const " + << "NullRegClasses[] = { NULL };\n\n"; unsigned NumSubRegIndices = RegBank.getSubRegIndices().size(); if (NumSubRegIndices) { - // Emit the sub-register classes for each RegisterClass + // Compute the super-register classes for each RegisterClass for (unsigned rc = 0, e = RegisterClasses.size(); rc != e; ++rc) { - const CodeGenRegisterClass &RC = RegisterClasses[rc]; - std::vector<Record*> SRC(NumSubRegIndices); + const CodeGenRegisterClass &RC = *RegisterClasses[rc]; for (DenseMap<Record*,Record*>::const_iterator i = RC.SubRegClasses.begin(), e = RC.SubRegClasses.end(); i != e; ++i) { - // Build SRC array. - unsigned idx = RegBank.getSubRegIndexNo(i->first); - SRC.at(idx-1) = i->second; - // Find the register class number of i->second for SuperRegClassMap. - for (unsigned rc2 = 0, e2 = RegisterClasses.size(); rc2 != e2; ++rc2) { - const CodeGenRegisterClass &RC2 = RegisterClasses[rc2]; - if (RC2.TheDef == i->second) { - SuperRegClassMap[rc2].insert(rc); - break; - } - } - } - - // Give the register class a legal C name if it's anonymous. - std::string Name = RC.TheDef->getName(); - - OS << " // " << Name - << " Sub-register Classes...\n" - << " static const TargetRegisterClass* const " - << Name << "SubRegClasses[] = {\n "; - - for (unsigned idx = 0; idx != NumSubRegIndices; ++idx) { - if (idx) - OS << ", "; - if (SRC[idx]) - OS << "&" << getQualifiedName(SRC[idx]) << "RegClass"; - else - OS << "0"; + const CodeGenRegisterClass *RC2 = RegBank.getRegClass(i->second); + assert(RC2 && "Invalid register class in SubRegClasses"); + SuperRegClassMap[RC2->EnumValue].insert(rc); } - OS << "\n };\n\n"; } // Emit the super-register classes for each RegisterClass for (unsigned rc = 0, e = RegisterClasses.size(); rc != e; ++rc) { - const CodeGenRegisterClass &RC = RegisterClasses[rc]; + const CodeGenRegisterClass &RC = *RegisterClasses[rc]; // Give the register class a legal C name if it's anonymous. - std::string Name = RC.TheDef->getName(); + std::string Name = RC.getName(); OS << " // " << Name << " Super-register Classes...\n" @@ -409,10 +574,10 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target, if (I != SuperRegClassMap.end()) { for (std::set<unsigned>::iterator II = I->second.begin(), EE = I->second.end(); II != EE; ++II) { - const CodeGenRegisterClass &RC2 = RegisterClasses[*II]; + const CodeGenRegisterClass &RC2 = *RegisterClasses[*II]; if (!Empty) OS << ", "; - OS << "&" << getQualifiedName(RC2.TheDef) << "RegClass"; + OS << "&" << RC2.getQualifiedName() << "RegClass"; Empty = false; } } @@ -420,97 +585,51 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target, OS << (!Empty ? ", " : "") << "NULL"; OS << "\n };\n\n"; } - } else { - // No subregindices in this target - OS << " static const TargetRegisterClass* const " - << "NullRegClasses[] = { NULL };\n\n"; } // Emit the sub-classes array for each RegisterClass for (unsigned rc = 0, e = RegisterClasses.size(); rc != e; ++rc) { - const CodeGenRegisterClass &RC = RegisterClasses[rc]; + const CodeGenRegisterClass &RC = *RegisterClasses[rc]; // Give the register class a legal C name if it's anonymous. - std::string Name = RC.TheDef->getName(); - - OS << " // " << Name - << " Register Class sub-classes...\n" - << " static const TargetRegisterClass* const " - << Name << "Subclasses[] = {\n "; - - bool Empty = true; - for (unsigned rc2 = 0, e2 = RegisterClasses.size(); rc2 != e2; ++rc2) { - const CodeGenRegisterClass &RC2 = RegisterClasses[rc2]; - - // Sub-classes are used to determine if a virtual register can be used - // as an instruction operand, or if it must be copied first. - if (rc == rc2 || !RC.hasSubClass(&RC2)) continue; - - if (!Empty) OS << ", "; - OS << "&" << getQualifiedName(RC2.TheDef) << "RegClass"; - Empty = false; - - std::map<unsigned, std::set<unsigned> >::iterator SCMI = - SuperClassMap.find(rc2); - if (SCMI == SuperClassMap.end()) { - SuperClassMap.insert(std::make_pair(rc2, std::set<unsigned>())); - SCMI = SuperClassMap.find(rc2); - } - SCMI->second.insert(rc); - } + std::string Name = RC.getName(); - OS << (!Empty ? ", " : "") << "NULL"; - OS << "\n };\n\n"; + OS << " static const unsigned " << Name << "SubclassMask[] = { "; + printBitVectorAsHex(OS, RC.getSubClasses(), 32); + OS << "};\n\n"; } + // Emit NULL terminated super-class lists. for (unsigned rc = 0, e = RegisterClasses.size(); rc != e; ++rc) { - const CodeGenRegisterClass &RC = RegisterClasses[rc]; + const CodeGenRegisterClass &RC = *RegisterClasses[rc]; + ArrayRef<CodeGenRegisterClass*> Supers = RC.getSuperClasses(); - // Give the register class a legal C name if it's anonymous. - std::string Name = RC.TheDef->getName(); - - OS << " // " << Name - << " Register Class super-classes...\n" - << " static const TargetRegisterClass* const " - << Name << "Superclasses[] = {\n "; - - bool Empty = true; - std::map<unsigned, std::set<unsigned> >::iterator I = - SuperClassMap.find(rc); - if (I != SuperClassMap.end()) { - for (std::set<unsigned>::iterator II = I->second.begin(), - EE = I->second.end(); II != EE; ++II) { - const CodeGenRegisterClass &RC2 = RegisterClasses[*II]; - if (!Empty) OS << ", "; - OS << "&" << getQualifiedName(RC2.TheDef) << "RegClass"; - Empty = false; - } - } + // Skip classes without supers. We can reuse NullRegClasses. + if (Supers.empty()) + continue; - OS << (!Empty ? ", " : "") << "NULL"; - OS << "\n };\n\n"; + OS << " static const TargetRegisterClass* const " + << RC.getName() << "Superclasses[] = {\n"; + for (unsigned i = 0; i != Supers.size(); ++i) + OS << " &" << Supers[i]->getQualifiedName() << "RegClass,\n"; + OS << " NULL\n };\n\n"; } // Emit methods. for (unsigned i = 0, e = RegisterClasses.size(); i != e; ++i) { - const CodeGenRegisterClass &RC = RegisterClasses[i]; + const CodeGenRegisterClass &RC = *RegisterClasses[i]; OS << RC.getName() << "Class::" << RC.getName() - << "Class() : TargetRegisterClass(" - << RC.getName() + "RegClassID" << ", " - << '\"' << RC.getName() << "\", " + << "Class() : TargetRegisterClass(&" + << Target.getName() << "MCRegisterClasses[" + << RC.getName() + "RegClassID" << "], " << RC.getName() + "VTs" << ", " - << RC.getName() + "Subclasses" << ", " - << RC.getName() + "Superclasses" << ", " - << (NumSubRegIndices ? RC.getName() + "Sub" : std::string("Null")) - << "RegClasses, " - << (NumSubRegIndices ? RC.getName() + "Super" : std::string("Null")) - << "RegClasses, " - << RC.SpillSize/8 << ", " - << RC.SpillAlignment/8 << ", " - << RC.CopyCost << ", " - << RC.Allocatable << ", " - << RC.getName() << ", " << RC.getName() << " + " - << RC.getOrder().size() + << RC.getName() + "SubclassMask" << ", "; + if (RC.getSuperClasses().empty()) + OS << "NullRegClasses, "; + else + OS << RC.getName() + "Superclasses, "; + OS << (NumSubRegIndices ? RC.getName() + "Super" : std::string("Null")) + << "RegClasses" << ") {}\n"; if (!RC.AltOrderSelect.empty()) { OS << "\nstatic inline unsigned " << RC.getName() @@ -525,10 +644,12 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target, OS << (elem ? ", " : " ") << getQualifiedName(Elems[elem]); OS << " };\n"; } - OS << " static const ArrayRef<unsigned> Order[] = {\n" - << " ArrayRef<unsigned>(" << RC.getName(); + OS << " const MCRegisterClass &MCR = " << Target.getName() + << "MCRegisterClasses[" << RC.getQualifiedName() + "RegClassID];" + << " static const ArrayRef<unsigned> Order[] = {\n" + << " makeArrayRef(MCR.begin(), MCR.getNumRegs()"; for (unsigned oi = 1, oe = RC.getNumOrders(); oi != oe; ++oi) - OS << "),\n ArrayRef<unsigned>(AltOrder" << oi; + OS << "),\n makeArrayRef(AltOrder" << oi; OS << ")\n };\n const unsigned Select = " << RC.getName() << "AltOrderSelect(MF);\n assert(Select < " << RC.getNumOrders() << ");\n return Order[Select];\n}\n"; @@ -541,7 +662,7 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target, OS << "\nnamespace {\n"; OS << " const TargetRegisterClass* const RegisterClasses[] = {\n"; for (unsigned i = 0, e = RegisterClasses.size(); i != e; ++i) - OS << " &" << getQualifiedName(RegisterClasses[i].TheDef) + OS << " &" << RegisterClasses[i]->getQualifiedName() << "RegClass,\n"; OS << " };\n"; OS << "}\n"; // End of anonymous namespace... @@ -658,106 +779,59 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target, } OS << " }\n}\n\n"; + // Emit getSubClassWithSubReg. + OS << "const TargetRegisterClass *" << ClassName + << "::getSubClassWithSubReg(const TargetRegisterClass *RC, unsigned Idx)" + " const {\n"; + if (SubRegIndices.empty()) { + OS << " assert(Idx == 0 && \"Target has no sub-registers\");\n" + << " return RC;\n"; + } else { + // Use the smallest type that can hold a regclass ID with room for a + // sentinel. + if (RegisterClasses.size() < UINT8_MAX) + OS << " static const uint8_t Table["; + else if (RegisterClasses.size() < UINT16_MAX) + OS << " static const uint16_t Table["; + else + throw "Too many register classes."; + OS << RegisterClasses.size() << "][" << SubRegIndices.size() << "] = {\n"; + for (unsigned rci = 0, rce = RegisterClasses.size(); rci != rce; ++rci) { + const CodeGenRegisterClass &RC = *RegisterClasses[rci]; + OS << " {\t// " << RC.getName() << "\n"; + for (unsigned sri = 0, sre = SubRegIndices.size(); sri != sre; ++sri) { + Record *Idx = SubRegIndices[sri]; + if (CodeGenRegisterClass *SRC = RC.getSubClassWithSubReg(Idx)) + OS << " " << SRC->EnumValue + 1 << ",\t// " << Idx->getName() + << " -> " << SRC->getName() << "\n"; + else + OS << " 0,\t// " << Idx->getName() << "\n"; + } + OS << " },\n"; + } + OS << " };\n assert(RC && \"Missing regclass\");\n" + << " if (!Idx) return RC;\n --Idx;\n" + << " assert(Idx < " << SubRegIndices.size() << " && \"Bad subreg\");\n" + << " unsigned TV = Table[RC->getID()][Idx];\n" + << " return TV ? getRegClass(TV - 1) : 0;\n"; + } + OS << "}\n\n"; + // Emit the constructor of the class... OS << "extern MCRegisterDesc " << TargetName << "RegDesc[];\n"; OS << ClassName << "::" << ClassName - << "()\n" + << "(unsigned RA, unsigned DwarfFlavour, unsigned EHFlavour)\n" << " : TargetRegisterInfo(" << TargetName << "RegInfoDesc" << ", RegisterClasses, RegisterClasses+" << RegisterClasses.size() <<",\n" << " " << TargetName << "SubRegIndexTable) {\n" << " InitMCRegisterInfo(" << TargetName << "RegDesc, " - << Regs.size()+1 << ");\n" - << "}\n\n"; + << Regs.size()+1 << ", RA, " << TargetName << "MCRegisterClasses, " + << RegisterClasses.size() << ");\n\n"; - // Collect all information about dwarf register numbers - typedef std::map<Record*, std::vector<int64_t>, LessRecord> DwarfRegNumsMapTy; - DwarfRegNumsMapTy DwarfRegNums; + EmitRegMapping(OS, Regs, true); - // First, just pull all provided information to the map - unsigned maxLength = 0; - for (unsigned i = 0, e = Regs.size(); i != e; ++i) { - Record *Reg = Regs[i]->TheDef; - std::vector<int64_t> RegNums = Reg->getValueAsListOfInts("DwarfNumbers"); - maxLength = std::max((size_t)maxLength, RegNums.size()); - if (DwarfRegNums.count(Reg)) - errs() << "Warning: DWARF numbers for register " << getQualifiedName(Reg) - << "specified multiple times\n"; - DwarfRegNums[Reg] = RegNums; - } - - // Now we know maximal length of number list. Append -1's, where needed - for (DwarfRegNumsMapTy::iterator - I = DwarfRegNums.begin(), E = DwarfRegNums.end(); I != E; ++I) - for (unsigned i = I->second.size(), e = maxLength; i != e; ++i) - I->second.push_back(-1); - - // Emit reverse information about the dwarf register numbers. - OS << "int " << ClassName << "::getLLVMRegNumFull(unsigned DwarfRegNum, " - << "unsigned Flavour) const {\n" - << " switch (Flavour) {\n" - << " default:\n" - << " assert(0 && \"Unknown DWARF flavour\");\n" - << " return -1;\n"; - - for (unsigned i = 0, e = maxLength; i != e; ++i) { - OS << " case " << i << ":\n" - << " switch (DwarfRegNum) {\n" - << " default:\n" - << " assert(0 && \"Invalid DwarfRegNum\");\n" - << " return -1;\n"; - - for (DwarfRegNumsMapTy::iterator - I = DwarfRegNums.begin(), E = DwarfRegNums.end(); I != E; ++I) { - int DwarfRegNo = I->second[i]; - if (DwarfRegNo >= 0) - OS << " case " << DwarfRegNo << ":\n" - << " return " << getQualifiedName(I->first) << ";\n"; - } - OS << " };\n"; - } - - OS << " };\n}\n\n"; - - for (unsigned i = 0, e = Regs.size(); i != e; ++i) { - Record *Reg = Regs[i]->TheDef; - const RecordVal *V = Reg->getValue("DwarfAlias"); - if (!V || !V->getValue()) - continue; - - DefInit *DI = dynamic_cast<DefInit*>(V->getValue()); - Record *Alias = DI->getDef(); - DwarfRegNums[Reg] = DwarfRegNums[Alias]; - } - - // Emit information about the dwarf register numbers. - OS << "int " << ClassName << "::getDwarfRegNumFull(unsigned RegNum, " - << "unsigned Flavour) const {\n" - << " switch (Flavour) {\n" - << " default:\n" - << " assert(0 && \"Unknown DWARF flavour\");\n" - << " return -1;\n"; - - for (unsigned i = 0, e = maxLength; i != e; ++i) { - OS << " case " << i << ":\n" - << " switch (RegNum) {\n" - << " default:\n" - << " assert(0 && \"Invalid RegNum\");\n" - << " return -1;\n"; - - // Sort by name to get a stable order. - - - for (DwarfRegNumsMapTy::iterator - I = DwarfRegNums.begin(), E = DwarfRegNums.end(); I != E; ++I) { - int RegNo = I->second[i]; - OS << " case " << getQualifiedName(I->first) << ":\n" - << " return " << RegNo << ";\n"; - } - OS << " };\n"; - } - - OS << " };\n}\n\n"; + OS << "}\n\n"; OS << "} // End llvm namespace \n"; OS << "#endif // GET_REGINFO_TARGET_DESC\n\n"; diff --git a/utils/TableGen/RegisterInfoEmitter.h b/utils/TableGen/RegisterInfoEmitter.h index 2c01b5c..0fd4d07 100644 --- a/utils/TableGen/RegisterInfoEmitter.h +++ b/utils/TableGen/RegisterInfoEmitter.h @@ -16,11 +16,13 @@ #ifndef REGISTER_INFO_EMITTER_H #define REGISTER_INFO_EMITTER_H -#include "TableGenBackend.h" +#include "llvm/TableGen/TableGenBackend.h" +#include <vector> namespace llvm { class CodeGenRegBank; +struct CodeGenRegister; class CodeGenTarget; class RegisterInfoEmitter : public TableGenBackend { @@ -44,6 +46,11 @@ public: // run - Output the register file description. void run(raw_ostream &o); + +private: + void EmitRegMapping(raw_ostream &o, + const std::vector<CodeGenRegister*> &Regs, bool isCtor); + void EmitRegClasses(raw_ostream &OS, CodeGenTarget &Target); }; } // End llvm namespace diff --git a/utils/TableGen/SetTheory.cpp b/utils/TableGen/SetTheory.cpp index 21ac09c..bef73f3 100644 --- a/utils/TableGen/SetTheory.cpp +++ b/utils/TableGen/SetTheory.cpp @@ -13,8 +13,8 @@ //===----------------------------------------------------------------------===// #include "SetTheory.h" -#include "Error.h" -#include "Record.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" #include "llvm/Support/Format.h" using namespace llvm; diff --git a/utils/TableGen/SubtargetEmitter.cpp b/utils/TableGen/SubtargetEmitter.cpp index 978e91a..103a403 100644 --- a/utils/TableGen/SubtargetEmitter.cpp +++ b/utils/TableGen/SubtargetEmitter.cpp @@ -13,7 +13,7 @@ #include "SubtargetEmitter.h" #include "CodeGenTarget.h" -#include "Record.h" +#include "llvm/TableGen/Record.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/Debug.h" #include <algorithm> diff --git a/utils/TableGen/SubtargetEmitter.h b/utils/TableGen/SubtargetEmitter.h index b239f3d..ff01274 100644 --- a/utils/TableGen/SubtargetEmitter.h +++ b/utils/TableGen/SubtargetEmitter.h @@ -14,7 +14,7 @@ #ifndef SUBTARGET_EMITTER_H #define SUBTARGET_EMITTER_H -#include "TableGenBackend.h" +#include "llvm/TableGen/TableGenBackend.h" #include "llvm/MC/MCInstrItineraries.h" #include <vector> #include <map> diff --git a/utils/TableGen/TGLexer.cpp b/utils/TableGen/TGLexer.cpp deleted file mode 100644 index b4b90ff..0000000 --- a/utils/TableGen/TGLexer.cpp +++ /dev/null @@ -1,435 +0,0 @@ -//===- TGLexer.cpp - Lexer for TableGen -----------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Implement the Lexer for TableGen. -// -//===----------------------------------------------------------------------===// - -#include "TGLexer.h" -#include "Error.h" -#include "llvm/Support/SourceMgr.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Config/config.h" -#include "llvm/ADT/StringSwitch.h" -#include "llvm/ADT/Twine.h" -#include <cctype> -#include <cstdio> -#include <cstdlib> -#include <cstring> -#include <cerrno> -using namespace llvm; - -TGLexer::TGLexer(SourceMgr &SM) : SrcMgr(SM) { - CurBuffer = 0; - CurBuf = SrcMgr.getMemoryBuffer(CurBuffer); - CurPtr = CurBuf->getBufferStart(); - TokStart = 0; -} - -SMLoc TGLexer::getLoc() const { - return SMLoc::getFromPointer(TokStart); -} - -/// ReturnError - Set the error to the specified string at the specified -/// location. This is defined to always return tgtok::Error. -tgtok::TokKind TGLexer::ReturnError(const char *Loc, const Twine &Msg) { - PrintError(Loc, Msg); - return tgtok::Error; -} - -int TGLexer::getNextChar() { - char CurChar = *CurPtr++; - switch (CurChar) { - default: - return (unsigned char)CurChar; - case 0: { - // A nul character in the stream is either the end of the current buffer or - // a random nul in the file. Disambiguate that here. - if (CurPtr-1 != CurBuf->getBufferEnd()) - return 0; // Just whitespace. - - // If this is the end of an included file, pop the parent file off the - // include stack. - SMLoc ParentIncludeLoc = SrcMgr.getParentIncludeLoc(CurBuffer); - if (ParentIncludeLoc != SMLoc()) { - CurBuffer = SrcMgr.FindBufferContainingLoc(ParentIncludeLoc); - CurBuf = SrcMgr.getMemoryBuffer(CurBuffer); - CurPtr = ParentIncludeLoc.getPointer(); - return getNextChar(); - } - - // Otherwise, return end of file. - --CurPtr; // Another call to lex will return EOF again. - return EOF; - } - case '\n': - case '\r': - // Handle the newline character by ignoring it and incrementing the line - // count. However, be careful about 'dos style' files with \n\r in them. - // Only treat a \n\r or \r\n as a single line. - if ((*CurPtr == '\n' || (*CurPtr == '\r')) && - *CurPtr != CurChar) - ++CurPtr; // Eat the two char newline sequence. - return '\n'; - } -} - -tgtok::TokKind TGLexer::LexToken() { - TokStart = CurPtr; - // This always consumes at least one character. - int CurChar = getNextChar(); - - switch (CurChar) { - default: - // Handle letters: [a-zA-Z_#] - if (isalpha(CurChar) || CurChar == '_' || CurChar == '#') - return LexIdentifier(); - - // Unknown character, emit an error. - return ReturnError(TokStart, "Unexpected character"); - case EOF: return tgtok::Eof; - case ':': return tgtok::colon; - case ';': return tgtok::semi; - case '.': return tgtok::period; - case ',': return tgtok::comma; - case '<': return tgtok::less; - case '>': return tgtok::greater; - case ']': return tgtok::r_square; - case '{': return tgtok::l_brace; - case '}': return tgtok::r_brace; - case '(': return tgtok::l_paren; - case ')': return tgtok::r_paren; - case '=': return tgtok::equal; - case '?': return tgtok::question; - - case 0: - case ' ': - case '\t': - case '\n': - case '\r': - // Ignore whitespace. - return LexToken(); - case '/': - // If this is the start of a // comment, skip until the end of the line or - // the end of the buffer. - if (*CurPtr == '/') - SkipBCPLComment(); - else if (*CurPtr == '*') { - if (SkipCComment()) - return tgtok::Error; - } else // Otherwise, this is an error. - return ReturnError(TokStart, "Unexpected character"); - return LexToken(); - case '-': case '+': - case '0': case '1': case '2': case '3': case '4': case '5': case '6': - case '7': case '8': case '9': - return LexNumber(); - case '"': return LexString(); - case '$': return LexVarName(); - case '[': return LexBracket(); - case '!': return LexExclaim(); - } -} - -/// LexString - Lex "[^"]*" -tgtok::TokKind TGLexer::LexString() { - const char *StrStart = CurPtr; - - CurStrVal = ""; - - while (*CurPtr != '"') { - // If we hit the end of the buffer, report an error. - if (*CurPtr == 0 && CurPtr == CurBuf->getBufferEnd()) - return ReturnError(StrStart, "End of file in string literal"); - - if (*CurPtr == '\n' || *CurPtr == '\r') - return ReturnError(StrStart, "End of line in string literal"); - - if (*CurPtr != '\\') { - CurStrVal += *CurPtr++; - continue; - } - - ++CurPtr; - - switch (*CurPtr) { - case '\\': case '\'': case '"': - // These turn into their literal character. - CurStrVal += *CurPtr++; - break; - case 't': - CurStrVal += '\t'; - ++CurPtr; - break; - case 'n': - CurStrVal += '\n'; - ++CurPtr; - break; - - case '\n': - case '\r': - return ReturnError(CurPtr, "escaped newlines not supported in tblgen"); - - // If we hit the end of the buffer, report an error. - case '\0': - if (CurPtr == CurBuf->getBufferEnd()) - return ReturnError(StrStart, "End of file in string literal"); - // FALL THROUGH - default: - return ReturnError(CurPtr, "invalid escape in string literal"); - } - } - - ++CurPtr; - return tgtok::StrVal; -} - -tgtok::TokKind TGLexer::LexVarName() { - if (!isalpha(CurPtr[0]) && CurPtr[0] != '_') - return ReturnError(TokStart, "Invalid variable name"); - - // Otherwise, we're ok, consume the rest of the characters. - const char *VarNameStart = CurPtr++; - - while (isalpha(*CurPtr) || isdigit(*CurPtr) || *CurPtr == '_') - ++CurPtr; - - CurStrVal.assign(VarNameStart, CurPtr); - return tgtok::VarName; -} - - -tgtok::TokKind TGLexer::LexIdentifier() { - // The first letter is [a-zA-Z_#]. - const char *IdentStart = TokStart; - - // Match the rest of the identifier regex: [0-9a-zA-Z_#]* - while (isalpha(*CurPtr) || isdigit(*CurPtr) || *CurPtr == '_' || - *CurPtr == '#') - ++CurPtr; - - - // Check to see if this identifier is a keyword. - unsigned Len = CurPtr-IdentStart; - - if (Len == 3 && !memcmp(IdentStart, "int", 3)) return tgtok::Int; - if (Len == 3 && !memcmp(IdentStart, "bit", 3)) return tgtok::Bit; - if (Len == 4 && !memcmp(IdentStart, "bits", 4)) return tgtok::Bits; - if (Len == 6 && !memcmp(IdentStart, "string", 6)) return tgtok::String; - if (Len == 4 && !memcmp(IdentStart, "list", 4)) return tgtok::List; - if (Len == 4 && !memcmp(IdentStart, "code", 4)) return tgtok::Code; - if (Len == 3 && !memcmp(IdentStart, "dag", 3)) return tgtok::Dag; - - if (Len == 5 && !memcmp(IdentStart, "class", 5)) return tgtok::Class; - if (Len == 3 && !memcmp(IdentStart, "def", 3)) return tgtok::Def; - if (Len == 4 && !memcmp(IdentStart, "defm", 4)) return tgtok::Defm; - if (Len == 10 && !memcmp(IdentStart, "multiclass", 10)) - return tgtok::MultiClass; - if (Len == 5 && !memcmp(IdentStart, "field", 5)) return tgtok::Field; - if (Len == 3 && !memcmp(IdentStart, "let", 3)) return tgtok::Let; - if (Len == 2 && !memcmp(IdentStart, "in", 2)) return tgtok::In; - - if (Len == 7 && !memcmp(IdentStart, "include", 7)) { - if (LexInclude()) return tgtok::Error; - return Lex(); - } - - CurStrVal.assign(IdentStart, CurPtr); - return tgtok::Id; -} - -/// LexInclude - We just read the "include" token. Get the string token that -/// comes next and enter the include. -bool TGLexer::LexInclude() { - // The token after the include must be a string. - tgtok::TokKind Tok = LexToken(); - if (Tok == tgtok::Error) return true; - if (Tok != tgtok::StrVal) { - PrintError(getLoc(), "Expected filename after include"); - return true; - } - - // Get the string. - std::string Filename = CurStrVal; - std::string IncludedFile; - - - CurBuffer = SrcMgr.AddIncludeFile(Filename, SMLoc::getFromPointer(CurPtr), - IncludedFile); - if (CurBuffer == -1) { - PrintError(getLoc(), "Could not find include file '" + Filename + "'"); - return true; - } - - Dependencies.push_back(IncludedFile); - // Save the line number and lex buffer of the includer. - CurBuf = SrcMgr.getMemoryBuffer(CurBuffer); - CurPtr = CurBuf->getBufferStart(); - return false; -} - -void TGLexer::SkipBCPLComment() { - ++CurPtr; // skip the second slash. - while (1) { - switch (*CurPtr) { - case '\n': - case '\r': - return; // Newline is end of comment. - case 0: - // If this is the end of the buffer, end the comment. - if (CurPtr == CurBuf->getBufferEnd()) - return; - break; - } - // Otherwise, skip the character. - ++CurPtr; - } -} - -/// SkipCComment - This skips C-style /**/ comments. The only difference from C -/// is that we allow nesting. -bool TGLexer::SkipCComment() { - ++CurPtr; // skip the star. - unsigned CommentDepth = 1; - - while (1) { - int CurChar = getNextChar(); - switch (CurChar) { - case EOF: - PrintError(TokStart, "Unterminated comment!"); - return true; - case '*': - // End of the comment? - if (CurPtr[0] != '/') break; - - ++CurPtr; // End the */. - if (--CommentDepth == 0) - return false; - break; - case '/': - // Start of a nested comment? - if (CurPtr[0] != '*') break; - ++CurPtr; - ++CommentDepth; - break; - } - } -} - -/// LexNumber - Lex: -/// [-+]?[0-9]+ -/// 0x[0-9a-fA-F]+ -/// 0b[01]+ -tgtok::TokKind TGLexer::LexNumber() { - if (CurPtr[-1] == '0') { - if (CurPtr[0] == 'x') { - ++CurPtr; - const char *NumStart = CurPtr; - while (isxdigit(CurPtr[0])) - ++CurPtr; - - // Requires at least one hex digit. - if (CurPtr == NumStart) - return ReturnError(TokStart, "Invalid hexadecimal number"); - - errno = 0; - CurIntVal = strtoll(NumStart, 0, 16); - if (errno == EINVAL) - return ReturnError(TokStart, "Invalid hexadecimal number"); - if (errno == ERANGE) { - errno = 0; - CurIntVal = (int64_t)strtoull(NumStart, 0, 16); - if (errno == EINVAL) - return ReturnError(TokStart, "Invalid hexadecimal number"); - if (errno == ERANGE) - return ReturnError(TokStart, "Hexadecimal number out of range"); - } - return tgtok::IntVal; - } else if (CurPtr[0] == 'b') { - ++CurPtr; - const char *NumStart = CurPtr; - while (CurPtr[0] == '0' || CurPtr[0] == '1') - ++CurPtr; - - // Requires at least one binary digit. - if (CurPtr == NumStart) - return ReturnError(CurPtr-2, "Invalid binary number"); - CurIntVal = strtoll(NumStart, 0, 2); - return tgtok::IntVal; - } - } - - // Check for a sign without a digit. - if (!isdigit(CurPtr[0])) { - if (CurPtr[-1] == '-') - return tgtok::minus; - else if (CurPtr[-1] == '+') - return tgtok::plus; - } - - while (isdigit(CurPtr[0])) - ++CurPtr; - CurIntVal = strtoll(TokStart, 0, 10); - return tgtok::IntVal; -} - -/// LexBracket - We just read '['. If this is a code block, return it, -/// otherwise return the bracket. Match: '[' and '[{ ( [^}]+ | }[^]] )* }]' -tgtok::TokKind TGLexer::LexBracket() { - if (CurPtr[0] != '{') - return tgtok::l_square; - ++CurPtr; - const char *CodeStart = CurPtr; - while (1) { - int Char = getNextChar(); - if (Char == EOF) break; - - if (Char != '}') continue; - - Char = getNextChar(); - if (Char == EOF) break; - if (Char == ']') { - CurStrVal.assign(CodeStart, CurPtr-2); - return tgtok::CodeFragment; - } - } - - return ReturnError(CodeStart-2, "Unterminated Code Block"); -} - -/// LexExclaim - Lex '!' and '![a-zA-Z]+'. -tgtok::TokKind TGLexer::LexExclaim() { - if (!isalpha(*CurPtr)) - return ReturnError(CurPtr - 1, "Invalid \"!operator\""); - - const char *Start = CurPtr++; - while (isalpha(*CurPtr)) - ++CurPtr; - - // Check to see which operator this is. - tgtok::TokKind Kind = - StringSwitch<tgtok::TokKind>(StringRef(Start, CurPtr - Start)) - .Case("eq", tgtok::XEq) - .Case("if", tgtok::XIf) - .Case("head", tgtok::XHead) - .Case("tail", tgtok::XTail) - .Case("con", tgtok::XConcat) - .Case("shl", tgtok::XSHL) - .Case("sra", tgtok::XSRA) - .Case("srl", tgtok::XSRL) - .Case("cast", tgtok::XCast) - .Case("empty", tgtok::XEmpty) - .Case("subst", tgtok::XSubst) - .Case("foreach", tgtok::XForEach) - .Case("strconcat", tgtok::XStrConcat) - .Default(tgtok::Error); - - return Kind != tgtok::Error ? Kind : ReturnError(Start-1, "Unknown operator"); -} - diff --git a/utils/TableGen/TGLexer.h b/utils/TableGen/TGLexer.h deleted file mode 100644 index 84d328b..0000000 --- a/utils/TableGen/TGLexer.h +++ /dev/null @@ -1,125 +0,0 @@ -//===- TGLexer.h - Lexer for TableGen Files ---------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This class represents the Lexer for tablegen files. -// -//===----------------------------------------------------------------------===// - -#ifndef TGLEXER_H -#define TGLEXER_H - -#include "llvm/Support/DataTypes.h" -#include <string> -#include <vector> -#include <cassert> - -namespace llvm { -class MemoryBuffer; -class SourceMgr; -class SMLoc; -class Twine; - -namespace tgtok { - enum TokKind { - // Markers - Eof, Error, - - // Tokens with no info. - minus, plus, // - + - l_square, r_square, // [ ] - l_brace, r_brace, // { } - l_paren, r_paren, // ( ) - less, greater, // < > - colon, semi, // : ; - comma, period, // , . - equal, question, // = ? - - // Keywords. - Bit, Bits, Class, Code, Dag, Def, Defm, Field, In, Int, Let, List, - MultiClass, String, - - // !keywords. - XConcat, XSRA, XSRL, XSHL, XStrConcat, XCast, XSubst, - XForEach, XHead, XTail, XEmpty, XIf, XEq, - - // Integer value. - IntVal, - - // String valued tokens. - Id, StrVal, VarName, CodeFragment - }; -} - -/// TGLexer - TableGen Lexer class. -class TGLexer { - SourceMgr &SrcMgr; - - const char *CurPtr; - const MemoryBuffer *CurBuf; - - // Information about the current token. - const char *TokStart; - tgtok::TokKind CurCode; - std::string CurStrVal; // This is valid for ID, STRVAL, VARNAME, CODEFRAGMENT - int64_t CurIntVal; // This is valid for INTVAL. - - /// CurBuffer - This is the current buffer index we're lexing from as managed - /// by the SourceMgr object. - int CurBuffer; - /// Dependencies - This is the list of all included files. - std::vector<std::string> Dependencies; - -public: - TGLexer(SourceMgr &SrcMgr); - ~TGLexer() {} - - tgtok::TokKind Lex() { - return CurCode = LexToken(); - } - - const std::vector<std::string> &getDependencies() const { - return Dependencies; - } - - tgtok::TokKind getCode() const { return CurCode; } - - const std::string &getCurStrVal() const { - assert((CurCode == tgtok::Id || CurCode == tgtok::StrVal || - CurCode == tgtok::VarName || CurCode == tgtok::CodeFragment) && - "This token doesn't have a string value"); - return CurStrVal; - } - int64_t getCurIntVal() const { - assert(CurCode == tgtok::IntVal && "This token isn't an integer"); - return CurIntVal; - } - - SMLoc getLoc() const; - -private: - /// LexToken - Read the next token and return its code. - tgtok::TokKind LexToken(); - - tgtok::TokKind ReturnError(const char *Loc, const Twine &Msg); - - int getNextChar(); - void SkipBCPLComment(); - bool SkipCComment(); - tgtok::TokKind LexIdentifier(); - bool LexInclude(); - tgtok::TokKind LexString(); - tgtok::TokKind LexVarName(); - tgtok::TokKind LexNumber(); - tgtok::TokKind LexBracket(); - tgtok::TokKind LexExclaim(); -}; - -} // end namespace llvm - -#endif diff --git a/utils/TableGen/TGParser.cpp b/utils/TableGen/TGParser.cpp deleted file mode 100644 index 59097f9..0000000 --- a/utils/TableGen/TGParser.cpp +++ /dev/null @@ -1,2152 +0,0 @@ -//===- TGParser.cpp - Parser for TableGen Files ---------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Implement the Parser for TableGen. -// -//===----------------------------------------------------------------------===// - -#include "TGParser.h" -#include "Record.h" -#include "llvm/ADT/StringExtras.h" -#include <algorithm> -#include <sstream> -#include "llvm/ADT/SmallVector.h" -#include "llvm/Support/CommandLine.h" -using namespace llvm; - -//===----------------------------------------------------------------------===// -// Support Code for the Semantic Actions. -//===----------------------------------------------------------------------===// - -namespace llvm { -struct SubClassReference { - SMLoc RefLoc; - Record *Rec; - std::vector<Init*> TemplateArgs; - SubClassReference() : Rec(0) {} - - bool isInvalid() const { return Rec == 0; } -}; - -struct SubMultiClassReference { - SMLoc RefLoc; - MultiClass *MC; - std::vector<Init*> TemplateArgs; - SubMultiClassReference() : MC(0) {} - - bool isInvalid() const { return MC == 0; } - void dump() const; -}; - -void SubMultiClassReference::dump() const { - errs() << "Multiclass:\n"; - - MC->dump(); - - errs() << "Template args:\n"; - for (std::vector<Init *>::const_iterator i = TemplateArgs.begin(), - iend = TemplateArgs.end(); - i != iend; - ++i) { - (*i)->dump(); - } -} - -} // end namespace llvm - -bool TGParser::AddValue(Record *CurRec, SMLoc Loc, const RecordVal &RV) { - if (CurRec == 0) - CurRec = &CurMultiClass->Rec; - - if (RecordVal *ERV = CurRec->getValue(RV.getName())) { - // The value already exists in the class, treat this as a set. - if (ERV->setValue(RV.getValue())) - return Error(Loc, "New definition of '" + RV.getName() + "' of type '" + - RV.getType()->getAsString() + "' is incompatible with " + - "previous definition of type '" + - ERV->getType()->getAsString() + "'"); - } else { - CurRec->addValue(RV); - } - return false; -} - -/// SetValue - -/// Return true on error, false on success. -bool TGParser::SetValue(Record *CurRec, SMLoc Loc, const std::string &ValName, - const std::vector<unsigned> &BitList, Init *V) { - if (!V) return false; - - if (CurRec == 0) CurRec = &CurMultiClass->Rec; - - RecordVal *RV = CurRec->getValue(ValName); - if (RV == 0) - return Error(Loc, "Value '" + ValName + "' unknown!"); - - // Do not allow assignments like 'X = X'. This will just cause infinite loops - // in the resolution machinery. - if (BitList.empty()) - if (VarInit *VI = dynamic_cast<VarInit*>(V)) - if (VI->getName() == ValName) - return false; - - // If we are assigning to a subset of the bits in the value... then we must be - // assigning to a field of BitsRecTy, which must have a BitsInit - // initializer. - // - if (!BitList.empty()) { - BitsInit *CurVal = dynamic_cast<BitsInit*>(RV->getValue()); - if (CurVal == 0) - return Error(Loc, "Value '" + ValName + "' is not a bits type"); - - // Convert the incoming value to a bits type of the appropriate size... - Init *BI = V->convertInitializerTo(new BitsRecTy(BitList.size())); - if (BI == 0) { - V->convertInitializerTo(new BitsRecTy(BitList.size())); - return Error(Loc, "Initializer is not compatible with bit range"); - } - - // We should have a BitsInit type now. - BitsInit *BInit = dynamic_cast<BitsInit*>(BI); - assert(BInit != 0); - - BitsInit *NewVal = new BitsInit(CurVal->getNumBits()); - - // Loop over bits, assigning values as appropriate. - for (unsigned i = 0, e = BitList.size(); i != e; ++i) { - unsigned Bit = BitList[i]; - if (NewVal->getBit(Bit)) - return Error(Loc, "Cannot set bit #" + utostr(Bit) + " of value '" + - ValName + "' more than once"); - NewVal->setBit(Bit, BInit->getBit(i)); - } - - for (unsigned i = 0, e = CurVal->getNumBits(); i != e; ++i) - if (NewVal->getBit(i) == 0) - NewVal->setBit(i, CurVal->getBit(i)); - - V = NewVal; - } - - if (RV->setValue(V)) - return Error(Loc, "Value '" + ValName + "' of type '" + - RV->getType()->getAsString() + - "' is incompatible with initializer '" + V->getAsString() +"'"); - return false; -} - -/// AddSubClass - Add SubClass as a subclass to CurRec, resolving its template -/// args as SubClass's template arguments. -bool TGParser::AddSubClass(Record *CurRec, SubClassReference &SubClass) { - Record *SC = SubClass.Rec; - // Add all of the values in the subclass into the current class. - const std::vector<RecordVal> &Vals = SC->getValues(); - for (unsigned i = 0, e = Vals.size(); i != e; ++i) - if (AddValue(CurRec, SubClass.RefLoc, Vals[i])) - return true; - - const std::vector<std::string> &TArgs = SC->getTemplateArgs(); - - // Ensure that an appropriate number of template arguments are specified. - if (TArgs.size() < SubClass.TemplateArgs.size()) - return Error(SubClass.RefLoc, "More template args specified than expected"); - - // Loop over all of the template arguments, setting them to the specified - // value or leaving them as the default if necessary. - for (unsigned i = 0, e = TArgs.size(); i != e; ++i) { - if (i < SubClass.TemplateArgs.size()) { - // If a value is specified for this template arg, set it now. - if (SetValue(CurRec, SubClass.RefLoc, TArgs[i], std::vector<unsigned>(), - SubClass.TemplateArgs[i])) - return true; - - // Resolve it next. - CurRec->resolveReferencesTo(CurRec->getValue(TArgs[i])); - - // Now remove it. - CurRec->removeValue(TArgs[i]); - - } else if (!CurRec->getValue(TArgs[i])->getValue()->isComplete()) { - return Error(SubClass.RefLoc,"Value not specified for template argument #" - + utostr(i) + " (" + TArgs[i] + ") of subclass '" + - SC->getName() + "'!"); - } - } - - // Since everything went well, we can now set the "superclass" list for the - // current record. - const std::vector<Record*> &SCs = SC->getSuperClasses(); - for (unsigned i = 0, e = SCs.size(); i != e; ++i) { - if (CurRec->isSubClassOf(SCs[i])) - return Error(SubClass.RefLoc, - "Already subclass of '" + SCs[i]->getName() + "'!\n"); - CurRec->addSuperClass(SCs[i]); - } - - if (CurRec->isSubClassOf(SC)) - return Error(SubClass.RefLoc, - "Already subclass of '" + SC->getName() + "'!\n"); - CurRec->addSuperClass(SC); - return false; -} - -/// AddSubMultiClass - Add SubMultiClass as a subclass to -/// CurMC, resolving its template args as SubMultiClass's -/// template arguments. -bool TGParser::AddSubMultiClass(MultiClass *CurMC, - SubMultiClassReference &SubMultiClass) { - MultiClass *SMC = SubMultiClass.MC; - Record *CurRec = &CurMC->Rec; - - const std::vector<RecordVal> &MCVals = CurRec->getValues(); - - // Add all of the values in the subclass into the current class. - const std::vector<RecordVal> &SMCVals = SMC->Rec.getValues(); - for (unsigned i = 0, e = SMCVals.size(); i != e; ++i) - if (AddValue(CurRec, SubMultiClass.RefLoc, SMCVals[i])) - return true; - - int newDefStart = CurMC->DefPrototypes.size(); - - // Add all of the defs in the subclass into the current multiclass. - for (MultiClass::RecordVector::const_iterator i = SMC->DefPrototypes.begin(), - iend = SMC->DefPrototypes.end(); - i != iend; - ++i) { - // Clone the def and add it to the current multiclass - Record *NewDef = new Record(**i); - - // Add all of the values in the superclass into the current def. - for (unsigned i = 0, e = MCVals.size(); i != e; ++i) - if (AddValue(NewDef, SubMultiClass.RefLoc, MCVals[i])) - return true; - - CurMC->DefPrototypes.push_back(NewDef); - } - - const std::vector<std::string> &SMCTArgs = SMC->Rec.getTemplateArgs(); - - // Ensure that an appropriate number of template arguments are - // specified. - if (SMCTArgs.size() < SubMultiClass.TemplateArgs.size()) - return Error(SubMultiClass.RefLoc, - "More template args specified than expected"); - - // Loop over all of the template arguments, setting them to the specified - // value or leaving them as the default if necessary. - for (unsigned i = 0, e = SMCTArgs.size(); i != e; ++i) { - if (i < SubMultiClass.TemplateArgs.size()) { - // If a value is specified for this template arg, set it in the - // superclass now. - if (SetValue(CurRec, SubMultiClass.RefLoc, SMCTArgs[i], - std::vector<unsigned>(), - SubMultiClass.TemplateArgs[i])) - return true; - - // Resolve it next. - CurRec->resolveReferencesTo(CurRec->getValue(SMCTArgs[i])); - - // Now remove it. - CurRec->removeValue(SMCTArgs[i]); - - // If a value is specified for this template arg, set it in the - // new defs now. - for (MultiClass::RecordVector::iterator j = - CurMC->DefPrototypes.begin() + newDefStart, - jend = CurMC->DefPrototypes.end(); - j != jend; - ++j) { - Record *Def = *j; - - if (SetValue(Def, SubMultiClass.RefLoc, SMCTArgs[i], - std::vector<unsigned>(), - SubMultiClass.TemplateArgs[i])) - return true; - - // Resolve it next. - Def->resolveReferencesTo(Def->getValue(SMCTArgs[i])); - - // Now remove it - Def->removeValue(SMCTArgs[i]); - } - } else if (!CurRec->getValue(SMCTArgs[i])->getValue()->isComplete()) { - return Error(SubMultiClass.RefLoc, - "Value not specified for template argument #" - + utostr(i) + " (" + SMCTArgs[i] + ") of subclass '" + - SMC->Rec.getName() + "'!"); - } - } - - return false; -} - -//===----------------------------------------------------------------------===// -// Parser Code -//===----------------------------------------------------------------------===// - -/// isObjectStart - Return true if this is a valid first token for an Object. -static bool isObjectStart(tgtok::TokKind K) { - return K == tgtok::Class || K == tgtok::Def || - K == tgtok::Defm || K == tgtok::Let || K == tgtok::MultiClass; -} - -static std::string GetNewAnonymousName() { - static unsigned AnonCounter = 0; - return "anonymous."+utostr(AnonCounter++); -} - -/// ParseObjectName - If an object name is specified, return it. Otherwise, -/// return an anonymous name. -/// ObjectName ::= ID -/// ObjectName ::= /*empty*/ -/// -std::string TGParser::ParseObjectName() { - if (Lex.getCode() != tgtok::Id) - return GetNewAnonymousName(); - - std::string Ret = Lex.getCurStrVal(); - Lex.Lex(); - return Ret; -} - - -/// ParseClassID - Parse and resolve a reference to a class name. This returns -/// null on error. -/// -/// ClassID ::= ID -/// -Record *TGParser::ParseClassID() { - if (Lex.getCode() != tgtok::Id) { - TokError("expected name for ClassID"); - return 0; - } - - Record *Result = Records.getClass(Lex.getCurStrVal()); - if (Result == 0) - TokError("Couldn't find class '" + Lex.getCurStrVal() + "'"); - - Lex.Lex(); - return Result; -} - -/// ParseMultiClassID - Parse and resolve a reference to a multiclass name. -/// This returns null on error. -/// -/// MultiClassID ::= ID -/// -MultiClass *TGParser::ParseMultiClassID() { - if (Lex.getCode() != tgtok::Id) { - TokError("expected name for ClassID"); - return 0; - } - - MultiClass *Result = MultiClasses[Lex.getCurStrVal()]; - if (Result == 0) - TokError("Couldn't find class '" + Lex.getCurStrVal() + "'"); - - Lex.Lex(); - return Result; -} - -Record *TGParser::ParseDefmID() { - if (Lex.getCode() != tgtok::Id) { - TokError("expected multiclass name"); - return 0; - } - - MultiClass *MC = MultiClasses[Lex.getCurStrVal()]; - if (MC == 0) { - TokError("Couldn't find multiclass '" + Lex.getCurStrVal() + "'"); - return 0; - } - - Lex.Lex(); - return &MC->Rec; -} - - -/// ParseSubClassReference - Parse a reference to a subclass or to a templated -/// subclass. This returns a SubClassRefTy with a null Record* on error. -/// -/// SubClassRef ::= ClassID -/// SubClassRef ::= ClassID '<' ValueList '>' -/// -SubClassReference TGParser:: -ParseSubClassReference(Record *CurRec, bool isDefm) { - SubClassReference Result; - Result.RefLoc = Lex.getLoc(); - - if (isDefm) - Result.Rec = ParseDefmID(); - else - Result.Rec = ParseClassID(); - if (Result.Rec == 0) return Result; - - // If there is no template arg list, we're done. - if (Lex.getCode() != tgtok::less) - return Result; - Lex.Lex(); // Eat the '<' - - if (Lex.getCode() == tgtok::greater) { - TokError("subclass reference requires a non-empty list of template values"); - Result.Rec = 0; - return Result; - } - - Result.TemplateArgs = ParseValueList(CurRec, Result.Rec); - if (Result.TemplateArgs.empty()) { - Result.Rec = 0; // Error parsing value list. - return Result; - } - - if (Lex.getCode() != tgtok::greater) { - TokError("expected '>' in template value list"); - Result.Rec = 0; - return Result; - } - Lex.Lex(); - - return Result; -} - -/// ParseSubMultiClassReference - Parse a reference to a subclass or to a -/// templated submulticlass. This returns a SubMultiClassRefTy with a null -/// Record* on error. -/// -/// SubMultiClassRef ::= MultiClassID -/// SubMultiClassRef ::= MultiClassID '<' ValueList '>' -/// -SubMultiClassReference TGParser:: -ParseSubMultiClassReference(MultiClass *CurMC) { - SubMultiClassReference Result; - Result.RefLoc = Lex.getLoc(); - - Result.MC = ParseMultiClassID(); - if (Result.MC == 0) return Result; - - // If there is no template arg list, we're done. - if (Lex.getCode() != tgtok::less) - return Result; - Lex.Lex(); // Eat the '<' - - if (Lex.getCode() == tgtok::greater) { - TokError("subclass reference requires a non-empty list of template values"); - Result.MC = 0; - return Result; - } - - Result.TemplateArgs = ParseValueList(&CurMC->Rec, &Result.MC->Rec); - if (Result.TemplateArgs.empty()) { - Result.MC = 0; // Error parsing value list. - return Result; - } - - if (Lex.getCode() != tgtok::greater) { - TokError("expected '>' in template value list"); - Result.MC = 0; - return Result; - } - Lex.Lex(); - - return Result; -} - -/// ParseRangePiece - Parse a bit/value range. -/// RangePiece ::= INTVAL -/// RangePiece ::= INTVAL '-' INTVAL -/// RangePiece ::= INTVAL INTVAL -bool TGParser::ParseRangePiece(std::vector<unsigned> &Ranges) { - if (Lex.getCode() != tgtok::IntVal) { - TokError("expected integer or bitrange"); - return true; - } - int64_t Start = Lex.getCurIntVal(); - int64_t End; - - if (Start < 0) - return TokError("invalid range, cannot be negative"); - - switch (Lex.Lex()) { // eat first character. - default: - Ranges.push_back(Start); - return false; - case tgtok::minus: - if (Lex.Lex() != tgtok::IntVal) { - TokError("expected integer value as end of range"); - return true; - } - End = Lex.getCurIntVal(); - break; - case tgtok::IntVal: - End = -Lex.getCurIntVal(); - break; - } - if (End < 0) - return TokError("invalid range, cannot be negative"); - Lex.Lex(); - - // Add to the range. - if (Start < End) { - for (; Start <= End; ++Start) - Ranges.push_back(Start); - } else { - for (; Start >= End; --Start) - Ranges.push_back(Start); - } - return false; -} - -/// ParseRangeList - Parse a list of scalars and ranges into scalar values. -/// -/// RangeList ::= RangePiece (',' RangePiece)* -/// -std::vector<unsigned> TGParser::ParseRangeList() { - std::vector<unsigned> Result; - - // Parse the first piece. - if (ParseRangePiece(Result)) - return std::vector<unsigned>(); - while (Lex.getCode() == tgtok::comma) { - Lex.Lex(); // Eat the comma. - - // Parse the next range piece. - if (ParseRangePiece(Result)) - return std::vector<unsigned>(); - } - return Result; -} - -/// ParseOptionalRangeList - Parse either a range list in <>'s or nothing. -/// OptionalRangeList ::= '<' RangeList '>' -/// OptionalRangeList ::= /*empty*/ -bool TGParser::ParseOptionalRangeList(std::vector<unsigned> &Ranges) { - if (Lex.getCode() != tgtok::less) - return false; - - SMLoc StartLoc = Lex.getLoc(); - Lex.Lex(); // eat the '<' - - // Parse the range list. - Ranges = ParseRangeList(); - if (Ranges.empty()) return true; - - if (Lex.getCode() != tgtok::greater) { - TokError("expected '>' at end of range list"); - return Error(StartLoc, "to match this '<'"); - } - Lex.Lex(); // eat the '>'. - return false; -} - -/// ParseOptionalBitList - Parse either a bit list in {}'s or nothing. -/// OptionalBitList ::= '{' RangeList '}' -/// OptionalBitList ::= /*empty*/ -bool TGParser::ParseOptionalBitList(std::vector<unsigned> &Ranges) { - if (Lex.getCode() != tgtok::l_brace) - return false; - - SMLoc StartLoc = Lex.getLoc(); - Lex.Lex(); // eat the '{' - - // Parse the range list. - Ranges = ParseRangeList(); - if (Ranges.empty()) return true; - - if (Lex.getCode() != tgtok::r_brace) { - TokError("expected '}' at end of bit list"); - return Error(StartLoc, "to match this '{'"); - } - Lex.Lex(); // eat the '}'. - return false; -} - - -/// ParseType - Parse and return a tblgen type. This returns null on error. -/// -/// Type ::= STRING // string type -/// Type ::= BIT // bit type -/// Type ::= BITS '<' INTVAL '>' // bits<x> type -/// Type ::= INT // int type -/// Type ::= LIST '<' Type '>' // list<x> type -/// Type ::= CODE // code type -/// Type ::= DAG // dag type -/// Type ::= ClassID // Record Type -/// -RecTy *TGParser::ParseType() { - switch (Lex.getCode()) { - default: TokError("Unknown token when expecting a type"); return 0; - case tgtok::String: Lex.Lex(); return new StringRecTy(); - case tgtok::Bit: Lex.Lex(); return new BitRecTy(); - case tgtok::Int: Lex.Lex(); return new IntRecTy(); - case tgtok::Code: Lex.Lex(); return new CodeRecTy(); - case tgtok::Dag: Lex.Lex(); return new DagRecTy(); - case tgtok::Id: - if (Record *R = ParseClassID()) return new RecordRecTy(R); - return 0; - case tgtok::Bits: { - if (Lex.Lex() != tgtok::less) { // Eat 'bits' - TokError("expected '<' after bits type"); - return 0; - } - if (Lex.Lex() != tgtok::IntVal) { // Eat '<' - TokError("expected integer in bits<n> type"); - return 0; - } - uint64_t Val = Lex.getCurIntVal(); - if (Lex.Lex() != tgtok::greater) { // Eat count. - TokError("expected '>' at end of bits<n> type"); - return 0; - } - Lex.Lex(); // Eat '>' - return new BitsRecTy(Val); - } - case tgtok::List: { - if (Lex.Lex() != tgtok::less) { // Eat 'bits' - TokError("expected '<' after list type"); - return 0; - } - Lex.Lex(); // Eat '<' - RecTy *SubType = ParseType(); - if (SubType == 0) return 0; - - if (Lex.getCode() != tgtok::greater) { - TokError("expected '>' at end of list<ty> type"); - return 0; - } - Lex.Lex(); // Eat '>' - return new ListRecTy(SubType); - } - } -} - -/// ParseIDValue - Parse an ID as a value and decode what it means. -/// -/// IDValue ::= ID [def local value] -/// IDValue ::= ID [def template arg] -/// IDValue ::= ID [multiclass local value] -/// IDValue ::= ID [multiclass template argument] -/// IDValue ::= ID [def name] -/// -Init *TGParser::ParseIDValue(Record *CurRec) { - assert(Lex.getCode() == tgtok::Id && "Expected ID in ParseIDValue"); - std::string Name = Lex.getCurStrVal(); - SMLoc Loc = Lex.getLoc(); - Lex.Lex(); - return ParseIDValue(CurRec, Name, Loc); -} - -/// ParseIDValue - This is just like ParseIDValue above, but it assumes the ID -/// has already been read. -Init *TGParser::ParseIDValue(Record *CurRec, - const std::string &Name, SMLoc NameLoc) { - if (CurRec) { - if (const RecordVal *RV = CurRec->getValue(Name)) - return new VarInit(Name, RV->getType()); - - std::string TemplateArgName = CurRec->getName()+":"+Name; - if (CurRec->isTemplateArg(TemplateArgName)) { - const RecordVal *RV = CurRec->getValue(TemplateArgName); - assert(RV && "Template arg doesn't exist??"); - return new VarInit(TemplateArgName, RV->getType()); - } - } - - if (CurMultiClass) { - std::string MCName = CurMultiClass->Rec.getName()+"::"+Name; - if (CurMultiClass->Rec.isTemplateArg(MCName)) { - const RecordVal *RV = CurMultiClass->Rec.getValue(MCName); - assert(RV && "Template arg doesn't exist??"); - return new VarInit(MCName, RV->getType()); - } - } - - if (Record *D = Records.getDef(Name)) - return new DefInit(D); - - Error(NameLoc, "Variable not defined: '" + Name + "'"); - return 0; -} - -/// ParseOperation - Parse an operator. This returns null on error. -/// -/// Operation ::= XOperator ['<' Type '>'] '(' Args ')' -/// -Init *TGParser::ParseOperation(Record *CurRec) { - switch (Lex.getCode()) { - default: - TokError("unknown operation"); - return 0; - break; - case tgtok::XHead: - case tgtok::XTail: - case tgtok::XEmpty: - case tgtok::XCast: { // Value ::= !unop '(' Value ')' - UnOpInit::UnaryOp Code; - RecTy *Type = 0; - - switch (Lex.getCode()) { - default: assert(0 && "Unhandled code!"); - case tgtok::XCast: - Lex.Lex(); // eat the operation - Code = UnOpInit::CAST; - - Type = ParseOperatorType(); - - if (Type == 0) { - TokError("did not get type for unary operator"); - return 0; - } - - break; - case tgtok::XHead: - Lex.Lex(); // eat the operation - Code = UnOpInit::HEAD; - break; - case tgtok::XTail: - Lex.Lex(); // eat the operation - Code = UnOpInit::TAIL; - break; - case tgtok::XEmpty: - Lex.Lex(); // eat the operation - Code = UnOpInit::EMPTY; - Type = new IntRecTy; - break; - } - if (Lex.getCode() != tgtok::l_paren) { - TokError("expected '(' after unary operator"); - return 0; - } - Lex.Lex(); // eat the '(' - - Init *LHS = ParseValue(CurRec); - if (LHS == 0) return 0; - - if (Code == UnOpInit::HEAD - || Code == UnOpInit::TAIL - || Code == UnOpInit::EMPTY) { - ListInit *LHSl = dynamic_cast<ListInit*>(LHS); - StringInit *LHSs = dynamic_cast<StringInit*>(LHS); - TypedInit *LHSt = dynamic_cast<TypedInit*>(LHS); - if (LHSl == 0 && LHSs == 0 && LHSt == 0) { - TokError("expected list or string type argument in unary operator"); - return 0; - } - if (LHSt) { - ListRecTy *LType = dynamic_cast<ListRecTy*>(LHSt->getType()); - StringRecTy *SType = dynamic_cast<StringRecTy*>(LHSt->getType()); - if (LType == 0 && SType == 0) { - TokError("expected list or string type argumnet in unary operator"); - return 0; - } - } - - if (Code == UnOpInit::HEAD - || Code == UnOpInit::TAIL) { - if (LHSl == 0 && LHSt == 0) { - TokError("expected list type argumnet in unary operator"); - return 0; - } - - if (LHSl && LHSl->getSize() == 0) { - TokError("empty list argument in unary operator"); - return 0; - } - if (LHSl) { - Init *Item = LHSl->getElement(0); - TypedInit *Itemt = dynamic_cast<TypedInit*>(Item); - if (Itemt == 0) { - TokError("untyped list element in unary operator"); - return 0; - } - if (Code == UnOpInit::HEAD) { - Type = Itemt->getType(); - } else { - Type = new ListRecTy(Itemt->getType()); - } - } else { - assert(LHSt && "expected list type argument in unary operator"); - ListRecTy *LType = dynamic_cast<ListRecTy*>(LHSt->getType()); - if (LType == 0) { - TokError("expected list type argumnet in unary operator"); - return 0; - } - if (Code == UnOpInit::HEAD) { - Type = LType->getElementType(); - } else { - Type = LType; - } - } - } - } - - if (Lex.getCode() != tgtok::r_paren) { - TokError("expected ')' in unary operator"); - return 0; - } - Lex.Lex(); // eat the ')' - return (new UnOpInit(Code, LHS, Type))->Fold(CurRec, CurMultiClass); - } - - case tgtok::XConcat: - case tgtok::XSRA: - case tgtok::XSRL: - case tgtok::XSHL: - case tgtok::XEq: - case tgtok::XStrConcat: { // Value ::= !binop '(' Value ',' Value ')' - tgtok::TokKind OpTok = Lex.getCode(); - SMLoc OpLoc = Lex.getLoc(); - Lex.Lex(); // eat the operation - - BinOpInit::BinaryOp Code; - RecTy *Type = 0; - - switch (OpTok) { - default: assert(0 && "Unhandled code!"); - case tgtok::XConcat: Code = BinOpInit::CONCAT; Type = new DagRecTy(); break; - case tgtok::XSRA: Code = BinOpInit::SRA; Type = new IntRecTy(); break; - case tgtok::XSRL: Code = BinOpInit::SRL; Type = new IntRecTy(); break; - case tgtok::XSHL: Code = BinOpInit::SHL; Type = new IntRecTy(); break; - case tgtok::XEq: Code = BinOpInit::EQ; Type = new BitRecTy(); break; - case tgtok::XStrConcat: - Code = BinOpInit::STRCONCAT; - Type = new StringRecTy(); - break; - } - - if (Lex.getCode() != tgtok::l_paren) { - TokError("expected '(' after binary operator"); - return 0; - } - Lex.Lex(); // eat the '(' - - SmallVector<Init*, 2> InitList; - - InitList.push_back(ParseValue(CurRec)); - if (InitList.back() == 0) return 0; - - while (Lex.getCode() == tgtok::comma) { - Lex.Lex(); // eat the ',' - - InitList.push_back(ParseValue(CurRec)); - if (InitList.back() == 0) return 0; - } - - if (Lex.getCode() != tgtok::r_paren) { - TokError("expected ')' in operator"); - return 0; - } - Lex.Lex(); // eat the ')' - - // We allow multiple operands to associative operators like !strconcat as - // shorthand for nesting them. - if (Code == BinOpInit::STRCONCAT) { - while (InitList.size() > 2) { - Init *RHS = InitList.pop_back_val(); - RHS = (new BinOpInit(Code, InitList.back(), RHS, Type)) - ->Fold(CurRec, CurMultiClass); - InitList.back() = RHS; - } - } - - if (InitList.size() == 2) - return (new BinOpInit(Code, InitList[0], InitList[1], Type)) - ->Fold(CurRec, CurMultiClass); - - Error(OpLoc, "expected two operands to operator"); - return 0; - } - - case tgtok::XIf: - case tgtok::XForEach: - case tgtok::XSubst: { // Value ::= !ternop '(' Value ',' Value ',' Value ')' - TernOpInit::TernaryOp Code; - RecTy *Type = 0; - - tgtok::TokKind LexCode = Lex.getCode(); - Lex.Lex(); // eat the operation - switch (LexCode) { - default: assert(0 && "Unhandled code!"); - case tgtok::XIf: - Code = TernOpInit::IF; - break; - case tgtok::XForEach: - Code = TernOpInit::FOREACH; - break; - case tgtok::XSubst: - Code = TernOpInit::SUBST; - break; - } - if (Lex.getCode() != tgtok::l_paren) { - TokError("expected '(' after ternary operator"); - return 0; - } - Lex.Lex(); // eat the '(' - - Init *LHS = ParseValue(CurRec); - if (LHS == 0) return 0; - - if (Lex.getCode() != tgtok::comma) { - TokError("expected ',' in ternary operator"); - return 0; - } - Lex.Lex(); // eat the ',' - - Init *MHS = ParseValue(CurRec); - if (MHS == 0) return 0; - - if (Lex.getCode() != tgtok::comma) { - TokError("expected ',' in ternary operator"); - return 0; - } - Lex.Lex(); // eat the ',' - - Init *RHS = ParseValue(CurRec); - if (RHS == 0) return 0; - - if (Lex.getCode() != tgtok::r_paren) { - TokError("expected ')' in binary operator"); - return 0; - } - Lex.Lex(); // eat the ')' - - switch (LexCode) { - default: assert(0 && "Unhandled code!"); - case tgtok::XIf: { - // FIXME: The `!if' operator doesn't handle non-TypedInit well at - // all. This can be made much more robust. - TypedInit *MHSt = dynamic_cast<TypedInit*>(MHS); - TypedInit *RHSt = dynamic_cast<TypedInit*>(RHS); - - RecTy *MHSTy = 0; - RecTy *RHSTy = 0; - - if (MHSt == 0 && RHSt == 0) { - BitsInit *MHSbits = dynamic_cast<BitsInit*>(MHS); - BitsInit *RHSbits = dynamic_cast<BitsInit*>(RHS); - - if (MHSbits && RHSbits && - MHSbits->getNumBits() == RHSbits->getNumBits()) { - Type = new BitRecTy(); - break; - } else { - BitInit *MHSbit = dynamic_cast<BitInit*>(MHS); - BitInit *RHSbit = dynamic_cast<BitInit*>(RHS); - - if (MHSbit && RHSbit) { - Type = new BitRecTy(); - break; - } - } - } else if (MHSt != 0 && RHSt != 0) { - MHSTy = MHSt->getType(); - RHSTy = RHSt->getType(); - } - - if (!MHSTy || !RHSTy) { - TokError("could not get type for !if"); - return 0; - } - - if (MHSTy->typeIsConvertibleTo(RHSTy)) { - Type = RHSTy; - } else if (RHSTy->typeIsConvertibleTo(MHSTy)) { - Type = MHSTy; - } else { - TokError("inconsistent types for !if"); - return 0; - } - break; - } - case tgtok::XForEach: { - TypedInit *MHSt = dynamic_cast<TypedInit *>(MHS); - if (MHSt == 0) { - TokError("could not get type for !foreach"); - return 0; - } - Type = MHSt->getType(); - break; - } - case tgtok::XSubst: { - TypedInit *RHSt = dynamic_cast<TypedInit *>(RHS); - if (RHSt == 0) { - TokError("could not get type for !subst"); - return 0; - } - Type = RHSt->getType(); - break; - } - } - return (new TernOpInit(Code, LHS, MHS, RHS, Type))->Fold(CurRec, - CurMultiClass); - } - } - TokError("could not parse operation"); - return 0; -} - -/// ParseOperatorType - Parse a type for an operator. This returns -/// null on error. -/// -/// OperatorType ::= '<' Type '>' -/// -RecTy *TGParser::ParseOperatorType() { - RecTy *Type = 0; - - if (Lex.getCode() != tgtok::less) { - TokError("expected type name for operator"); - return 0; - } - Lex.Lex(); // eat the < - - Type = ParseType(); - - if (Type == 0) { - TokError("expected type name for operator"); - return 0; - } - - if (Lex.getCode() != tgtok::greater) { - TokError("expected type name for operator"); - return 0; - } - Lex.Lex(); // eat the > - - return Type; -} - - -/// ParseSimpleValue - Parse a tblgen value. This returns null on error. -/// -/// SimpleValue ::= IDValue -/// SimpleValue ::= INTVAL -/// SimpleValue ::= STRVAL+ -/// SimpleValue ::= CODEFRAGMENT -/// SimpleValue ::= '?' -/// SimpleValue ::= '{' ValueList '}' -/// SimpleValue ::= ID '<' ValueListNE '>' -/// SimpleValue ::= '[' ValueList ']' -/// SimpleValue ::= '(' IDValue DagArgList ')' -/// SimpleValue ::= CONCATTOK '(' Value ',' Value ')' -/// SimpleValue ::= SHLTOK '(' Value ',' Value ')' -/// SimpleValue ::= SRATOK '(' Value ',' Value ')' -/// SimpleValue ::= SRLTOK '(' Value ',' Value ')' -/// SimpleValue ::= STRCONCATTOK '(' Value ',' Value ')' -/// -Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType) { - Init *R = 0; - switch (Lex.getCode()) { - default: TokError("Unknown token when parsing a value"); break; - case tgtok::IntVal: R = new IntInit(Lex.getCurIntVal()); Lex.Lex(); break; - case tgtok::StrVal: { - std::string Val = Lex.getCurStrVal(); - Lex.Lex(); - - // Handle multiple consecutive concatenated strings. - while (Lex.getCode() == tgtok::StrVal) { - Val += Lex.getCurStrVal(); - Lex.Lex(); - } - - R = new StringInit(Val); - break; - } - case tgtok::CodeFragment: - R = new CodeInit(Lex.getCurStrVal()); - Lex.Lex(); - break; - case tgtok::question: - R = new UnsetInit(); - Lex.Lex(); - break; - case tgtok::Id: { - SMLoc NameLoc = Lex.getLoc(); - std::string Name = Lex.getCurStrVal(); - if (Lex.Lex() != tgtok::less) // consume the Id. - return ParseIDValue(CurRec, Name, NameLoc); // Value ::= IDValue - - // Value ::= ID '<' ValueListNE '>' - if (Lex.Lex() == tgtok::greater) { - TokError("expected non-empty value list"); - return 0; - } - - // This is a CLASS<initvalslist> expression. This is supposed to synthesize - // a new anonymous definition, deriving from CLASS<initvalslist> with no - // body. - Record *Class = Records.getClass(Name); - if (!Class) { - Error(NameLoc, "Expected a class name, got '" + Name + "'"); - return 0; - } - - std::vector<Init*> ValueList = ParseValueList(CurRec, Class); - if (ValueList.empty()) return 0; - - if (Lex.getCode() != tgtok::greater) { - TokError("expected '>' at end of value list"); - return 0; - } - Lex.Lex(); // eat the '>' - - // Create the new record, set it as CurRec temporarily. - static unsigned AnonCounter = 0; - Record *NewRec = new Record("anonymous.val."+utostr(AnonCounter++), - NameLoc, - Records); - SubClassReference SCRef; - SCRef.RefLoc = NameLoc; - SCRef.Rec = Class; - SCRef.TemplateArgs = ValueList; - // Add info about the subclass to NewRec. - if (AddSubClass(NewRec, SCRef)) - return 0; - NewRec->resolveReferences(); - Records.addDef(NewRec); - - // The result of the expression is a reference to the new record. - return new DefInit(NewRec); - } - case tgtok::l_brace: { // Value ::= '{' ValueList '}' - SMLoc BraceLoc = Lex.getLoc(); - Lex.Lex(); // eat the '{' - std::vector<Init*> Vals; - - if (Lex.getCode() != tgtok::r_brace) { - Vals = ParseValueList(CurRec); - if (Vals.empty()) return 0; - } - if (Lex.getCode() != tgtok::r_brace) { - TokError("expected '}' at end of bit list value"); - return 0; - } - Lex.Lex(); // eat the '}' - - BitsInit *Result = new BitsInit(Vals.size()); - for (unsigned i = 0, e = Vals.size(); i != e; ++i) { - Init *Bit = Vals[i]->convertInitializerTo(new BitRecTy()); - if (Bit == 0) { - Error(BraceLoc, "Element #" + utostr(i) + " (" + Vals[i]->getAsString()+ - ") is not convertable to a bit"); - return 0; - } - Result->setBit(Vals.size()-i-1, Bit); - } - return Result; - } - case tgtok::l_square: { // Value ::= '[' ValueList ']' - Lex.Lex(); // eat the '[' - std::vector<Init*> Vals; - - RecTy *DeducedEltTy = 0; - ListRecTy *GivenListTy = 0; - - if (ItemType != 0) { - ListRecTy *ListType = dynamic_cast<ListRecTy*>(ItemType); - if (ListType == 0) { - std::stringstream s; - s << "Type mismatch for list, expected list type, got " - << ItemType->getAsString(); - TokError(s.str()); - return 0; - } - GivenListTy = ListType; - } - - if (Lex.getCode() != tgtok::r_square) { - Vals = ParseValueList(CurRec, 0, - GivenListTy ? GivenListTy->getElementType() : 0); - if (Vals.empty()) return 0; - } - if (Lex.getCode() != tgtok::r_square) { - TokError("expected ']' at end of list value"); - return 0; - } - Lex.Lex(); // eat the ']' - - RecTy *GivenEltTy = 0; - if (Lex.getCode() == tgtok::less) { - // Optional list element type - Lex.Lex(); // eat the '<' - - GivenEltTy = ParseType(); - if (GivenEltTy == 0) { - // Couldn't parse element type - return 0; - } - - if (Lex.getCode() != tgtok::greater) { - TokError("expected '>' at end of list element type"); - return 0; - } - Lex.Lex(); // eat the '>' - } - - // Check elements - RecTy *EltTy = 0; - for (std::vector<Init *>::iterator i = Vals.begin(), ie = Vals.end(); - i != ie; - ++i) { - TypedInit *TArg = dynamic_cast<TypedInit*>(*i); - if (TArg == 0) { - TokError("Untyped list element"); - return 0; - } - if (EltTy != 0) { - EltTy = resolveTypes(EltTy, TArg->getType()); - if (EltTy == 0) { - TokError("Incompatible types in list elements"); - return 0; - } - } else { - EltTy = TArg->getType(); - } - } - - if (GivenEltTy != 0) { - if (EltTy != 0) { - // Verify consistency - if (!EltTy->typeIsConvertibleTo(GivenEltTy)) { - TokError("Incompatible types in list elements"); - return 0; - } - } - EltTy = GivenEltTy; - } - - if (EltTy == 0) { - if (ItemType == 0) { - TokError("No type for list"); - return 0; - } - DeducedEltTy = GivenListTy->getElementType(); - } else { - // Make sure the deduced type is compatible with the given type - if (GivenListTy) { - if (!EltTy->typeIsConvertibleTo(GivenListTy->getElementType())) { - TokError("Element type mismatch for list"); - return 0; - } - } - DeducedEltTy = EltTy; - } - - return new ListInit(Vals, DeducedEltTy); - } - case tgtok::l_paren: { // Value ::= '(' IDValue DagArgList ')' - Lex.Lex(); // eat the '(' - if (Lex.getCode() != tgtok::Id && Lex.getCode() != tgtok::XCast) { - TokError("expected identifier in dag init"); - return 0; - } - - Init *Operator = ParseValue(CurRec); - if (Operator == 0) return 0; - - // If the operator name is present, parse it. - std::string OperatorName; - if (Lex.getCode() == tgtok::colon) { - if (Lex.Lex() != tgtok::VarName) { // eat the ':' - TokError("expected variable name in dag operator"); - return 0; - } - OperatorName = Lex.getCurStrVal(); - Lex.Lex(); // eat the VarName. - } - - std::vector<std::pair<llvm::Init*, std::string> > DagArgs; - if (Lex.getCode() != tgtok::r_paren) { - DagArgs = ParseDagArgList(CurRec); - if (DagArgs.empty()) return 0; - } - - if (Lex.getCode() != tgtok::r_paren) { - TokError("expected ')' in dag init"); - return 0; - } - Lex.Lex(); // eat the ')' - - return new DagInit(Operator, OperatorName, DagArgs); - } - - case tgtok::XHead: - case tgtok::XTail: - case tgtok::XEmpty: - case tgtok::XCast: // Value ::= !unop '(' Value ')' - case tgtok::XConcat: - case tgtok::XSRA: - case tgtok::XSRL: - case tgtok::XSHL: - case tgtok::XEq: - case tgtok::XStrConcat: // Value ::= !binop '(' Value ',' Value ')' - case tgtok::XIf: - case tgtok::XForEach: - case tgtok::XSubst: { // Value ::= !ternop '(' Value ',' Value ',' Value ')' - return ParseOperation(CurRec); - } - } - - return R; -} - -/// ParseValue - Parse a tblgen value. This returns null on error. -/// -/// Value ::= SimpleValue ValueSuffix* -/// ValueSuffix ::= '{' BitList '}' -/// ValueSuffix ::= '[' BitList ']' -/// ValueSuffix ::= '.' ID -/// -Init *TGParser::ParseValue(Record *CurRec, RecTy *ItemType) { - Init *Result = ParseSimpleValue(CurRec, ItemType); - if (Result == 0) return 0; - - // Parse the suffixes now if present. - while (1) { - switch (Lex.getCode()) { - default: return Result; - case tgtok::l_brace: { - SMLoc CurlyLoc = Lex.getLoc(); - Lex.Lex(); // eat the '{' - std::vector<unsigned> Ranges = ParseRangeList(); - if (Ranges.empty()) return 0; - - // Reverse the bitlist. - std::reverse(Ranges.begin(), Ranges.end()); - Result = Result->convertInitializerBitRange(Ranges); - if (Result == 0) { - Error(CurlyLoc, "Invalid bit range for value"); - return 0; - } - - // Eat the '}'. - if (Lex.getCode() != tgtok::r_brace) { - TokError("expected '}' at end of bit range list"); - return 0; - } - Lex.Lex(); - break; - } - case tgtok::l_square: { - SMLoc SquareLoc = Lex.getLoc(); - Lex.Lex(); // eat the '[' - std::vector<unsigned> Ranges = ParseRangeList(); - if (Ranges.empty()) return 0; - - Result = Result->convertInitListSlice(Ranges); - if (Result == 0) { - Error(SquareLoc, "Invalid range for list slice"); - return 0; - } - - // Eat the ']'. - if (Lex.getCode() != tgtok::r_square) { - TokError("expected ']' at end of list slice"); - return 0; - } - Lex.Lex(); - break; - } - case tgtok::period: - if (Lex.Lex() != tgtok::Id) { // eat the . - TokError("expected field identifier after '.'"); - return 0; - } - if (!Result->getFieldType(Lex.getCurStrVal())) { - TokError("Cannot access field '" + Lex.getCurStrVal() + "' of value '" + - Result->getAsString() + "'"); - return 0; - } - Result = new FieldInit(Result, Lex.getCurStrVal()); - Lex.Lex(); // eat field name - break; - } - } -} - -/// ParseDagArgList - Parse the argument list for a dag literal expression. -/// -/// ParseDagArgList ::= Value (':' VARNAME)? -/// ParseDagArgList ::= ParseDagArgList ',' Value (':' VARNAME)? -std::vector<std::pair<llvm::Init*, std::string> > -TGParser::ParseDagArgList(Record *CurRec) { - std::vector<std::pair<llvm::Init*, std::string> > Result; - - while (1) { - Init *Val = ParseValue(CurRec); - if (Val == 0) return std::vector<std::pair<llvm::Init*, std::string> >(); - - // If the variable name is present, add it. - std::string VarName; - if (Lex.getCode() == tgtok::colon) { - if (Lex.Lex() != tgtok::VarName) { // eat the ':' - TokError("expected variable name in dag literal"); - return std::vector<std::pair<llvm::Init*, std::string> >(); - } - VarName = Lex.getCurStrVal(); - Lex.Lex(); // eat the VarName. - } - - Result.push_back(std::make_pair(Val, VarName)); - - if (Lex.getCode() != tgtok::comma) break; - Lex.Lex(); // eat the ',' - } - - return Result; -} - - -/// ParseValueList - Parse a comma separated list of values, returning them as a -/// vector. Note that this always expects to be able to parse at least one -/// value. It returns an empty list if this is not possible. -/// -/// ValueList ::= Value (',' Value) -/// -std::vector<Init*> TGParser::ParseValueList(Record *CurRec, Record *ArgsRec, - RecTy *EltTy) { - std::vector<Init*> Result; - RecTy *ItemType = EltTy; - unsigned int ArgN = 0; - if (ArgsRec != 0 && EltTy == 0) { - const std::vector<std::string> &TArgs = ArgsRec->getTemplateArgs(); - const RecordVal *RV = ArgsRec->getValue(TArgs[ArgN]); - assert(RV && "Template argument record not found??"); - ItemType = RV->getType(); - ++ArgN; - } - Result.push_back(ParseValue(CurRec, ItemType)); - if (Result.back() == 0) return std::vector<Init*>(); - - while (Lex.getCode() == tgtok::comma) { - Lex.Lex(); // Eat the comma - - if (ArgsRec != 0 && EltTy == 0) { - const std::vector<std::string> &TArgs = ArgsRec->getTemplateArgs(); - if (ArgN >= TArgs.size()) { - TokError("too many template arguments"); - return std::vector<Init*>(); - } - const RecordVal *RV = ArgsRec->getValue(TArgs[ArgN]); - assert(RV && "Template argument record not found??"); - ItemType = RV->getType(); - ++ArgN; - } - Result.push_back(ParseValue(CurRec, ItemType)); - if (Result.back() == 0) return std::vector<Init*>(); - } - - return Result; -} - - -/// ParseDeclaration - Read a declaration, returning the name of field ID, or an -/// empty string on error. This can happen in a number of different context's, -/// including within a def or in the template args for a def (which which case -/// CurRec will be non-null) and within the template args for a multiclass (in -/// which case CurRec will be null, but CurMultiClass will be set). This can -/// also happen within a def that is within a multiclass, which will set both -/// CurRec and CurMultiClass. -/// -/// Declaration ::= FIELD? Type ID ('=' Value)? -/// -std::string TGParser::ParseDeclaration(Record *CurRec, - bool ParsingTemplateArgs) { - // Read the field prefix if present. - bool HasField = Lex.getCode() == tgtok::Field; - if (HasField) Lex.Lex(); - - RecTy *Type = ParseType(); - if (Type == 0) return ""; - - if (Lex.getCode() != tgtok::Id) { - TokError("Expected identifier in declaration"); - return ""; - } - - SMLoc IdLoc = Lex.getLoc(); - std::string DeclName = Lex.getCurStrVal(); - Lex.Lex(); - - if (ParsingTemplateArgs) { - if (CurRec) { - DeclName = CurRec->getName() + ":" + DeclName; - } else { - assert(CurMultiClass); - } - if (CurMultiClass) - DeclName = CurMultiClass->Rec.getName() + "::" + DeclName; - } - - // Add the value. - if (AddValue(CurRec, IdLoc, RecordVal(DeclName, Type, HasField))) - return ""; - - // If a value is present, parse it. - if (Lex.getCode() == tgtok::equal) { - Lex.Lex(); - SMLoc ValLoc = Lex.getLoc(); - Init *Val = ParseValue(CurRec, Type); - if (Val == 0 || - SetValue(CurRec, ValLoc, DeclName, std::vector<unsigned>(), Val)) - return ""; - } - - return DeclName; -} - -/// ParseTemplateArgList - Read a template argument list, which is a non-empty -/// sequence of template-declarations in <>'s. If CurRec is non-null, these are -/// template args for a def, which may or may not be in a multiclass. If null, -/// these are the template args for a multiclass. -/// -/// TemplateArgList ::= '<' Declaration (',' Declaration)* '>' -/// -bool TGParser::ParseTemplateArgList(Record *CurRec) { - assert(Lex.getCode() == tgtok::less && "Not a template arg list!"); - Lex.Lex(); // eat the '<' - - Record *TheRecToAddTo = CurRec ? CurRec : &CurMultiClass->Rec; - - // Read the first declaration. - std::string TemplArg = ParseDeclaration(CurRec, true/*templateargs*/); - if (TemplArg.empty()) - return true; - - TheRecToAddTo->addTemplateArg(TemplArg); - - while (Lex.getCode() == tgtok::comma) { - Lex.Lex(); // eat the ',' - - // Read the following declarations. - TemplArg = ParseDeclaration(CurRec, true/*templateargs*/); - if (TemplArg.empty()) - return true; - TheRecToAddTo->addTemplateArg(TemplArg); - } - - if (Lex.getCode() != tgtok::greater) - return TokError("expected '>' at end of template argument list"); - Lex.Lex(); // eat the '>'. - return false; -} - - -/// ParseBodyItem - Parse a single item at within the body of a def or class. -/// -/// BodyItem ::= Declaration ';' -/// BodyItem ::= LET ID OptionalBitList '=' Value ';' -bool TGParser::ParseBodyItem(Record *CurRec) { - if (Lex.getCode() != tgtok::Let) { - if (ParseDeclaration(CurRec, false).empty()) - return true; - - if (Lex.getCode() != tgtok::semi) - return TokError("expected ';' after declaration"); - Lex.Lex(); - return false; - } - - // LET ID OptionalRangeList '=' Value ';' - if (Lex.Lex() != tgtok::Id) - return TokError("expected field identifier after let"); - - SMLoc IdLoc = Lex.getLoc(); - std::string FieldName = Lex.getCurStrVal(); - Lex.Lex(); // eat the field name. - - std::vector<unsigned> BitList; - if (ParseOptionalBitList(BitList)) - return true; - std::reverse(BitList.begin(), BitList.end()); - - if (Lex.getCode() != tgtok::equal) - return TokError("expected '=' in let expression"); - Lex.Lex(); // eat the '='. - - RecordVal *Field = CurRec->getValue(FieldName); - if (Field == 0) - return TokError("Value '" + FieldName + "' unknown!"); - - RecTy *Type = Field->getType(); - - Init *Val = ParseValue(CurRec, Type); - if (Val == 0) return true; - - if (Lex.getCode() != tgtok::semi) - return TokError("expected ';' after let expression"); - Lex.Lex(); - - return SetValue(CurRec, IdLoc, FieldName, BitList, Val); -} - -/// ParseBody - Read the body of a class or def. Return true on error, false on -/// success. -/// -/// Body ::= ';' -/// Body ::= '{' BodyList '}' -/// BodyList BodyItem* -/// -bool TGParser::ParseBody(Record *CurRec) { - // If this is a null definition, just eat the semi and return. - if (Lex.getCode() == tgtok::semi) { - Lex.Lex(); - return false; - } - - if (Lex.getCode() != tgtok::l_brace) - return TokError("Expected ';' or '{' to start body"); - // Eat the '{'. - Lex.Lex(); - - while (Lex.getCode() != tgtok::r_brace) - if (ParseBodyItem(CurRec)) - return true; - - // Eat the '}'. - Lex.Lex(); - return false; -} - -/// ParseObjectBody - Parse the body of a def or class. This consists of an -/// optional ClassList followed by a Body. CurRec is the current def or class -/// that is being parsed. -/// -/// ObjectBody ::= BaseClassList Body -/// BaseClassList ::= /*empty*/ -/// BaseClassList ::= ':' BaseClassListNE -/// BaseClassListNE ::= SubClassRef (',' SubClassRef)* -/// -bool TGParser::ParseObjectBody(Record *CurRec) { - // If there is a baseclass list, read it. - if (Lex.getCode() == tgtok::colon) { - Lex.Lex(); - - // Read all of the subclasses. - SubClassReference SubClass = ParseSubClassReference(CurRec, false); - while (1) { - // Check for error. - if (SubClass.Rec == 0) return true; - - // Add it. - if (AddSubClass(CurRec, SubClass)) - return true; - - if (Lex.getCode() != tgtok::comma) break; - Lex.Lex(); // eat ','. - SubClass = ParseSubClassReference(CurRec, false); - } - } - - // Process any variables on the let stack. - for (unsigned i = 0, e = LetStack.size(); i != e; ++i) - for (unsigned j = 0, e = LetStack[i].size(); j != e; ++j) - if (SetValue(CurRec, LetStack[i][j].Loc, LetStack[i][j].Name, - LetStack[i][j].Bits, LetStack[i][j].Value)) - return true; - - return ParseBody(CurRec); -} - -/// ParseDef - Parse and return a top level or multiclass def, return the record -/// corresponding to it. This returns null on error. -/// -/// DefInst ::= DEF ObjectName ObjectBody -/// -bool TGParser::ParseDef(MultiClass *CurMultiClass) { - SMLoc DefLoc = Lex.getLoc(); - assert(Lex.getCode() == tgtok::Def && "Unknown tok"); - Lex.Lex(); // Eat the 'def' token. - - // Parse ObjectName and make a record for it. - Record *CurRec = new Record(ParseObjectName(), DefLoc, Records); - - if (!CurMultiClass) { - // Top-level def definition. - - // Ensure redefinition doesn't happen. - if (Records.getDef(CurRec->getName())) { - Error(DefLoc, "def '" + CurRec->getName() + "' already defined"); - return true; - } - Records.addDef(CurRec); - } else { - // Otherwise, a def inside a multiclass, add it to the multiclass. - for (unsigned i = 0, e = CurMultiClass->DefPrototypes.size(); i != e; ++i) - if (CurMultiClass->DefPrototypes[i]->getName() == CurRec->getName()) { - Error(DefLoc, "def '" + CurRec->getName() + - "' already defined in this multiclass!"); - return true; - } - CurMultiClass->DefPrototypes.push_back(CurRec); - } - - if (ParseObjectBody(CurRec)) - return true; - - if (CurMultiClass == 0) // Def's in multiclasses aren't really defs. - CurRec->resolveReferences(); - - // If ObjectBody has template arguments, it's an error. - assert(CurRec->getTemplateArgs().empty() && "How'd this get template args?"); - - if (CurMultiClass) { - // Copy the template arguments for the multiclass into the def. - const std::vector<std::string> &TArgs = - CurMultiClass->Rec.getTemplateArgs(); - - for (unsigned i = 0, e = TArgs.size(); i != e; ++i) { - const RecordVal *RV = CurMultiClass->Rec.getValue(TArgs[i]); - assert(RV && "Template arg doesn't exist?"); - CurRec->addValue(*RV); - } - } - - return false; -} - - -/// ParseClass - Parse a tblgen class definition. -/// -/// ClassInst ::= CLASS ID TemplateArgList? ObjectBody -/// -bool TGParser::ParseClass() { - assert(Lex.getCode() == tgtok::Class && "Unexpected token!"); - Lex.Lex(); - - if (Lex.getCode() != tgtok::Id) - return TokError("expected class name after 'class' keyword"); - - Record *CurRec = Records.getClass(Lex.getCurStrVal()); - if (CurRec) { - // If the body was previously defined, this is an error. - if (!CurRec->getValues().empty() || - !CurRec->getSuperClasses().empty() || - !CurRec->getTemplateArgs().empty()) - return TokError("Class '" + CurRec->getName() + "' already defined"); - } else { - // If this is the first reference to this class, create and add it. - CurRec = new Record(Lex.getCurStrVal(), Lex.getLoc(), Records); - Records.addClass(CurRec); - } - Lex.Lex(); // eat the name. - - // If there are template args, parse them. - if (Lex.getCode() == tgtok::less) - if (ParseTemplateArgList(CurRec)) - return true; - - // Finally, parse the object body. - return ParseObjectBody(CurRec); -} - -/// ParseLetList - Parse a non-empty list of assignment expressions into a list -/// of LetRecords. -/// -/// LetList ::= LetItem (',' LetItem)* -/// LetItem ::= ID OptionalRangeList '=' Value -/// -std::vector<LetRecord> TGParser::ParseLetList() { - std::vector<LetRecord> Result; - - while (1) { - if (Lex.getCode() != tgtok::Id) { - TokError("expected identifier in let definition"); - return std::vector<LetRecord>(); - } - std::string Name = Lex.getCurStrVal(); - SMLoc NameLoc = Lex.getLoc(); - Lex.Lex(); // Eat the identifier. - - // Check for an optional RangeList. - std::vector<unsigned> Bits; - if (ParseOptionalRangeList(Bits)) - return std::vector<LetRecord>(); - std::reverse(Bits.begin(), Bits.end()); - - if (Lex.getCode() != tgtok::equal) { - TokError("expected '=' in let expression"); - return std::vector<LetRecord>(); - } - Lex.Lex(); // eat the '='. - - Init *Val = ParseValue(0); - if (Val == 0) return std::vector<LetRecord>(); - - // Now that we have everything, add the record. - Result.push_back(LetRecord(Name, Bits, Val, NameLoc)); - - if (Lex.getCode() != tgtok::comma) - return Result; - Lex.Lex(); // eat the comma. - } -} - -/// ParseTopLevelLet - Parse a 'let' at top level. This can be a couple of -/// different related productions. This works inside multiclasses too. -/// -/// Object ::= LET LetList IN '{' ObjectList '}' -/// Object ::= LET LetList IN Object -/// -bool TGParser::ParseTopLevelLet(MultiClass *CurMultiClass) { - assert(Lex.getCode() == tgtok::Let && "Unexpected token"); - Lex.Lex(); - - // Add this entry to the let stack. - std::vector<LetRecord> LetInfo = ParseLetList(); - if (LetInfo.empty()) return true; - LetStack.push_back(LetInfo); - - if (Lex.getCode() != tgtok::In) - return TokError("expected 'in' at end of top-level 'let'"); - Lex.Lex(); - - // If this is a scalar let, just handle it now - if (Lex.getCode() != tgtok::l_brace) { - // LET LetList IN Object - if (ParseObject(CurMultiClass)) - return true; - } else { // Object ::= LETCommand '{' ObjectList '}' - SMLoc BraceLoc = Lex.getLoc(); - // Otherwise, this is a group let. - Lex.Lex(); // eat the '{'. - - // Parse the object list. - if (ParseObjectList(CurMultiClass)) - return true; - - if (Lex.getCode() != tgtok::r_brace) { - TokError("expected '}' at end of top level let command"); - return Error(BraceLoc, "to match this '{'"); - } - Lex.Lex(); - } - - // Outside this let scope, this let block is not active. - LetStack.pop_back(); - return false; -} - -/// ParseMultiClass - Parse a multiclass definition. -/// -/// MultiClassInst ::= MULTICLASS ID TemplateArgList? -/// ':' BaseMultiClassList '{' MultiClassDef+ '}' -/// -bool TGParser::ParseMultiClass() { - assert(Lex.getCode() == tgtok::MultiClass && "Unexpected token"); - Lex.Lex(); // Eat the multiclass token. - - if (Lex.getCode() != tgtok::Id) - return TokError("expected identifier after multiclass for name"); - std::string Name = Lex.getCurStrVal(); - - if (MultiClasses.count(Name)) - return TokError("multiclass '" + Name + "' already defined"); - - CurMultiClass = MultiClasses[Name] = new MultiClass(Name, - Lex.getLoc(), Records); - Lex.Lex(); // Eat the identifier. - - // If there are template args, parse them. - if (Lex.getCode() == tgtok::less) - if (ParseTemplateArgList(0)) - return true; - - bool inherits = false; - - // If there are submulticlasses, parse them. - if (Lex.getCode() == tgtok::colon) { - inherits = true; - - Lex.Lex(); - - // Read all of the submulticlasses. - SubMultiClassReference SubMultiClass = - ParseSubMultiClassReference(CurMultiClass); - while (1) { - // Check for error. - if (SubMultiClass.MC == 0) return true; - - // Add it. - if (AddSubMultiClass(CurMultiClass, SubMultiClass)) - return true; - - if (Lex.getCode() != tgtok::comma) break; - Lex.Lex(); // eat ','. - SubMultiClass = ParseSubMultiClassReference(CurMultiClass); - } - } - - if (Lex.getCode() != tgtok::l_brace) { - if (!inherits) - return TokError("expected '{' in multiclass definition"); - else if (Lex.getCode() != tgtok::semi) - return TokError("expected ';' in multiclass definition"); - else - Lex.Lex(); // eat the ';'. - } else { - if (Lex.Lex() == tgtok::r_brace) // eat the '{'. - return TokError("multiclass must contain at least one def"); - - while (Lex.getCode() != tgtok::r_brace) { - switch (Lex.getCode()) { - default: - return TokError("expected 'let', 'def' or 'defm' in multiclass body"); - case tgtok::Let: - case tgtok::Def: - case tgtok::Defm: - if (ParseObject(CurMultiClass)) - return true; - break; - } - } - Lex.Lex(); // eat the '}'. - } - - CurMultiClass = 0; - return false; -} - -/// ParseDefm - Parse the instantiation of a multiclass. -/// -/// DefMInst ::= DEFM ID ':' DefmSubClassRef ';' -/// -bool TGParser::ParseDefm(MultiClass *CurMultiClass) { - assert(Lex.getCode() == tgtok::Defm && "Unexpected token!"); - - std::string DefmPrefix; - if (Lex.Lex() == tgtok::Id) { // eat the defm. - DefmPrefix = Lex.getCurStrVal(); - Lex.Lex(); // Eat the defm prefix. - } - - SMLoc DefmPrefixLoc = Lex.getLoc(); - if (Lex.getCode() != tgtok::colon) - return TokError("expected ':' after defm identifier"); - - // Keep track of the new generated record definitions. - std::vector<Record*> NewRecDefs; - - // This record also inherits from a regular class (non-multiclass)? - bool InheritFromClass = false; - - // eat the colon. - Lex.Lex(); - - SMLoc SubClassLoc = Lex.getLoc(); - SubClassReference Ref = ParseSubClassReference(0, true); - - while (1) { - if (Ref.Rec == 0) return true; - - // To instantiate a multiclass, we need to first get the multiclass, then - // instantiate each def contained in the multiclass with the SubClassRef - // template parameters. - MultiClass *MC = MultiClasses[Ref.Rec->getName()]; - assert(MC && "Didn't lookup multiclass correctly?"); - std::vector<Init*> &TemplateVals = Ref.TemplateArgs; - - // Verify that the correct number of template arguments were specified. - const std::vector<std::string> &TArgs = MC->Rec.getTemplateArgs(); - if (TArgs.size() < TemplateVals.size()) - return Error(SubClassLoc, - "more template args specified than multiclass expects"); - - // Loop over all the def's in the multiclass, instantiating each one. - for (unsigned i = 0, e = MC->DefPrototypes.size(); i != e; ++i) { - Record *DefProto = MC->DefPrototypes[i]; - - // Add in the defm name. If the defm prefix is empty, give each - // instantiated def a unique name. Otherwise, if "#NAME#" exists in the - // name, substitute the prefix for #NAME#. Otherwise, use the defm name - // as a prefix. - std::string DefName = DefProto->getName(); - if (DefmPrefix.empty()) { - DefName = GetNewAnonymousName(); - } else { - std::string::size_type idx = DefName.find("#NAME#"); - if (idx != std::string::npos) { - DefName.replace(idx, 6, DefmPrefix); - } else { - // Add the suffix to the defm name to get the new name. - DefName = DefmPrefix + DefName; - } - } - - Record *CurRec = new Record(DefName, DefmPrefixLoc, Records); - - SubClassReference Ref; - Ref.RefLoc = DefmPrefixLoc; - Ref.Rec = DefProto; - AddSubClass(CurRec, Ref); - - // Loop over all of the template arguments, setting them to the specified - // value or leaving them as the default if necessary. - for (unsigned i = 0, e = TArgs.size(); i != e; ++i) { - // Check if a value is specified for this temp-arg. - if (i < TemplateVals.size()) { - // Set it now. - if (SetValue(CurRec, DefmPrefixLoc, TArgs[i], std::vector<unsigned>(), - TemplateVals[i])) - return true; - - // Resolve it next. - CurRec->resolveReferencesTo(CurRec->getValue(TArgs[i])); - - // Now remove it. - CurRec->removeValue(TArgs[i]); - - } else if (!CurRec->getValue(TArgs[i])->getValue()->isComplete()) { - return Error(SubClassLoc, - "value not specified for template argument #"+ - utostr(i) + " (" + TArgs[i] + ") of multiclassclass '" + - MC->Rec.getName() + "'"); - } - } - - // If the mdef is inside a 'let' expression, add to each def. - for (unsigned i = 0, e = LetStack.size(); i != e; ++i) - for (unsigned j = 0, e = LetStack[i].size(); j != e; ++j) - if (SetValue(CurRec, LetStack[i][j].Loc, LetStack[i][j].Name, - LetStack[i][j].Bits, LetStack[i][j].Value)) { - Error(DefmPrefixLoc, "when instantiating this defm"); - return true; - } - - // Ensure redefinition doesn't happen. - if (Records.getDef(CurRec->getName())) - return Error(DefmPrefixLoc, "def '" + CurRec->getName() + - "' already defined, instantiating defm with subdef '" + - DefProto->getName() + "'"); - - // Don't create a top level definition for defm inside multiclasses, - // instead, only update the prototypes and bind the template args - // with the new created definition. - if (CurMultiClass) { - for (unsigned i = 0, e = CurMultiClass->DefPrototypes.size(); - i != e; ++i) { - if (CurMultiClass->DefPrototypes[i]->getName() == CurRec->getName()) { - Error(DefmPrefixLoc, "defm '" + CurRec->getName() + - "' already defined in this multiclass!"); - return 0; - } - } - CurMultiClass->DefPrototypes.push_back(CurRec); - - // Copy the template arguments for the multiclass into the new def. - const std::vector<std::string> &TA = - CurMultiClass->Rec.getTemplateArgs(); - - for (unsigned i = 0, e = TA.size(); i != e; ++i) { - const RecordVal *RV = CurMultiClass->Rec.getValue(TA[i]); - assert(RV && "Template arg doesn't exist?"); - CurRec->addValue(*RV); - } - } else { - Records.addDef(CurRec); - } - - NewRecDefs.push_back(CurRec); - } - - if (Lex.getCode() != tgtok::comma) break; - Lex.Lex(); // eat ','. - - SubClassLoc = Lex.getLoc(); - - // A defm can inherit from regular classes (non-multiclass) as - // long as they come in the end of the inheritance list. - InheritFromClass = (Records.getClass(Lex.getCurStrVal()) != 0); - - if (InheritFromClass) - break; - - Ref = ParseSubClassReference(0, true); - } - - if (InheritFromClass) { - // Process all the classes to inherit as if they were part of a - // regular 'def' and inherit all record values. - SubClassReference SubClass = ParseSubClassReference(0, false); - while (1) { - // Check for error. - if (SubClass.Rec == 0) return true; - - // Get the expanded definition prototypes and teach them about - // the record values the current class to inherit has - for (unsigned i = 0, e = NewRecDefs.size(); i != e; ++i) { - Record *CurRec = NewRecDefs[i]; - - // Add it. - if (AddSubClass(CurRec, SubClass)) - return true; - - // Process any variables on the let stack. - for (unsigned i = 0, e = LetStack.size(); i != e; ++i) - for (unsigned j = 0, e = LetStack[i].size(); j != e; ++j) - if (SetValue(CurRec, LetStack[i][j].Loc, LetStack[i][j].Name, - LetStack[i][j].Bits, LetStack[i][j].Value)) - return true; - } - - if (Lex.getCode() != tgtok::comma) break; - Lex.Lex(); // eat ','. - SubClass = ParseSubClassReference(0, false); - } - } - - if (!CurMultiClass) - for (unsigned i = 0, e = NewRecDefs.size(); i != e; ++i) - NewRecDefs[i]->resolveReferences(); - - if (Lex.getCode() != tgtok::semi) - return TokError("expected ';' at end of defm"); - Lex.Lex(); - - return false; -} - -/// ParseObject -/// Object ::= ClassInst -/// Object ::= DefInst -/// Object ::= MultiClassInst -/// Object ::= DefMInst -/// Object ::= LETCommand '{' ObjectList '}' -/// Object ::= LETCommand Object -bool TGParser::ParseObject(MultiClass *MC) { - switch (Lex.getCode()) { - default: - return TokError("Expected class, def, defm, multiclass or let definition"); - case tgtok::Let: return ParseTopLevelLet(MC); - case tgtok::Def: return ParseDef(MC); - case tgtok::Defm: return ParseDefm(MC); - case tgtok::Class: return ParseClass(); - case tgtok::MultiClass: return ParseMultiClass(); - } -} - -/// ParseObjectList -/// ObjectList :== Object* -bool TGParser::ParseObjectList(MultiClass *MC) { - while (isObjectStart(Lex.getCode())) { - if (ParseObject(MC)) - return true; - } - return false; -} - -bool TGParser::ParseFile() { - Lex.Lex(); // Prime the lexer. - if (ParseObjectList()) return true; - - // If we have unread input at the end of the file, report it. - if (Lex.getCode() == tgtok::Eof) - return false; - - return TokError("Unexpected input at top level"); -} - diff --git a/utils/TableGen/TGParser.h b/utils/TableGen/TGParser.h deleted file mode 100644 index dce7e1d..0000000 --- a/utils/TableGen/TGParser.h +++ /dev/null @@ -1,122 +0,0 @@ -//===- TGParser.h - Parser for TableGen Files -------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This class represents the Parser for tablegen files. -// -//===----------------------------------------------------------------------===// - -#ifndef TGPARSER_H -#define TGPARSER_H - -#include "TGLexer.h" -#include "Error.h" -#include "llvm/ADT/Twine.h" -#include "llvm/Support/SourceMgr.h" -#include <map> - -namespace llvm { - class Record; - class RecordVal; - class RecordKeeper; - struct RecTy; - class Init; - struct MultiClass; - struct SubClassReference; - struct SubMultiClassReference; - - struct LetRecord { - std::string Name; - std::vector<unsigned> Bits; - Init *Value; - SMLoc Loc; - LetRecord(const std::string &N, const std::vector<unsigned> &B, Init *V, - SMLoc L) - : Name(N), Bits(B), Value(V), Loc(L) { - } - }; - -class TGParser { - TGLexer Lex; - std::vector<std::vector<LetRecord> > LetStack; - std::map<std::string, MultiClass*> MultiClasses; - - /// CurMultiClass - If we are parsing a 'multiclass' definition, this is the - /// current value. - MultiClass *CurMultiClass; - - // Record tracker - RecordKeeper &Records; -public: - TGParser(SourceMgr &SrcMgr, RecordKeeper &records) : - Lex(SrcMgr), CurMultiClass(0), Records(records) {} - - /// ParseFile - Main entrypoint for parsing a tblgen file. These parser - /// routines return true on error, or false on success. - bool ParseFile(); - - bool Error(SMLoc L, const Twine &Msg) const { - PrintError(L, Msg); - return true; - } - bool TokError(const Twine &Msg) const { - return Error(Lex.getLoc(), Msg); - } - const std::vector<std::string> &getDependencies() const { - return Lex.getDependencies(); - } -private: // Semantic analysis methods. - bool AddValue(Record *TheRec, SMLoc Loc, const RecordVal &RV); - bool SetValue(Record *TheRec, SMLoc Loc, const std::string &ValName, - const std::vector<unsigned> &BitList, Init *V); - bool AddSubClass(Record *Rec, SubClassReference &SubClass); - bool AddSubMultiClass(MultiClass *CurMC, - SubMultiClassReference &SubMultiClass); - -private: // Parser methods. - bool ParseObjectList(MultiClass *MC = 0); - bool ParseObject(MultiClass *MC); - bool ParseClass(); - bool ParseMultiClass(); - bool ParseDefm(MultiClass *CurMultiClass); - bool ParseDef(MultiClass *CurMultiClass); - bool ParseTopLevelLet(MultiClass *CurMultiClass); - std::vector<LetRecord> ParseLetList(); - - bool ParseObjectBody(Record *CurRec); - bool ParseBody(Record *CurRec); - bool ParseBodyItem(Record *CurRec); - - bool ParseTemplateArgList(Record *CurRec); - std::string ParseDeclaration(Record *CurRec, bool ParsingTemplateArgs); - - SubClassReference ParseSubClassReference(Record *CurRec, bool isDefm); - SubMultiClassReference ParseSubMultiClassReference(MultiClass *CurMC); - - Init *ParseIDValue(Record *CurRec); - Init *ParseIDValue(Record *CurRec, const std::string &Name, SMLoc NameLoc); - Init *ParseSimpleValue(Record *CurRec, RecTy *ItemType = 0); - Init *ParseValue(Record *CurRec, RecTy *ItemType = 0); - std::vector<Init*> ParseValueList(Record *CurRec, Record *ArgsRec = 0, RecTy *EltTy = 0); - std::vector<std::pair<llvm::Init*, std::string> > ParseDagArgList(Record *); - bool ParseOptionalRangeList(std::vector<unsigned> &Ranges); - bool ParseOptionalBitList(std::vector<unsigned> &Ranges); - std::vector<unsigned> ParseRangeList(); - bool ParseRangePiece(std::vector<unsigned> &Ranges); - RecTy *ParseType(); - Init *ParseOperation(Record *CurRec); - RecTy *ParseOperatorType(); - std::string ParseObjectName(); - Record *ParseClassID(); - MultiClass *ParseMultiClassID(); - Record *ParseDefmID(); -}; - -} // end namespace llvm - -#endif diff --git a/utils/TableGen/TableGen.cpp b/utils/TableGen/TableGen.cpp index e8eacb8..eacfdf6 100644 --- a/utils/TableGen/TableGen.cpp +++ b/utils/TableGen/TableGen.cpp @@ -1,4 +1,4 @@ -//===- TableGen.cpp - Top-Level TableGen implementation -------------------===// +//===- TableGen.cpp - Top-Level TableGen implementation for LLVM ----------===// // // The LLVM Compiler Infrastructure // @@ -7,48 +7,34 @@ // //===----------------------------------------------------------------------===// // -// TableGen is a tool which can be used to build up a description of something, -// then invoke one or more "tablegen backends" to emit information about the -// description in some predefined format. In practice, this is used by the LLVM -// code generators to automate generation of a code generator through a -// high-level description of the target. +// This file contains the main function for LLVM's TableGen. // //===----------------------------------------------------------------------===// #include "AsmMatcherEmitter.h" #include "AsmWriterEmitter.h" #include "CallingConvEmitter.h" -#include "ClangASTNodesEmitter.h" -#include "ClangAttrEmitter.h" -#include "ClangDiagnosticsEmitter.h" -#include "ClangSACheckersEmitter.h" #include "CodeEmitterGen.h" #include "DAGISelEmitter.h" #include "DisassemblerEmitter.h" #include "EDEmitter.h" -#include "Error.h" #include "FastISelEmitter.h" #include "InstrInfoEmitter.h" #include "IntrinsicEmitter.h" -#include "LLVMCConfigurationEmitter.h" -#include "NeonEmitter.h" -#include "OptParserEmitter.h" #include "PseudoLoweringEmitter.h" -#include "Record.h" #include "RegisterInfoEmitter.h" #include "ARMDecoderEmitter.h" #include "SubtargetEmitter.h" #include "SetTheory.h" -#include "TGParser.h" -#include "llvm/ADT/OwningPtr.h" + #include "llvm/Support/CommandLine.h" -#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/PrettyStackTrace.h" -#include "llvm/Support/ToolOutputFile.h" #include "llvm/Support/Signals.h" -#include "llvm/Support/system_error.h" -#include <algorithm> -#include <cstdio> +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Main.h" +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/TableGenAction.h" + using namespace llvm; enum ActionType { @@ -62,29 +48,12 @@ enum ActionType { GenDisassembler, GenPseudoLowering, GenCallingConv, - GenClangAttrClasses, - GenClangAttrImpl, - GenClangAttrList, - GenClangAttrPCHRead, - GenClangAttrPCHWrite, - GenClangAttrSpellingList, - GenClangDiagsDefs, - GenClangDiagGroups, - GenClangDiagsIndexName, - GenClangDeclNodes, - GenClangStmtNodes, - GenClangSACheckers, GenDAGISel, GenFastISel, - GenOptParserDefs, GenOptParserImpl, GenSubtarget, GenIntrinsic, GenTgtIntrinsic, - GenLLVMCConf, GenEDInfo, - GenArmNeon, - GenArmNeonSema, - GenArmNeonTest, PrintEnums, PrintSets }; @@ -116,52 +85,14 @@ namespace { "Generate a DAG instruction selector"), clEnumValN(GenFastISel, "gen-fast-isel", "Generate a \"fast\" instruction selector"), - clEnumValN(GenOptParserDefs, "gen-opt-parser-defs", - "Generate option definitions"), - clEnumValN(GenOptParserImpl, "gen-opt-parser-impl", - "Generate option parser implementation"), clEnumValN(GenSubtarget, "gen-subtarget", "Generate subtarget enumerations"), clEnumValN(GenIntrinsic, "gen-intrinsic", "Generate intrinsic information"), clEnumValN(GenTgtIntrinsic, "gen-tgt-intrinsic", "Generate target intrinsic information"), - clEnumValN(GenClangAttrClasses, "gen-clang-attr-classes", - "Generate clang attribute clases"), - clEnumValN(GenClangAttrImpl, "gen-clang-attr-impl", - "Generate clang attribute implementations"), - clEnumValN(GenClangAttrList, "gen-clang-attr-list", - "Generate a clang attribute list"), - clEnumValN(GenClangAttrPCHRead, "gen-clang-attr-pch-read", - "Generate clang PCH attribute reader"), - clEnumValN(GenClangAttrPCHWrite, "gen-clang-attr-pch-write", - "Generate clang PCH attribute writer"), - clEnumValN(GenClangAttrSpellingList, - "gen-clang-attr-spelling-list", - "Generate a clang attribute spelling list"), - clEnumValN(GenClangDiagsDefs, "gen-clang-diags-defs", - "Generate Clang diagnostics definitions"), - clEnumValN(GenClangDiagGroups, "gen-clang-diag-groups", - "Generate Clang diagnostic groups"), - clEnumValN(GenClangDiagsIndexName, - "gen-clang-diags-index-name", - "Generate Clang diagnostic name index"), - clEnumValN(GenClangDeclNodes, "gen-clang-decl-nodes", - "Generate Clang AST declaration nodes"), - clEnumValN(GenClangStmtNodes, "gen-clang-stmt-nodes", - "Generate Clang AST statement nodes"), - clEnumValN(GenClangSACheckers, "gen-clang-sa-checkers", - "Generate Clang Static Analyzer checkers"), - clEnumValN(GenLLVMCConf, "gen-llvmc", - "Generate LLVMC configuration library"), clEnumValN(GenEDInfo, "gen-enhanced-disassembly-info", "Generate enhanced disassembly info"), - clEnumValN(GenArmNeon, "gen-arm-neon", - "Generate arm_neon.h for clang"), - clEnumValN(GenArmNeonSema, "gen-arm-neon-sema", - "Generate ARM NEON sema support for clang"), - clEnumValN(GenArmNeonTest, "gen-arm-neon-test", - "Generate ARM NEON tests for clang"), clEnumValN(PrintEnums, "print-enums", "Print enum values for a class"), clEnumValN(PrintSets, "print-sets", @@ -171,198 +102,66 @@ namespace { cl::opt<std::string> Class("class", cl::desc("Print Enum list for this class"), cl::value_desc("class name")); - - cl::opt<std::string> - OutputFilename("o", cl::desc("Output filename"), cl::value_desc("filename"), - cl::init("-")); - - cl::opt<std::string> - DependFilename("d", cl::desc("Dependency filename"), cl::value_desc("filename"), - cl::init("")); - - cl::opt<std::string> - InputFilename(cl::Positional, cl::desc("<input file>"), cl::init("-")); - - cl::list<std::string> - IncludeDirs("I", cl::desc("Directory of include files"), - cl::value_desc("directory"), cl::Prefix); - - cl::opt<std::string> - ClangComponent("clang-component", - cl::desc("Only use warnings from specified component"), - cl::value_desc("component"), cl::Hidden); } - -int main(int argc, char **argv) { - RecordKeeper Records; - - sys::PrintStackTraceOnErrorSignal(); - PrettyStackTraceProgram X(argc, argv); - cl::ParseCommandLineOptions(argc, argv); - - - try { - // Parse the input file. - OwningPtr<MemoryBuffer> File; - if (error_code ec = MemoryBuffer::getFileOrSTDIN(InputFilename.c_str(), File)) { - errs() << "Could not open input file '" << InputFilename << "': " - << ec.message() <<"\n"; - return 1; - } - MemoryBuffer *F = File.take(); - - // Tell SrcMgr about this buffer, which is what TGParser will pick up. - SrcMgr.AddNewSourceBuffer(F, SMLoc()); - - // Record the location of the include directory so that the lexer can find - // it later. - SrcMgr.setIncludeDirs(IncludeDirs); - - TGParser Parser(SrcMgr, Records); - - if (Parser.ParseFile()) - return 1; - - std::string Error; - tool_output_file Out(OutputFilename.c_str(), Error); - if (!Error.empty()) { - errs() << argv[0] << ": error opening " << OutputFilename - << ":" << Error << "\n"; - return 1; - } - if (!DependFilename.empty()) { - if (OutputFilename == "-") { - errs() << argv[0] << ": the option -d must be used together with -o\n"; - return 1; - } - tool_output_file DepOut(DependFilename.c_str(), Error); - if (!Error.empty()) { - errs() << argv[0] << ": error opening " << DependFilename - << ":" << Error << "\n"; - return 1; - } - DepOut.os() << DependFilename << ":"; - const std::vector<std::string> &Dependencies = Parser.getDependencies(); - for (std::vector<std::string>::const_iterator I = Dependencies.begin(), - E = Dependencies.end(); - I != E; ++I) { - DepOut.os() << " " << (*I); - } - DepOut.os() << "\n"; - DepOut.keep(); - } - +class LLVMTableGenAction : public TableGenAction { +public: + bool operator()(raw_ostream &OS, RecordKeeper &Records) { switch (Action) { case PrintRecords: - Out.os() << Records; // No argument, dump all contents + OS << Records; // No argument, dump all contents break; case GenEmitter: - CodeEmitterGen(Records).run(Out.os()); + CodeEmitterGen(Records).run(OS); break; case GenRegisterInfo: - RegisterInfoEmitter(Records).run(Out.os()); + RegisterInfoEmitter(Records).run(OS); break; case GenInstrInfo: - InstrInfoEmitter(Records).run(Out.os()); + InstrInfoEmitter(Records).run(OS); break; case GenCallingConv: - CallingConvEmitter(Records).run(Out.os()); + CallingConvEmitter(Records).run(OS); break; case GenAsmWriter: - AsmWriterEmitter(Records).run(Out.os()); + AsmWriterEmitter(Records).run(OS); break; case GenARMDecoder: - ARMDecoderEmitter(Records).run(Out.os()); + ARMDecoderEmitter(Records).run(OS); break; case GenAsmMatcher: - AsmMatcherEmitter(Records).run(Out.os()); - break; - case GenClangAttrClasses: - ClangAttrClassEmitter(Records).run(Out.os()); - break; - case GenClangAttrImpl: - ClangAttrImplEmitter(Records).run(Out.os()); - break; - case GenClangAttrList: - ClangAttrListEmitter(Records).run(Out.os()); - break; - case GenClangAttrPCHRead: - ClangAttrPCHReadEmitter(Records).run(Out.os()); - break; - case GenClangAttrPCHWrite: - ClangAttrPCHWriteEmitter(Records).run(Out.os()); - break; - case GenClangAttrSpellingList: - ClangAttrSpellingListEmitter(Records).run(Out.os()); - break; - case GenClangDiagsDefs: - ClangDiagsDefsEmitter(Records, ClangComponent).run(Out.os()); - break; - case GenClangDiagGroups: - ClangDiagGroupsEmitter(Records).run(Out.os()); - break; - case GenClangDiagsIndexName: - ClangDiagsIndexNameEmitter(Records).run(Out.os()); - break; - case GenClangDeclNodes: - ClangASTNodesEmitter(Records, "Decl", "Decl").run(Out.os()); - ClangDeclContextEmitter(Records).run(Out.os()); - break; - case GenClangStmtNodes: - ClangASTNodesEmitter(Records, "Stmt", "").run(Out.os()); - break; - case GenClangSACheckers: - ClangSACheckersEmitter(Records).run(Out.os()); + AsmMatcherEmitter(Records).run(OS); break; case GenDisassembler: - DisassemblerEmitter(Records).run(Out.os()); + DisassemblerEmitter(Records).run(OS); break; case GenPseudoLowering: - PseudoLoweringEmitter(Records).run(Out.os()); - break; - case GenOptParserDefs: - OptParserEmitter(Records, true).run(Out.os()); - break; - case GenOptParserImpl: - OptParserEmitter(Records, false).run(Out.os()); + PseudoLoweringEmitter(Records).run(OS); break; case GenDAGISel: - DAGISelEmitter(Records).run(Out.os()); + DAGISelEmitter(Records).run(OS); break; case GenFastISel: - FastISelEmitter(Records).run(Out.os()); + FastISelEmitter(Records).run(OS); break; case GenSubtarget: - SubtargetEmitter(Records).run(Out.os()); + SubtargetEmitter(Records).run(OS); break; case GenIntrinsic: - IntrinsicEmitter(Records).run(Out.os()); + IntrinsicEmitter(Records).run(OS); break; case GenTgtIntrinsic: - IntrinsicEmitter(Records, true).run(Out.os()); - break; - case GenLLVMCConf: - LLVMCConfigurationEmitter(Records).run(Out.os()); + IntrinsicEmitter(Records, true).run(OS); break; case GenEDInfo: - EDEmitter(Records).run(Out.os()); - break; - case GenArmNeon: - NeonEmitter(Records).run(Out.os()); - break; - case GenArmNeonSema: - NeonEmitter(Records).runHeader(Out.os()); - break; - case GenArmNeonTest: - NeonEmitter(Records).runTests(Out.os()); + EDEmitter(Records).run(OS); break; case PrintEnums: { std::vector<Record*> Recs = Records.getAllDerivedDefinitions(Class); for (unsigned i = 0, e = Recs.size(); i != e; ++i) - Out.os() << Recs[i]->getName() << ", "; - Out.os() << "\n"; + OS << Recs[i]->getName() << ", "; + OS << "\n"; break; } case PrintSets: @@ -371,33 +170,29 @@ int main(int argc, char **argv) { Sets.addFieldExpander("Set", "Elements"); std::vector<Record*> Recs = Records.getAllDerivedDefinitions("Set"); for (unsigned i = 0, e = Recs.size(); i != e; ++i) { - Out.os() << Recs[i]->getName() << " = ["; + OS << Recs[i]->getName() << " = ["; const std::vector<Record*> *Elts = Sets.expand(Recs[i]); assert(Elts && "Couldn't expand Set instance"); for (unsigned ei = 0, ee = Elts->size(); ei != ee; ++ei) - Out.os() << ' ' << (*Elts)[ei]->getName(); - Out.os() << " ]\n"; + OS << ' ' << (*Elts)[ei]->getName(); + OS << " ]\n"; } break; } default: assert(1 && "Invalid Action"); - return 1; + return true; } - // Declare success. - Out.keep(); - return 0; - - } catch (const TGError &Error) { - PrintError(Error); - } catch (const std::string &Error) { - PrintError(Error); - } catch (const char *Error) { - PrintError(Error); - } catch (...) { - errs() << argv[0] << ": Unknown unexpected exception occurred.\n"; + return false; } +}; + +int main(int argc, char **argv) { + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + cl::ParseCommandLineOptions(argc, argv); - return 1; + LLVMTableGenAction Action; + return TableGenMain(argv[0], Action); } diff --git a/utils/TableGen/TableGenBackend.cpp b/utils/TableGen/TableGenBackend.cpp deleted file mode 100644 index b3e33b5..0000000 --- a/utils/TableGen/TableGenBackend.cpp +++ /dev/null @@ -1,25 +0,0 @@ -//===- TableGenBackend.cpp - Base class for TableGen Backends ---*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file provides useful services for TableGen backends... -// -//===----------------------------------------------------------------------===// - -#include "TableGenBackend.h" -#include "Record.h" -using namespace llvm; - -void TableGenBackend::EmitSourceFileHeader(const std::string &Desc, - raw_ostream &OS) const { - OS << "//===- TableGen'erated file -------------------------------------*-" - " C++ -*-===//\n//\n// " << Desc << "\n//\n// Automatically generate" - "d file, do not edit!\n//\n//===------------------------------------" - "----------------------------------===//\n\n"; -} - diff --git a/utils/TableGen/TableGenBackend.h b/utils/TableGen/TableGenBackend.h deleted file mode 100644 index 9c2b948..0000000 --- a/utils/TableGen/TableGenBackend.h +++ /dev/null @@ -1,43 +0,0 @@ -//===- TableGenBackend.h - Base class for TableGen Backends -----*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// The TableGenBackend class is provided as a common interface for all TableGen -// backends. It provides useful services and an standardized interface. -// -//===----------------------------------------------------------------------===// - -#ifndef TABLEGENBACKEND_H -#define TABLEGENBACKEND_H - -#include "llvm/Support/raw_ostream.h" -#include <string> - -namespace llvm { - -class Record; -class RecordKeeper; - -struct TableGenBackend { - virtual ~TableGenBackend() {} - - // run - All TableGen backends should implement the run method, which should - // be the main entry point. - virtual void run(raw_ostream &OS) = 0; - - -public: // Useful helper routines... - /// EmitSourceFileHeader - Output a LLVM style file header to the specified - /// ostream. - void EmitSourceFileHeader(const std::string &Desc, raw_ostream &OS) const; - -}; - -} // End llvm namespace - -#endif diff --git a/utils/TableGen/X86DisassemblerTables.cpp b/utils/TableGen/X86DisassemblerTables.cpp index 7431059..e8c9a48 100644 --- a/utils/TableGen/X86DisassemblerTables.cpp +++ b/utils/TableGen/X86DisassemblerTables.cpp @@ -17,7 +17,7 @@ #include "X86DisassemblerShared.h" #include "X86DisassemblerTables.h" -#include "TableGenBackend.h" +#include "llvm/TableGen/TableGenBackend.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Format.h" @@ -32,26 +32,32 @@ using namespace X86Disassembler; /// @param parent - The class that may be the superset /// @return - True if child is a subset of parent, false otherwise. static inline bool inheritsFrom(InstructionContext child, - InstructionContext parent) { + InstructionContext parent, + bool VEX_LIG = false) { if (child == parent) return true; switch (parent) { case IC: - return true; + return(inheritsFrom(child, IC_64BIT) || + inheritsFrom(child, IC_OPSIZE) || + inheritsFrom(child, IC_XD) || + inheritsFrom(child, IC_XS)); case IC_64BIT: return(inheritsFrom(child, IC_64BIT_REXW) || inheritsFrom(child, IC_64BIT_OPSIZE) || inheritsFrom(child, IC_64BIT_XD) || inheritsFrom(child, IC_64BIT_XS)); case IC_OPSIZE: - return(inheritsFrom(child, IC_64BIT_OPSIZE)); + return inheritsFrom(child, IC_64BIT_OPSIZE); case IC_XD: - return(inheritsFrom(child, IC_64BIT_XD) || - inheritsFrom(child, IC_VEX_XD)); + return inheritsFrom(child, IC_64BIT_XD); case IC_XS: - return(inheritsFrom(child, IC_64BIT_XS) || - inheritsFrom(child, IC_VEX_XS)); + return inheritsFrom(child, IC_64BIT_XS); + case IC_XD_OPSIZE: + return inheritsFrom(child, IC_64BIT_XD_OPSIZE); + case IC_XS_OPSIZE: + return inheritsFrom(child, IC_64BIT_XS_OPSIZE); case IC_64BIT_REXW: return(inheritsFrom(child, IC_64BIT_REXW_XS) || inheritsFrom(child, IC_64BIT_REXW_XD) || @@ -62,42 +68,37 @@ static inline bool inheritsFrom(InstructionContext child, return(inheritsFrom(child, IC_64BIT_REXW_XD)); case IC_64BIT_XS: return(inheritsFrom(child, IC_64BIT_REXW_XS)); - case IC_64BIT_REXW_XD: + case IC_64BIT_XD_OPSIZE: + case IC_64BIT_XS_OPSIZE: return false; + case IC_64BIT_REXW_XD: case IC_64BIT_REXW_XS: - return false; case IC_64BIT_REXW_OPSIZE: return false; case IC_VEX: - return(inheritsFrom(child, IC_VEX_XS) || - inheritsFrom(child, IC_VEX_XD) || - inheritsFrom(child, IC_VEX_L) || - inheritsFrom(child, IC_VEX_W) || - inheritsFrom(child, IC_VEX_OPSIZE)); + return inheritsFrom(child, IC_VEX_W) || + (VEX_LIG && inheritsFrom(child, IC_VEX_L)); case IC_VEX_XS: - return(inheritsFrom(child, IC_VEX_L_XS) || - inheritsFrom(child, IC_VEX_W_XS)); + return inheritsFrom(child, IC_VEX_W_XS) || + (VEX_LIG && inheritsFrom(child, IC_VEX_L_XS)); case IC_VEX_XD: - return(inheritsFrom(child, IC_VEX_L_XD) || - inheritsFrom(child, IC_VEX_W_XD)); - case IC_VEX_L: - return(inheritsFrom(child, IC_VEX_L_XS) || - inheritsFrom(child, IC_VEX_L_XD)); - case IC_VEX_L_XS: - return false; - case IC_VEX_L_XD: - return false; + return 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) || + (VEX_LIG && inheritsFrom(child, IC_VEX_L_OPSIZE)); case IC_VEX_W: - return(inheritsFrom(child, IC_VEX_W_XS) || - inheritsFrom(child, IC_VEX_W_XD) || - inheritsFrom(child, IC_VEX_W_OPSIZE)); case IC_VEX_W_XS: - return false; case IC_VEX_W_XD: + case IC_VEX_W_OPSIZE: + return false; + case IC_VEX_L: + case IC_VEX_L_XS: + case IC_VEX_L_XD: + case IC_VEX_L_OPSIZE: return false; - case IC_VEX_OPSIZE: - return inheritsFrom(child, IC_VEX_W_OPSIZE); default: + llvm_unreachable("Unknown instruction class"); return false; } } @@ -515,6 +516,8 @@ void DisassemblerTables::emitContextTable(raw_ostream &o, uint32_t &i) const { o << "IC_VEX_XD"; else if ((index & ATTR_VEX) && (index & ATTR_XS)) o << "IC_VEX_XS"; + else if (index & ATTR_VEX) + o << "IC_VEX"; else if ((index & ATTR_64BIT) && (index & ATTR_REXW) && (index & ATTR_XS)) o << "IC_64BIT_REXW_XS"; else if ((index & ATTR_64BIT) && (index & ATTR_REXW) && (index & ATTR_XD)) @@ -522,6 +525,10 @@ void DisassemblerTables::emitContextTable(raw_ostream &o, uint32_t &i) const { else if ((index & ATTR_64BIT) && (index & ATTR_REXW) && (index & ATTR_OPSIZE)) o << "IC_64BIT_REXW_OPSIZE"; + else if ((index & ATTR_64BIT) && (index & ATTR_XD) && (index & ATTR_OPSIZE)) + o << "IC_64BIT_XD_OPSIZE"; + else if ((index & ATTR_64BIT) && (index & ATTR_XS) && (index & ATTR_OPSIZE)) + o << "IC_64BIT_XS_OPSIZE"; else if ((index & ATTR_64BIT) && (index & ATTR_XS)) o << "IC_64BIT_XS"; else if ((index & ATTR_64BIT) && (index & ATTR_XD)) @@ -532,14 +539,16 @@ void DisassemblerTables::emitContextTable(raw_ostream &o, uint32_t &i) const { o << "IC_64BIT_REXW"; else if ((index & ATTR_64BIT)) o << "IC_64BIT"; + else if ((index & ATTR_XS) && (index & ATTR_OPSIZE)) + o << "IC_XS_OPSIZE"; + else if ((index & ATTR_XD) && (index & ATTR_OPSIZE)) + o << "IC_XD_OPSIZE"; else if (index & ATTR_XS) o << "IC_XS"; else if (index & ATTR_XD) o << "IC_XD"; else if (index & ATTR_OPSIZE) o << "IC_OPSIZE"; - else if (index & ATTR_VEX) - o << "IC_VEX"; else o << "IC"; @@ -616,8 +625,11 @@ void DisassemblerTables::setTableFields(ModRMDecision &decision, if(newInfo.filtered) continue; // filtered instructions get lowest priority - if(previousInfo.name == "NOOP") - continue; // special case for XCHG32ar and NOOP + if(previousInfo.name == "NOOP" && (newInfo.name == "XCHG16ar" || + newInfo.name == "XCHG32ar" || + newInfo.name == "XCHG32ar64" || + newInfo.name == "XCHG64ar")) + continue; // special case for XCHG*ar and NOOP if (outranks(previousInfo.insnContext, newInfo.insnContext)) continue; @@ -643,14 +655,19 @@ void DisassemblerTables::setTableFields(OpcodeType type, InstructionContext insnContext, uint8_t opcode, const ModRMFilter &filter, - InstrUID uid) { + InstrUID uid, + bool is32bit, + bool ignoresVEX_L) { unsigned index; ContextDecision &decision = *Tables[type]; for (index = 0; index < IC_max; ++index) { + if (is32bit && inheritsFrom((InstructionContext)index, IC_64BIT)) + continue; + if (inheritsFrom((InstructionContext)index, - InstructionSpecifiers[uid].insnContext)) + InstructionSpecifiers[uid].insnContext, ignoresVEX_L)) setTableFields(decision.opcodeDecisions[index].modRMDecisions[opcode], filter, uid, diff --git a/utils/TableGen/X86DisassemblerTables.h b/utils/TableGen/X86DisassemblerTables.h index d16ebfc..e148cd2 100644 --- a/utils/TableGen/X86DisassemblerTables.h +++ b/utils/TableGen/X86DisassemblerTables.h @@ -260,11 +260,15 @@ public: /// @param filter - The ModRMFilter that decides which ModR/M byte values /// correspond to the desired instruction. /// @param uid - The unique ID of the instruction. + /// @param is32bit - Instructon is only 32-bit + /// @param ignoresVEX_L - Instruction ignores VEX.L void setTableFields(OpcodeType type, InstructionContext insnContext, uint8_t opcode, const ModRMFilter &filter, - InstrUID uid); + InstrUID uid, + bool is32bit, + bool ignoresVEX_L); /// specForUID - Returns the instruction specifier for a given unique /// instruction ID. Used when resolving collisions. diff --git a/utils/TableGen/X86RecognizableInstr.cpp b/utils/TableGen/X86RecognizableInstr.cpp index ea3bb70..cae8237 100644 --- a/utils/TableGen/X86RecognizableInstr.cpp +++ b/utils/TableGen/X86RecognizableInstr.cpp @@ -68,7 +68,7 @@ namespace X86Local { DC = 7, DD = 8, DE = 9, DF = 10, XD = 11, XS = 12, T8 = 13, P_TA = 14, - A6 = 15, A7 = 16 + A6 = 15, A7 = 16, TF = 17 }; } @@ -217,6 +217,7 @@ RecognizableInstr::RecognizableInstr(DisassemblerTables &tables, HasVEXPrefix = Rec->getValueAsBit("hasVEXPrefix"); HasVEX_4VPrefix = Rec->getValueAsBit("hasVEX_4VPrefix"); HasVEX_WPrefix = Rec->getValueAsBit("hasVEX_WPrefix"); + IgnoresVEX_L = Rec->getValueAsBit("ignoresVEX_L"); HasLockPrefix = Rec->getValueAsBit("hasLockPrefix"); IsCodeGenOnly = Rec->getValueAsBit("isCodeGenOnly"); @@ -225,15 +226,21 @@ RecognizableInstr::RecognizableInstr(DisassemblerTables &tables, Operands = &insn.Operands.OperandList; - IsSSE = HasOpSizePrefix && (Name.find("16") == Name.npos); + IsSSE = (HasOpSizePrefix && (Name.find("16") == Name.npos)) || + (Name.find("CRC32") != Name.npos); HasFROperands = hasFROperands(); HasVEX_LPrefix = has256BitOperands() || Rec->getValueAsBit("hasVEX_L"); // Check for 64-bit inst which does not require REX + Is32Bit = false; Is64Bit = false; // FIXME: Is there some better way to check for In64BitMode? std::vector<Record*> Predicates = Rec->getValueAsListOfDefs("Predicates"); for (unsigned i = 0, e = Predicates.size(); i != e; ++i) { + if (Predicates[i]->getName().find("32Bit") != Name.npos) { + Is32Bit = true; + break; + } if (Predicates[i]->getName().find("64Bit") != Name.npos) { Is64Bit = true; break; @@ -249,6 +256,8 @@ RecognizableInstr::RecognizableInstr(DisassemblerTables &tables, Rec->getName() == "REX64_PREFIX" || Rec->getName().find("VMREAD64") != Name.npos || Rec->getName().find("VMWRITE64") != Name.npos || + Rec->getName().find("INVEPT64") != Name.npos || + Rec->getName().find("INVVPID64") != Name.npos || Rec->getName().find("MOV64") != Name.npos || Rec->getName().find("PUSH64") != Name.npos || Rec->getName().find("POP64") != Name.npos; @@ -257,7 +266,7 @@ RecognizableInstr::RecognizableInstr(DisassemblerTables &tables, } void RecognizableInstr::processInstr(DisassemblerTables &tables, - const CodeGenInstruction &insn, + const CodeGenInstruction &insn, InstrUID uid) { // Ignore "asm parser only" instructions. @@ -276,7 +285,9 @@ InstructionContext RecognizableInstr::insnContext() const { InstructionContext insnContext; if (HasVEX_4VPrefix || HasVEXPrefix) { - if (HasOpSizePrefix && HasVEX_LPrefix) + if (HasVEX_LPrefix && HasVEX_WPrefix) + llvm_unreachable("Don't support VEX.L and VEX.W together"); + else if (HasOpSizePrefix && HasVEX_LPrefix) insnContext = IC_VEX_L_OPSIZE; else if (HasOpSizePrefix && HasVEX_WPrefix) insnContext = IC_VEX_W_OPSIZE; @@ -303,13 +314,19 @@ InstructionContext RecognizableInstr::insnContext() const { } else if (Is64Bit || HasREX_WPrefix) { if (HasREX_WPrefix && HasOpSizePrefix) insnContext = IC_64BIT_REXW_OPSIZE; + else if (HasOpSizePrefix && + (Prefix == X86Local::XD || Prefix == X86Local::TF)) + insnContext = IC_64BIT_XD_OPSIZE; + else if (HasOpSizePrefix && Prefix == X86Local::XS) + insnContext = IC_64BIT_XS_OPSIZE; else if (HasOpSizePrefix) insnContext = IC_64BIT_OPSIZE; else if (HasREX_WPrefix && Prefix == X86Local::XS) insnContext = IC_64BIT_REXW_XS; - else if (HasREX_WPrefix && Prefix == X86Local::XD) + else if (HasREX_WPrefix && + (Prefix == X86Local::XD || Prefix == X86Local::TF)) insnContext = IC_64BIT_REXW_XD; - else if (Prefix == X86Local::XD) + else if (Prefix == X86Local::XD || Prefix == X86Local::TF) insnContext = IC_64BIT_XD; else if (Prefix == X86Local::XS) insnContext = IC_64BIT_XS; @@ -318,11 +335,16 @@ InstructionContext RecognizableInstr::insnContext() const { else insnContext = IC_64BIT; } else { - if (HasOpSizePrefix) + if (HasOpSizePrefix && + (Prefix == X86Local::XD || Prefix == X86Local::TF)) + insnContext = IC_XD_OPSIZE; + else if (HasOpSizePrefix && Prefix == X86Local::XS) + insnContext = IC_XS_OPSIZE; + else if (HasOpSizePrefix) insnContext = IC_OPSIZE; - else if (Prefix == X86Local::XD) + else if (Prefix == X86Local::XD || Prefix == X86Local::TF) insnContext = IC_XD; - else if (Prefix == X86Local::XS) + else if (Prefix == X86Local::XS || Prefix == X86Local::REP) insnContext = IC_XS; else insnContext = IC; @@ -342,20 +364,13 @@ RecognizableInstr::filter_ret RecognizableInstr::filter() const { return FILTER_STRONG; if (Form == X86Local::Pseudo || - IsCodeGenOnly) + (IsCodeGenOnly && Name.find("_REV") == Name.npos)) return FILTER_STRONG; if (Form == X86Local::MRMInitReg) return FILTER_STRONG; - // TEMPORARY pending bug fixes - - if (Name.find("VMOVDQU") != Name.npos || - Name.find("VMOVDQA") != Name.npos || - Name.find("VROUND") != Name.npos) - return FILTER_STRONG; - // Filter out artificial instructions if (Name.find("TAILJMP") != Name.npos || @@ -401,7 +416,7 @@ RecognizableInstr::filter_ret RecognizableInstr::filter() const { // Filter out alternate forms of AVX instructions if (Name.find("_alt") != Name.npos || Name.find("XrYr") != Name.npos || - Name.find("r64r") != Name.npos || + (Name.find("r64r") != Name.npos && Name.find("r64r64") == Name.npos) || Name.find("_64mr") != Name.npos || Name.find("Xrr") != Name.npos || Name.find("rr64") != Name.npos) @@ -623,20 +638,43 @@ void RecognizableInstr::emitInstructionSpecifier(DisassemblerTables &tables) { case X86Local::MRMDestReg: // Operand 1 is a register operand in the R/M field. // Operand 2 is a register operand in the Reg/Opcode field. + // - In AVX, there is a register operand in the VEX.vvvv field here - // Operand 3 (optional) is an immediate. - assert(numPhysicalOperands >= 2 && numPhysicalOperands <= 3 && - "Unexpected number of operands for MRMDestRegFrm"); + if (HasVEX_4VPrefix) + assert(numPhysicalOperands >= 3 && numPhysicalOperands <= 4 && + "Unexpected number of operands for MRMDestRegFrm with VEX_4V"); + else + assert(numPhysicalOperands >= 2 && numPhysicalOperands <= 3 && + "Unexpected number of operands for MRMDestRegFrm"); + HANDLE_OPERAND(rmRegister) + + 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 + HANDLE_OPERAND(vvvvRegister) + HANDLE_OPERAND(roRegister) HANDLE_OPTIONAL(immediate) break; case X86Local::MRMDestMem: // Operand 1 is a memory operand (possibly SIB-extended) // Operand 2 is a register operand in the Reg/Opcode field. + // - In AVX, there is a register operand in the VEX.vvvv field here - // Operand 3 (optional) is an immediate. - assert(numPhysicalOperands >= 2 && numPhysicalOperands <= 3 && - "Unexpected number of operands for MRMDestMemFrm"); + if (HasVEX_4VPrefix) + assert(numPhysicalOperands >= 3 && numPhysicalOperands <= 4 && + "Unexpected number of operands for MRMDestMemFrm with VEX_4V"); + else + assert(numPhysicalOperands >= 2 && numPhysicalOperands <= 3 && + "Unexpected number of operands for MRMDestMemFrm"); HANDLE_OPERAND(memory) + + 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 + HANDLE_OPERAND(vvvvRegister) + HANDLE_OPERAND(roRegister) HANDLE_OPTIONAL(immediate) break; @@ -805,6 +843,7 @@ void RecognizableInstr::emitDecodePath(DisassemblerTables &tables) const { opcodeToSet = Opcode; break; case X86Local::T8: + case X86Local::TF: opcodeType = THREEBYTE_38; if (needsModRMForDecode(Form)) filter = new ModFilter(isRegFormat(Form)); @@ -855,6 +894,7 @@ void RecognizableInstr::emitDecodePath(DisassemblerTables &tables) const { } opcodeToSet = 0xd8 + (Prefix - X86Local::D8); break; + case X86Local::REP: default: opcodeType = ONEBYTE; switch (Opcode) { @@ -926,7 +966,7 @@ void RecognizableInstr::emitDecodePath(DisassemblerTables &tables) const { insnContext(), currentOpcode, *filter, - UID); + UID, Is32Bit, IgnoresVEX_L); Spec->modifierType = MODIFIER_OPCODE; Spec->modifierBase = opcodeToSet; @@ -936,14 +976,14 @@ void RecognizableInstr::emitDecodePath(DisassemblerTables &tables) const { insnContext(), opcodeToSet, *filter, - UID); + UID, Is32Bit, IgnoresVEX_L); } } else { tables.setTableFields(opcodeType, insnContext(), opcodeToSet, *filter, - UID); + UID, Is32Bit, IgnoresVEX_L); Spec->modifierType = MODIFIER_NONE; Spec->modifierBase = opcodeToSet; @@ -984,6 +1024,7 @@ OperandType RecognizableInstr::typeFromString(const std::string &s, TYPE("i32mem", TYPE_Mv) TYPE("i32imm", TYPE_IMMv) TYPE("i32i8imm", TYPE_IMM32) + TYPE("u32u8imm", TYPE_IMM32) TYPE("GR32", TYPE_Rv) TYPE("i64mem", TYPE_Mv) TYPE("i64i32imm", TYPE_IMM64) @@ -1029,6 +1070,9 @@ OperandType RecognizableInstr::typeFromString(const std::string &s, TYPE("offset32", TYPE_MOFFS32) TYPE("offset64", TYPE_MOFFS64) TYPE("VR256", TYPE_XMM256) + TYPE("GR16_NOAX", TYPE_Rv) + TYPE("GR32_NOAX", TYPE_Rv) + TYPE("GR64_NOAX", TYPE_R64) errs() << "Unhandled type string " << s << "\n"; llvm_unreachable("Unhandled type string"); } @@ -1044,6 +1088,7 @@ OperandEncoding RecognizableInstr::immediateEncodingFromString ENCODING("i16imm", ENCODING_IW) } ENCODING("i32i8imm", ENCODING_IB) + ENCODING("u32u8imm", ENCODING_IB) ENCODING("SSECC", ENCODING_IB) ENCODING("i16imm", ENCODING_Iv) ENCODING("i16i8imm", ENCODING_IB) @@ -1097,6 +1142,8 @@ OperandEncoding RecognizableInstr::roRegisterEncodingFromString OperandEncoding RecognizableInstr::vvvvRegisterEncodingFromString (const std::string &s, bool hasOpSizePrefix) { + ENCODING("GR32", ENCODING_VVVV) + ENCODING("GR64", ENCODING_VVVV) ENCODING("FR32", ENCODING_VVVV) ENCODING("FR64", ENCODING_VVVV) ENCODING("VR128", ENCODING_VVVV) @@ -1169,6 +1216,9 @@ OperandEncoding RecognizableInstr::opcodeModifierEncodingFromString ENCODING("GR64", ENCODING_RO) ENCODING("GR16", ENCODING_Rv) ENCODING("GR8", ENCODING_RB) + ENCODING("GR16_NOAX", ENCODING_Rv) + ENCODING("GR32_NOAX", ENCODING_Rv) + ENCODING("GR64_NOAX", ENCODING_RO) errs() << "Unhandled opcode modifier encoding " << s << "\n"; llvm_unreachable("Unhandled opcode modifier encoding"); } diff --git a/utils/TableGen/X86RecognizableInstr.h b/utils/TableGen/X86RecognizableInstr.h index 677d9f0..4441597 100644 --- a/utils/TableGen/X86RecognizableInstr.h +++ b/utils/TableGen/X86RecognizableInstr.h @@ -20,8 +20,8 @@ #include "X86DisassemblerTables.h" #include "CodeGenTarget.h" -#include "Record.h" +#include "llvm/TableGen/Record.h" #include "llvm/Support/DataTypes.h" #include "llvm/ADT/SmallVector.h" @@ -60,12 +60,16 @@ private: bool HasVEX_WPrefix; /// Inferred from the operands; indicates whether the L bit in the VEX prefix is set bool HasVEX_LPrefix; + // The ignoreVEX_L field from the record + bool IgnoresVEX_L; /// The hasLockPrefix field from the record bool HasLockPrefix; /// The isCodeGenOnly filed from the record bool IsCodeGenOnly; - // Whether the instruction has the predicate "Mode64Bit" + // Whether the instruction has the predicate "In64BitMode" bool Is64Bit; + // Whether the instruction has the predicate "In32BitMode" + bool Is32Bit; /// The instruction name as listed in the tables std::string Name; diff --git a/utils/buildit/build_llvm b/utils/buildit/build_llvm index 5665e4c..0ffbc19 100755 --- a/utils/buildit/build_llvm +++ b/utils/buildit/build_llvm @@ -97,31 +97,16 @@ if [ "$ARM_HOSTED_BUILD" = yes ]; then echo 'exec '$T' "$@"' >> $P || exit 1 chmod a+x $P || exit 1 done - # Try to use the platform llvm-gcc. Fall back to gcc if it's not available. - for prog in gcc g++ ; do + # Set up the links for clang. + for prog in clang clang++ ; do P=$DIR/bin/arm-apple-darwin$DARWIN_VERS-${prog} - T=`xcrun -sdk $SDKROOT -find llvm-${prog}` - if [ "x$T" = "x" ] ; then - T=`xcrun -sdk $SDKROOT -find ${prog}` - fi + T=`xcrun -sdk $SDKROOT -find ${prog}` echo '#!/bin/sh' > $P || exit 1 echo 'exec '$T' -arch armv7 -isysroot '${SDKROOT}' "$@"' >> $P || exit 1 chmod a+x $P || exit 1 done PATH=$DIR/bin:$PATH -# otherwise, try to use llvm-gcc if it's available -elif [ $DARWIN_VERS -gt 9 ]; then - # If the user has set CC or CXX, respect their wishes. If not, - # compile with LLVM-GCC/LLVM-G++ if available; if LLVM is not - # available, fall back to usual GCC/G++ default. - savedPATH=$PATH ; PATH="/Developer/usr/bin:$PATH" - XTMPCC=$(which llvm-gcc) - if [ x$CC = x -a x$XTMPCC != x ] ; then export CC=$XTMPCC ; fi - XTMPCC=$(which llvm-g++) - if [ x$CXX = x -a x$XTMPCC != x ] ; then export CXX=$XTMPCC ; fi - PATH=$savedPATH - unset XTMPCC savedPATH fi if [ "$ARM_HOSTED_BUILD" = yes ]; then @@ -157,32 +142,15 @@ else LLVM_VERSION="$LLVM_SUBMIT_VERSION-$LLVM_SUBMIT_SUBVERSION" fi -GCC_VER=`cc --version 2>/dev/null | sed 1q` - -if echo "$GCC_VER" | grep GCC > /dev/null; then - GCC_VER=`echo $GCC_VER | sed -e 's/.*(GCC) \([0-9.][0-9.]*\).*/\1/'` - MAJ_VER=`echo $GCC_VER | sed 's/\..*//'` - MIN_VER=`echo $GCC_VER | sed 's/[^.]*\.\([0-9]*\).*/\1/'` -fi - -JOBS_FLAG="" - -# Note: If compiling with GCC 4.0, don't pass the -jN flag. Building universal -# already has parallelism and we don't want to make the builders hit swap by -# firing off too many gccs at the same time. -if [ "x$MAJ_VER" != "x4" -o "x$MIN_VER" != "x0" ]; then - # Figure out how many make processes to run. - SYSCTL=`sysctl -n hw.activecpu` - - # sysctl -n hw.* does not work when invoked via B&I chroot /BuildRoot. - # Builders can default to 2, since even if they are single processor, - # nothing else is running on the machine. - if [ -z "$SYSCTL" ]; then - SYSCTL=2 - fi - - JOBS_FLAG="-j $SYSCTL" +# Figure out how many make processes to run. +SYSCTL=`sysctl -n hw.activecpu` +# sysctl -n hw.* does not work when invoked via B&I chroot /BuildRoot. +# Builders can default to 2, since even if they are single processor, +# nothing else is running on the machine. +if [ -z "$SYSCTL" ]; then + SYSCTL=2 fi +JOBS_FLAG="-j $SYSCTL" make $JOBS_FLAG $OPTIMIZE_OPTS UNIVERSAL=1 UNIVERSAL_ARCH="$HOSTS" \ UNIVERSAL_SDK_PATH=$SDKROOT \ @@ -233,14 +201,20 @@ RC_ProjectSourceSubversion=`printf "%d" $LLVM_MINOR_VERSION` echo "#define LLVM_VERSION ${RC_ProjectSourceVersion}" > $DEST_DIR$DEST_ROOT/include/llvm/Version.h echo "#define LLVM_MINOR_VERSION ${RC_ProjectSourceSubversion}" >> $DEST_DIR$DEST_ROOT/include/llvm/Version.h +# Find the right version of strip to use. +STRIP=strip +if [ -n "$SDKROOT" ]; then + STRIP=`xcrun -sdk $SDKROOT -find strip` +fi + if [ "x$LLVM_DEBUG" != "x1" ]; then # Strip local symbols from llvm libraries. # # Use '-l' to strip i386 modules. N.B. that flag doesn't work with kext or # PPC objects! - strip -Sl $DEST_DIR$DEST_ROOT/lib/*.[oa] + $STRIP -Sl $DEST_DIR$DEST_ROOT/lib/*.[oa] for f in `ls $DEST_DIR$DEST_ROOT/lib/*.so`; do - strip -Sxl $f + $STRIP -Sxl $f done fi @@ -329,7 +303,7 @@ if [ "$INSTALL_LIBLTO" = "yes" ]; then # Use '-l' to strip i386 modules. N.B. that flag doesn't work with kext or # PPC objects! - strip -arch all -Sl $DT_HOME/lib/libLTO.dylib + $STRIP -arch all -Sl $DT_HOME/lib/libLTO.dylib if [ "x$DISABLE_USR_LINKS" == "x" ]; then # Add a symlink in /usr/lib for B&I. @@ -359,7 +333,7 @@ find $DEST_DIR -name \*.dSYM -print | xargs rm -r || exit 1 # PPC objects! find $DEST_DIR -perm -0111 -type f \ ! \( -name '*.la' -o -name gccas -o -name gccld -o -name llvm-config \) \ - -print | xargs -n 1 -P ${SYSCTL} strip -arch all -Sl + -print | xargs -n 1 -P ${SYSCTL} $STRIP -arch all -Sl chgrp -h -R wheel $DEST_DIR chgrp -R wheel $DEST_DIR diff --git a/utils/lit/lit/LitConfig.py b/utils/lit/lit/LitConfig.py index bda9174..2cc2781 100644 --- a/utils/lit/lit/LitConfig.py +++ b/utils/lit/lit/LitConfig.py @@ -20,7 +20,7 @@ class LitConfig: def __init__(self, progname, path, quiet, useValgrind, valgrindLeakCheck, valgrindArgs, useTclAsSh, - noExecute, debug, isWindows, + noExecute, ignoreStdErr, debug, isWindows, params): # The name of the test runner. self.progname = progname @@ -32,6 +32,7 @@ class LitConfig: self.valgrindUserArgs = list(valgrindArgs) self.useTclAsSh = bool(useTclAsSh) self.noExecute = noExecute + self.ignoreStdErr = ignoreStdErr self.debug = debug self.isWindows = bool(isWindows) self.params = dict(params) diff --git a/utils/lit/lit/TestFormats.py b/utils/lit/lit/TestFormats.py index 6dda2fd..d1c0558 100644 --- a/utils/lit/lit/TestFormats.py +++ b/utils/lit/lit/TestFormats.py @@ -125,7 +125,11 @@ class ShTest(FileBasedTest): self.execute_external) class TclTest(FileBasedTest): + def __init__(self, ignoreStdErr=False): + self.ignoreStdErr = ignoreStdErr + def execute(self, test, litConfig): + litConfig.ignoreStdErr = self.ignoreStdErr return TestRunner.executeTclTest(test, litConfig) ### diff --git a/utils/lit/lit/TestRunner.py b/utils/lit/lit/TestRunner.py index 80d0ba1..f5f7c19 100644 --- a/utils/lit/lit/TestRunner.py +++ b/utils/lit/lit/TestRunner.py @@ -397,7 +397,8 @@ def parseIntegratedTestScript(test, normalize_slashes=False): sourcedir = os.path.dirname(sourcepath) execpath = test.getExecPath() execdir,execbase = os.path.split(execpath) - tmpBase = os.path.join(execdir, 'Output', execbase) + tmpDir = os.path.join(execdir, 'Output') + tmpBase = os.path.join(tmpDir, execbase) if test.index is not None: tmpBase += '_%d' % test.index @@ -405,6 +406,7 @@ def parseIntegratedTestScript(test, normalize_slashes=False): if normalize_slashes: sourcepath = sourcepath.replace('\\', '/') sourcedir = sourcedir.replace('\\', '/') + tmpDir = tmpDir.replace('\\', '/') tmpBase = tmpBase.replace('\\', '/') # We use #_MARKER_# to hide %% while we do the other substitutions. @@ -414,6 +416,7 @@ def parseIntegratedTestScript(test, normalize_slashes=False): ('%S', sourcedir), ('%p', sourcedir), ('%t', tmpBase + '.tmp'), + ('%T', tmpDir), # FIXME: Remove this once we kill DejaGNU. ('%abs_tmp', tmpBase + '.tmp'), ('#_MARKER_#', '%')]) @@ -533,13 +536,13 @@ def executeTclTest(test, litConfig): # considered to fail if there is any standard error output. out,err,exitCode = res if isXFail: - ok = exitCode != 0 or err + ok = exitCode != 0 or err and not litConfig.ignoreStdErr if ok: status = Test.XFAIL else: status = Test.XPASS else: - ok = exitCode == 0 and not err + ok = exitCode == 0 and (not err or litConfig.ignoreStdErr) if ok: status = Test.PASS else: @@ -550,7 +553,7 @@ def executeTclTest(test, litConfig): # Set a flag for formatTestOutput so it can explain why the test was # considered to have failed, despite having an exit code of 0. - failDueToStderr = exitCode == 0 and err + failDueToStderr = exitCode == 0 and err and not litConfig.ignoreStdErr return formatTestOutput(status, out, err, exitCode, failDueToStderr, script) diff --git a/utils/lit/lit/TestingConfig.py b/utils/lit/lit/TestingConfig.py index 25bb341..a92dca8 100644 --- a/utils/lit/lit/TestingConfig.py +++ b/utils/lit/lit/TestingConfig.py @@ -1,4 +1,5 @@ import os +import sys class TestingConfig: """" @@ -14,12 +15,18 @@ class TestingConfig: 'LD_LIBRARY_PATH' : os.environ.get('LD_LIBRARY_PATH',''), 'PATH' : os.pathsep.join(litConfig.path + [os.environ.get('PATH','')]), - 'PATHEXT' : os.environ.get('PATHEXT',''), 'SYSTEMROOT' : os.environ.get('SYSTEMROOT',''), - 'LLVM_DISABLE_CRT_DEBUG' : '1', - 'PYTHONUNBUFFERED' : '1', } + if sys.platform == 'win32': + environment.update({ + 'LLVM_DISABLE_CRT_DEBUG' : '1', + 'PATHEXT' : os.environ.get('PATHEXT',''), + 'PYTHONUNBUFFERED' : '1', + 'TEMP' : os.environ.get('TEMP',''), + 'TMP' : os.environ.get('TMP',''), + }) + config = TestingConfig(parent, name = '<unnamed>', suffixes = set(), diff --git a/utils/lit/lit/main.py b/utils/lit/lit/main.py index 13d2630..e1a380c 100755 --- a/utils/lit/lit/main.py +++ b/utils/lit/lit/main.py @@ -328,6 +328,7 @@ def load_test_suite(inputs): valgrindArgs = [], useTclAsSh = False, noExecute = False, + ignoreStdErr = False, debug = False, isWindows = (platform.system()=='Windows'), params = {}) @@ -485,6 +486,7 @@ def main(builtinParameters = {}): # Bump the GIL check interval, its more imp valgrindArgs = opts.valgrindArgs, useTclAsSh = opts.useTclAsSh, noExecute = opts.noExecute, + ignoreStdErr = False, debug = opts.debug, isWindows = (platform.system()=='Windows'), params = userParams) diff --git a/utils/llvm.grm b/utils/llvm.grm index 3f33702..fb26dbb 100644 --- a/utils/llvm.grm +++ b/utils/llvm.grm @@ -172,7 +172,7 @@ FuncAttr ::= noreturn | optsize | ssp | sspreq - | hotpatch + | returns_twice | nonlazybind ; diff --git a/utils/llvmbuild b/utils/llvmbuild index c7d8814..b623d32 100755 --- a/utils/llvmbuild +++ b/utils/llvmbuild @@ -189,6 +189,10 @@ def add_options(parser): help=("Extra flags to pass to gcc configure [default: %default]")) parser.add_option("--force-configure", default=False, action="store_true", help=("Force reconfigure of all components")) + parser.add_option("--no-gcc", default=False, action="store_true", + help=("Do not build dragonegg and gcc")) + parser.add_option("--no-install", default=False, action="store_true", + help=("Do not do installs")) return def check_options(parser, options, valid_builds): @@ -622,6 +626,12 @@ class Builder(threading.Thread): for component in components: comp = component[:] + + if (self.options.no_gcc): + if (comp == 'gcc' or comp == 'dragonegg' or comp == 'llvm2'): + self.logger.info("Skipping " + component + " in " + + builddir) + continue srcdir = source + "/" + comp.rstrip("2") builddir = self.build_prefix + "/" + comp + "/" + build_suffix @@ -643,16 +653,20 @@ class Builder(threading.Thread): configure_env[comp_key][build]) self.logger.info("Building " + component + " in " + builddir) + self.logger.info("Build: make " + str(make_flags[comp_key][build])) self.make(component, srcdir, builddir, make_flags[comp_key][build], make_env[comp_key][build]) - self.logger.info("Installing " + component + " in " + installdir) - self.make(component, srcdir, builddir, - make_install_flags[comp_key][build], - make_install_env[comp_key][build]) + if (not self.options.no_install): + self.logger.info("Installing " + component + " in " + installdir) + self.make(component, srcdir, builddir, + make_install_flags[comp_key][build], + make_install_env[comp_key][build]) self.logger.info("Testing " + component + " in " + builddir) + self.logger.info("Test: make " + + str(make_check_flags[comp_key][build])) self.make(component, srcdir, builddir, make_check_flags[comp_key][build], make_check_env[comp_key][build]) @@ -741,9 +755,10 @@ if jobs == 0: jobs = 1 numthreads = options.threads -if jobs < numthreads: - numthreads = jobs - jobs = 1 + +logging.getLogger().info("Building with " + str(options.jobs) + " jobs and " + + str(numthreads) + " threads using " + str(jobs) + + " make jobs") for t in range(numthreads): builder = Builder(work_queue, jobs, diff --git a/utils/release/findRegressions.py b/utils/release/findRegressions.py index e801dab..7629c8b 100755 --- a/utils/release/findRegressions.py +++ b/utils/release/findRegressions.py @@ -1,68 +1,60 @@ #!/usr/bin/python -import re, string, sys, os, time +import re, string, sys, os, time, math DEBUG = 0 -testDirName = 'llvm-test' -test = ['compile', 'llc', 'jit', 'cbe'] -exectime = ['llc-time', 'jit-time', 'cbe-time',] -comptime = ['llc', 'jit-comptime', 'compile'] -(tp, exp) = ('compileTime_', 'executeTime_') +(tp, exp) = ('compile', 'exec') def parse(file): - f=open(file, 'r') + f = open(file, 'r') d = f.read() - #Cleanup weird stuff - d = re.sub(r',\d+:\d','', d) - + # Cleanup weird stuff + d = re.sub(r',\d+:\d', '', d) + r = re.findall(r'TEST-(PASS|FAIL|RESULT.*?):\s+(.*?)\s+(.*?)\r*\n', d) - + test = {} fname = '' for t in r: if DEBUG: print t + if t[0] == 'PASS' or t[0] == 'FAIL' : - tmp = t[2].split(testDirName) + tmp = t[2].split('llvm-test/') if DEBUG: print tmp - + if len(tmp) == 2: fname = tmp[1].strip('\r\n') else: fname = tmp[0].strip('\r\n') - - if not test.has_key(fname) : + + if not test.has_key(fname): test[fname] = {} - - for k in test: - test[fname][k] = 'NA' - test[fname][t[1]] = t[0] - if DEBUG: - print test[fname][t[1]] + + test[fname][t[1] + ' state'] = t[0] + test[fname][t[1] + ' time'] = float('nan') else : try: n = t[0].split('RESULT-')[1] - + if DEBUG: - print n; + print "n == ", n; - if n == 'llc' or n == 'jit-comptime' or n == 'compile': - test[fname][tp + n] = float(t[2].split(' ')[2]) + if n == 'compile-success': + test[fname]['compile time'] = float(t[2].split('program')[1].strip('\r\n')) + + elif n == 'exec-success': + test[fname]['exec time'] = float(t[2].split('program')[1].strip('\r\n')) if DEBUG: - print test[fname][tp + n] - - elif n.endswith('-time') : - test[fname][exp + n] = float(t[2].strip('\r\n')) - if DEBUG: - print test[fname][exp + n] - + print test[fname][string.replace(n, '-success', '')] + else : - print "ERROR!" + # print "ERROR!" sys.exit(1) - + except: continue @@ -72,59 +64,60 @@ def parse(file): def diffResults(d_old, d_new): for t in sorted(d_old.keys()) : - if DEBUG: - print t - - if d_new.has_key(t) : - + if d_new.has_key(t): + # Check if the test passed or failed. - for x in test: + for x in ['compile state', 'compile time', 'exec state', 'exec time']: + + if not d_old[t].has_key(x) and not d_new[t].has_key(x): + continue + if d_old[t].has_key(x): if d_new[t].has_key(x): + if d_old[t][x] == 'PASS': if d_new[t][x] != 'PASS': - print t + " *** REGRESSION (" + x + ")\n" + print t + " *** REGRESSION (" + x + " now fails)" else: if d_new[t][x] == 'PASS': - print t + " * NEW PASS (" + x + ")\n" - - else : - print t + "*** REGRESSION (" + x + ")\n" - - # For execution time, if there is no result, its a fail. - for x in exectime: - if d_old[t].has_key(tp + x): - if not d_new[t].has_key(tp + x): - print t + " *** REGRESSION (" + tp + x + ")\n" - - else : - if d_new[t].has_key(tp + x): - print t + " * NEW PASS (" + tp + x + ")\n" - - - for x in comptime: - if d_old[t].has_key(exp + x): - if not d_new[t].has_key(exp + x): - print t + " *** REGRESSION (" + exp + x + ")\n" - + print t + " * NEW PASS (" + x + " now fails)" + else : - if d_new[t].has_key(exp + x): - print t + " * NEW PASS (" + exp + x + ")\n" - + print t + "*** REGRESSION (" + x + " now fails)" + + if x == 'compile state' or x == 'exec state': + continue + + # For execution time, if there is no result it's a fail. + if not d_old[t].has_key(x) and not d_new[t].has_key(x): + continue + elif not d_new[t].has_key(x): + print t + " *** REGRESSION (" + x + ")" + elif not d_old[t].has_key(x): + print t + " * NEW PASS (" + x + ")" + + if math.isnan(d_old[t][x]) and math.isnan(d_new[t][x]): + continue + + elif math.isnan(d_old[t][x]) and not math.isnan(d_new[t][x]): + print t + " * NEW PASS (" + x + ")" + + elif not math.isnan(d_old[t][x]) and math.isnan(d_new[t][x]): + print t + " *** REGRESSION (" + x + ")" + + if d_new[t][x] > d_old[t][x] and \ + (d_new[t][x] - d_old[t][x]) / d_new[t][x] > .05: + print t + " *** REGRESSION (" + x + ")" + else : - print t + ": Removed from test-suite.\n" - + print t + ": Removed from test-suite." -#Main +# Main if len(sys.argv) < 3 : - print 'Usage:', sys.argv[0], \ - '<old log> <new log>' - sys.exit(-1) + print 'Usage:', sys.argv[0], '<old log> <new log>' + sys.exit(-1) d_old = parse(sys.argv[1]) d_new = parse(sys.argv[2]) - diffResults(d_old, d_new) - - diff --git a/utils/release/test-release.sh b/utils/release/test-release.sh index 21d7fee..4255324 100755 --- a/utils/release/test-release.sh +++ b/utils/release/test-release.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash #===-- test-release.sh - Test the LLVM release candidates ------------------===# # # The LLVM Compiler Infrastructure @@ -12,7 +12,16 @@ # #===------------------------------------------------------------------------===# -set -e # Exit if any command fails +if [ `uname -s` = "FreeBSD" ]; then + MAKE=gmake +else + MAKE=make +fi + +projects="llvm cfe dragonegg test-suite" + +# Base SVN URL for the sources. +Base_url="http://llvm.org/svn/llvm-project" Release="" Release_no_dot="" @@ -20,8 +29,10 @@ RC="" do_checkout="yes" do_ada="no" do_objc="yes" -do_fortran="yes" +do_fortran="no" do_64bit="yes" +do_debug="no" +do_asserts="no" BuildDir="`pwd`" function usage() { @@ -34,8 +45,10 @@ function usage() { echo " -no-checkout Don't checkout the sources from SVN." echo " -no-64bit Don't test the 64-bit version. [default: yes]" echo " -enable-ada Build Ada. [default: disable]" + echo " -enable-fortran Enable Fortran build. [default: disable]" echo " -disable-objc Disable ObjC build. [default: enable]" - echo " -disable-fortran Disable Fortran build. [default: enable]" + echo " -test-debug Test the debug build. [default: no]" + echo " -test-asserts Test with asserts on. [default: no]" } while [ $# -gt 0 ]; do @@ -69,13 +82,17 @@ while [ $# -gt 0 ]; do -enable-ada | --enable-ada ) do_ada="yes" ;; + -enable-fortran | --enable-fortran ) + do_fortran="yes" + ;; -disable-objc | --disable-objc ) do_objc="no" ;; - -disable-fortran | --disable-fortran ) - echo "WARNING: Do you *really* need to disable Fortran?" - sleep 5 - do_fortran="no" + -test-debug | --test-debug ) + do_debug="yes" + ;; + -test-asserts | --test-asserts ) + do_asserts="yes" ;; -help | --help | -h | --h | -\? ) usage @@ -92,11 +109,11 @@ done # Check required arguments. if [ -z "$Release" ]; then - echo "No release number specified!" + echo "error: no release number specified" exit 1 fi if [ -z "$RC" ]; then - echo "No release candidate number specified!" + echo "error: no release candidate number specified" exit 1 fi @@ -114,55 +131,53 @@ if [ -z "$NumJobs" ]; then NumJobs=3 fi -# Location of sources. -llvmCore_srcdir=$BuildDir/llvmCore-$Release-rc$RC.src -llvmgcc42_srcdir=$BuildDir/llvmgcc42-$Release-rc$RC.src +# Go to the build directory (may be different from CWD) +BuildDir=$BuildDir/rc$RC +mkdir -p $BuildDir +cd $BuildDir # Location of log files. -LogDirName="$Release-rc$RC.logs" -LogDir=$BuildDir/$LogDirName +LogDir=$BuildDir/logs mkdir -p $LogDir -# SVN URLs for the sources. -Base_url="http://llvm.org/svn/llvm-project" -llvmCore_RC_url="$Base_url/llvm/tags/RELEASE_$Release_no_dot/rc$RC" -llvmgcc42_RC_url="$Base_url/llvm-gcc-4.2/tags/RELEASE_$Release_no_dot/rc$RC" -clang_RC_url="$Base_url/cfe/tags/RELEASE_$Release_no_dot/rc$RC" -test_suite_RC_url="$Base_url/test-suite/tags/RELEASE_$Release_no_dot/rc$RC" +# Find a compilers. +c_compiler="$CC" +cxx_compiler="$CXX" # Make sure that the URLs are valid. function check_valid_urls() { - echo "# Validating SVN URLs" - if ! svn ls $llvmCore_RC_url > /dev/null 2>&1 ; then - echo "llvm $Release release candidate $RC doesn't exist!" - exit 1 - fi - if ! svn ls $llvmgcc42_RC_url > /dev/null 2>&1 ; then - echo "llvm-gcc-4.2 $Release release candidate $RC doesn't exist!" - exit 1 - fi - if ! svn ls $clang_RC_url > /dev/null 2>&1 ; then - echo "clang $Release release candidate $RC doesn't exist!" - exit 1 - fi - if ! svn ls $test_suite_RC_url > /dev/null 2>&1 ; then - echo "test-suite $Release release candidate $RC doesn't exist!" - exit 1 - fi + for proj in $projects ; do + echo "# Validating $proj SVN URL" + + if ! svn ls $Base_url/$proj/tags/RELEASE_$Release_no_dot/rc$RC > /dev/null 2>&1 ; then + echo "llvm $Release release candidate $RC doesn't exist!" + exit 1 + fi + done } # Export sources to the the build directory. function export_sources() { check_valid_urls - echo "# Exporting llvm $Release-RC$RC sources" - svn export -q $llvmCore_RC_url $llvmCore_srcdir - echo "# Exporting llvm-gcc-4.2 $Release-rc$RC sources" - svn export -q $llvmgcc42_RC_url $llvmgcc42_srcdir - echo "# Exporting clang $Release-rc$RC sources" - svn export -q $clang_RC_url $llvmCore_srcdir/tools/clang - echo "# Exporting llvm test suite $Release-rc$RC sources" - svn export -q $test_suite_RC_url $llvmCore_srcdir/projects/llvm-test + for proj in $projects ; do + echo "# Exporting $proj $Release-RC$RC sources" + if ! svn export -q $Base_url/$proj/tags/RELEASE_$Release_no_dot/rc$RC $proj.src ; then + echo "error: failed to export $proj project" + exit 1 + fi + done + + echo "# Creating symlinks" + cd $BuildDir/llvm.src/tools + if [ ! -h clang ]; then + ln -s $BuildDir/cfe.src clang + fi + cd $BuildDir/llvm.src/projects + if [ ! -h llvm-test ]; then + ln -s $BuildDir/test-suite.src llvm-test + fi + cd $BuildDir } function configure_llvmCore() { @@ -170,7 +185,6 @@ function configure_llvmCore() { Flavor="$2" ObjDir="$3" InstallDir="$4" - llvmgccDir="$5" case $Flavor in Release | Release-64 ) @@ -186,24 +200,26 @@ function configure_llvmCore() { Assertions="yes" ;; * ) - echo "# Invalid flavor $Flavor!" + echo "# Invalid flavor '$Flavor'" echo "" return ;; esac + echo "# Using C compiler: $c_compiler" + echo "# Using C++ compiler: $cxx_compiler" + cd $ObjDir echo "# Configuring llvm $Release-rc$RC $Flavor" - echo "# $llvmCore_srcdir/configure --prefix=$InstallDir \ + echo "# $BuildDir/llvm.src/configure --prefix=$InstallDir \ --enable-optimized=$Optimized \ - --enable-assertions=$Assertions \ - --with-llvmgccdir=$llvmgccDir" - $llvmCore_srcdir/configure --prefix=$InstallDir \ + --enable-assertions=$Assertions" + env CC=$c_compiler CXX=$cxx_compiler \ + $BuildDir/llvm.src/configure --prefix=$InstallDir \ --enable-optimized=$Optimized \ --enable-assertions=$Assertions \ - --with-llvmgccdir=$llvmgccDir \ - > $LogDir/llvm.configure.$Release-rc$RC-Phase$Phase-$Flavor.log 2>&1 - cd - + 2>&1 | tee $LogDir/llvm.configure-Phase$Phase-$Flavor.log + cd $BuildDir } function build_llvmCore() { @@ -212,25 +228,21 @@ function build_llvmCore() { ObjDir="$3" ExtraOpts="" - CompilerFlags="" - if [ "$Phase" = "2" ]; then - CompilerFlags="CC=$llvmgccDir/bin/llvm-gcc CXX=$llvmgccDir/bin/llvm-g++" - fi if [ "$Flavor" = "Release-64" ]; then ExtraOpts="EXTRA_OPTIONS=-m64" fi cd $ObjDir echo "# Compiling llvm $Release-rc$RC $Flavor" - echo "# make -j $NumJobs VERBOSE=1 $ExtraOpts" - make -j $NumJobs VERBOSE=1 $ExtraOpts $CompilerFlags \ - > $LogDir/llvm.make.$Release-rc$RC-Phase$Phase-$Flavor.log 2>&1 + echo "# ${MAKE} -j $NumJobs VERBOSE=1 $ExtraOpts" + ${MAKE} -j $NumJobs VERBOSE=1 $ExtraOpts \ + 2>&1 | tee $LogDir/llvm.make-Phase$Phase-$Flavor.log echo "# Installing llvm $Release-rc$RC $Flavor" - echo "# make install" - make install \ - > $LogDir/llvm.install.$Release-rc$RC-Phase$Phase-$Flavor.log 2>&1 - cd - + echo "# ${MAKE} install" + ${MAKE} install \ + 2>&1 | tee $LogDir/llvm.install-Phase$Phase-$Flavor.log + cd $BuildDir } function test_llvmCore() { @@ -239,75 +251,27 @@ function test_llvmCore() { ObjDir="$3" cd $ObjDir - make check \ - > $LogDir/llvm.check.$Release-rc$RC-Phase$Phase-$Flavor.log 2>&1 - make -C tools/clang test \ - > $LogDir/clang.check.$Release-rc$RC-Phase$Phase-$Flavor.log 2>&1 - make unittests \ - > $LogDir/llvm.unittests.$Release-rc$RC-Phase$Phase-$Flavor.log 2>&1 - cd - + ${MAKE} -k check-all \ + 2>&1 | tee $LogDir/llvm.check-Phase$Phase-$Flavor.log + ${MAKE} -k unittests \ + 2>&1 | tee $LogDir/llvm.unittests-Phase$Phase-$Flavor.log + cd $BuildDir } -function configure_llvm_gcc() { - Phase="$1" - Flavor="$2" - ObjDir="$3" - InstallDir="$4" - llvmObjDir="$5" - - languages="c,c++" - if [ "$do_objc" = "yes" ]; then - languages="$languages,objc,obj-c++" - fi - if [ "$do_fortran" = "yes" ]; then - languages="$languages,fortran" - fi - if [ "$do_ada" = "yes" ]; then - languages="$languages,ada" - fi - - cd $ObjDir - echo "# Configuring llvm-gcc $Release-rc$RC $Flavor" - echo "# $llvmgcc42_srcdir/configure --prefix=$InstallDir \ - --program-prefix=llvm- --enable-llvm=$llvmObjDir \ - --enable-languages=$languages" - $llvmgcc42_srcdir/configure --prefix=$InstallDir \ - --program-prefix=llvm- --enable-llvm=$llvmObjDir \ - --enable-languages=$languages \ - > $LogDir/llvm-gcc.configure.$Release-rc$RC-Phase$Phase-$Flavor.log 2>&1 - cd - -} - -function build_llvm_gcc() { - Phase="$1" - Flavor="$2" - ObjDir="$3" - llvmgccDir="$4" - - CompilerFlags="" - if [ "$Phase" = "2" ]; then - CompilerFlags="CC=$llvmgccDir/bin/llvm-gcc CXX=$llvmgccDir/bin/llvm-g++" - fi - - cd $ObjDir - echo "# Compiling llvm-gcc $Release-rc$RC $Flavor" - echo "# make -j $NumJobs bootstrap LLVM_VERSION_INFO=$Release" - make -j $NumJobs bootstrap LLVM_VERSION_INFO=$Release $CompilerFlags \ - > $LogDir/llvm-gcc.make.$Release-rc$RC-Phase$Phase-$Flavor.log 2>&1 - - echo "# Installing llvm-gcc $Release-rc$RC $Flavor" - echo "# make install" - make install \ - > $LogDir/llvm-gcc.install.$Release-rc$RC-Phase$Phase-$Flavor.log 2>&1 - cd - -} +set -e # Exit if any command fails if [ "$do_checkout" = "yes" ]; then export_sources fi ( -Flavors="Debug Release Release+Asserts" +Flavors="Release" +if [ "$do_debug" = "yes" ]; then + Flavors="Debug $Flavors" +fi +if [ "$do_asserts" = "yes" ]; then + Flavors="$Flavors Release+Asserts" +fi if [ "$do_64bit" = "yes" ]; then Flavors="$Flavors Release-64" fi @@ -329,69 +293,71 @@ for Flavor in $Flavors ; do llvmCore_phase2_objdir=$BuildDir/Phase2/$Flavor/llvmCore-$Release-rc$RC.obj llvmCore_phase2_installdir=$BuildDir/Phase2/$Flavor/llvmCore-$Release-rc$RC.install + llvmCore_phase3_objdir=$BuildDir/Phase3/$Flavor/llvmCore-$Release-rc$RC.obj + llvmCore_phase3_installdir=$BuildDir/Phase3/$Flavor/llvmCore-$Release-rc$RC.install + rm -rf $llvmCore_phase1_objdir rm -rf $llvmCore_phase1_installdir rm -rf $llvmCore_phase2_objdir rm -rf $llvmCore_phase2_installdir + rm -rf $llvmCore_phase3_objdir + rm -rf $llvmCore_phase3_installdir mkdir -p $llvmCore_phase1_objdir mkdir -p $llvmCore_phase1_installdir mkdir -p $llvmCore_phase2_objdir mkdir -p $llvmCore_phase2_installdir - - llvmgcc42_phase1_objdir=$BuildDir/Phase1/$Flavor/llvmgcc42-$Release-rc$RC.obj - llvmgcc42_phase1_installdir=$BuildDir/Phase1/$Flavor/llvmgcc42-$Release-rc$RC.install - - llvmgcc42_phase2_objdir=$BuildDir/Phase2/$Flavor/llvmgcc42-$Release-rc$RC.obj - llvmgcc42_phase2_installdir=$BuildDir/Phase2/$Flavor/llvmgcc42-$Release-rc$RC.install - - rm -rf $llvmgcc42_phase1_objdir - rm -rf $llvmgcc42_phase1_installdir - rm -rf $llvmgcc42_phase2_objdir - rm -rf $llvmgcc42_phase2_installdir - - mkdir -p $llvmgcc42_phase1_objdir - mkdir -p $llvmgcc42_phase1_installdir - mkdir -p $llvmgcc42_phase2_objdir - mkdir -p $llvmgcc42_phase2_installdir + mkdir -p $llvmCore_phase3_objdir + mkdir -p $llvmCore_phase3_installdir ############################################################################ # Phase 1: Build llvmCore and llvmgcc42 echo "# Phase 1: Building llvmCore" configure_llvmCore 1 $Flavor \ - $llvmCore_phase1_objdir $llvmCore_phase1_installdir \ - $llvmgcc42_phase1_installdir + $llvmCore_phase1_objdir $llvmCore_phase1_installdir build_llvmCore 1 $Flavor \ $llvmCore_phase1_objdir - echo "# Phase 1: Building llvmgcc42" - configure_llvm_gcc 1 $Flavor \ - $llvmgcc42_phase1_objdir $llvmgcc42_phase1_installdir \ - $llvmCore_phase1_objdir - build_llvm_gcc 1 $Flavor \ - $llvmgcc42_phase1_objdir $llvmgcc42_phase1_installdir - ############################################################################ - # Phase 2: Build llvmCore with newly built llvmgcc42 from phase 1. + # Phase 2: Build llvmCore with newly built clang from phase 1. + c_compiler=$llvmCore_phase1_installdir/bin/clang + cxx_compiler=$llvmCore_phase1_installdir/bin/clang++ echo "# Phase 2: Building llvmCore" configure_llvmCore 2 $Flavor \ - $llvmCore_phase2_objdir $llvmCore_phase2_installdir \ - $llvmgcc42_phase1_installdir + $llvmCore_phase2_objdir $llvmCore_phase2_installdir build_llvmCore 2 $Flavor \ $llvmCore_phase2_objdir - echo "# Phase 2: Building llvmgcc42" - configure_llvm_gcc 2 $Flavor \ - $llvmgcc42_phase2_objdir $llvmgcc42_phase2_installdir \ - $llvmCore_phase2_objdir - build_llvm_gcc 2 $Flavor \ - $llvmgcc42_phase2_objdir $llvmgcc42_phase1_installdir + ############################################################################ + # Phase 3: Build llvmCore with newly built clang from phase 2. + c_compiler=$llvmCore_phase2_installdir/bin/clang + cxx_compiler=$llvmCore_phase2_installdir/bin/clang++ + echo "# Phase 3: Building llvmCore" + configure_llvmCore 3 $Flavor \ + $llvmCore_phase3_objdir $llvmCore_phase3_installdir + build_llvmCore 3 $Flavor \ + $llvmCore_phase3_objdir - echo "# Testing - built with llvmgcc42" - test_llvmCore 2 $Flavor $llvmCore_phase2_objdir + ############################################################################ + # Testing: Test phase 3 + echo "# Testing - built with clang" + test_llvmCore 3 $Flavor $llvmCore_phase3_objdir + + ############################################################################ + # Compare .o files between Phase2 and Phase3 and report which ones differ. + echo + echo "# Comparing Phase 2 and Phase 3 files" + for o in `find $llvmCore_phase2_objdir -name '*.o'` ; do + p3=`echo $o | sed -e 's,Phase2,Phase3,'` + if ! cmp --ignore-initial=16 $o $p3 > /dev/null 2>&1 ; then + echo "file `basename $o` differs between phase 2 and phase 3" + fi + done done ) 2>&1 | tee $LogDir/testing.$Release-rc$RC.log +set +e + # Woo hoo! echo "### Testing Finished ###" echo "### Logs: $LogDir" diff --git a/utils/unittest/CMakeLists.txt b/utils/unittest/CMakeLists.txt index 29218bb..73491f0 100644 --- a/utils/unittest/CMakeLists.txt +++ b/utils/unittest/CMakeLists.txt @@ -32,6 +32,7 @@ add_llvm_library(gtest googletest/gtest-death-test.cc googletest/gtest-filepath.cc googletest/gtest-port.cc + googletest/gtest-printers.cc googletest/gtest-test-part.cc googletest/gtest-typed-test.cc ) diff --git a/utils/unittest/googletest/README.LLVM b/utils/unittest/googletest/README.LLVM index d6e6f98..51340e9 100644 --- a/utils/unittest/googletest/README.LLVM +++ b/utils/unittest/googletest/README.LLVM @@ -1,14 +1,14 @@ LLVM notes ---------- -This directory contains Google Test 1.5.0, with all elements removed except for +This directory contains Google Test 1.6.0, with all elements removed except for the actual source code, to minimize the addition to the LLVM distribution. Cleaned up as follows: # Remove all the unnecessary files and directories $ rm -f aclocal* CMakeLists.txt configure* Makefile* CHANGES CONTRIBUTORS README -$ rm -rf build-aux codegear fused-src m4 make msvc samples scripts test xcode +$ rm -rf build-aux cmake codegear fused-src m4 make msvc samples scripts test xcode $ rm -f `find . -name \*\.pump` # Move all the source files to the current directory @@ -21,6 +21,8 @@ $ mv *.h include/gtest/internal/ # Update paths to the included files $ perl -pi -e 's|^#include "src/|#include "gtest/internal/|' *.cc +$ rm -f gtest-all.cc gtest_main.cc + $ mv COPYING LICENSE.TXT diff --git a/utils/unittest/googletest/gtest-death-test.cc b/utils/unittest/googletest/gtest-death-test.cc index e4199de..6589385 100644 --- a/utils/unittest/googletest/gtest-death-test.cc +++ b/utils/unittest/googletest/gtest-death-test.cc @@ -31,31 +31,31 @@ // // This file implements death tests. -#include <gtest/gtest-death-test.h> -#include <gtest/internal/gtest-port.h> +#include "gtest/gtest-death-test.h" +#include "gtest/internal/gtest-port.h" #if GTEST_HAS_DEATH_TEST -#if GTEST_OS_MAC -#include <crt_externs.h> -#endif // GTEST_OS_MAC +# if GTEST_OS_MAC +# include <crt_externs.h> +# endif // GTEST_OS_MAC -#include <errno.h> -#include <fcntl.h> -#include <limits.h> -#include <stdarg.h> +# include <errno.h> +# include <fcntl.h> +# include <limits.h> +# include <stdarg.h> -#if GTEST_OS_WINDOWS -#include <windows.h> -#else -#include <sys/mman.h> -#include <sys/wait.h> -#endif // GTEST_OS_WINDOWS +# if GTEST_OS_WINDOWS +# include <windows.h> +# else +# include <sys/mman.h> +# include <sys/wait.h> +# endif // GTEST_OS_WINDOWS #endif // GTEST_HAS_DEATH_TEST -#include <gtest/gtest-message.h> -#include <gtest/internal/gtest-string.h> +#include "gtest/gtest-message.h" +#include "gtest/internal/gtest-string.h" // Indicates that this translation unit is part of Google Test's // implementation. It must come before gtest-internal-inl.h is @@ -113,14 +113,18 @@ ExitedWithCode::ExitedWithCode(int exit_code) : exit_code_(exit_code) { // ExitedWithCode function-call operator. bool ExitedWithCode::operator()(int exit_status) const { -#if GTEST_OS_WINDOWS +# if GTEST_OS_WINDOWS + return exit_status == exit_code_; -#else + +# else + return WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == exit_code_; -#endif // GTEST_OS_WINDOWS + +# endif // GTEST_OS_WINDOWS } -#if !GTEST_OS_WINDOWS +# if !GTEST_OS_WINDOWS // KilledBySignal constructor. KilledBySignal::KilledBySignal(int signum) : signum_(signum) { } @@ -129,7 +133,7 @@ KilledBySignal::KilledBySignal(int signum) : signum_(signum) { bool KilledBySignal::operator()(int exit_status) const { return WIFSIGNALED(exit_status) && WTERMSIG(exit_status) == signum_; } -#endif // !GTEST_OS_WINDOWS +# endif // !GTEST_OS_WINDOWS namespace internal { @@ -139,20 +143,25 @@ namespace internal { // specified by wait(2). static String ExitSummary(int exit_code) { Message m; -#if GTEST_OS_WINDOWS + +# if GTEST_OS_WINDOWS + m << "Exited with exit status " << exit_code; -#else + +# else + if (WIFEXITED(exit_code)) { m << "Exited with exit status " << WEXITSTATUS(exit_code); } else if (WIFSIGNALED(exit_code)) { m << "Terminated by signal " << WTERMSIG(exit_code); } -#ifdef WCOREDUMP +# ifdef WCOREDUMP if (WCOREDUMP(exit_code)) { m << " (core dumped)"; } -#endif -#endif // GTEST_OS_WINDOWS +# endif +# endif // GTEST_OS_WINDOWS + return m.GetString(); } @@ -162,7 +171,7 @@ bool ExitedUnsuccessfully(int exit_status) { return !ExitedWithCode(0)(exit_status); } -#if !GTEST_OS_WINDOWS +# if !GTEST_OS_WINDOWS // Generates a textual failure message when a death test finds more than // one thread running, or cannot determine the number of threads, prior // to executing the given statement. It is the responsibility of the @@ -177,20 +186,24 @@ static String DeathTestThreadWarning(size_t thread_count) { msg << "detected " << thread_count << " threads."; return msg.GetString(); } -#endif // !GTEST_OS_WINDOWS +# endif // !GTEST_OS_WINDOWS // Flag characters for reporting a death test that did not die. static const char kDeathTestLived = 'L'; static const char kDeathTestReturned = 'R'; +static const char kDeathTestThrew = 'T'; static const char kDeathTestInternalError = 'I'; -// An enumeration describing all of the possible ways that a death test -// can conclude. DIED means that the process died while executing the -// test code; LIVED means that process lived beyond the end of the test -// code; and RETURNED means that the test statement attempted a "return," -// which is not allowed. IN_PROGRESS means the test has not yet -// concluded. -enum DeathTestOutcome { IN_PROGRESS, DIED, LIVED, RETURNED }; +// An enumeration describing all of the possible ways that a death test can +// conclude. DIED means that the process died while executing the test +// code; LIVED means that process lived beyond the end of the test code; +// RETURNED means that the test statement attempted to execute a return +// statement, which is not allowed; THREW means that the test statement +// returned control by throwing an exception. IN_PROGRESS means the test +// has not yet concluded. +// TODO(vladl@google.com): Unify names and possibly values for +// AbortReason, DeathTestOutcome, and flag characters above. +enum DeathTestOutcome { IN_PROGRESS, DIED, LIVED, RETURNED, THREW }; // Routine for aborting the program which is safe to call from an // exec-style death test child process, in which case the error @@ -212,13 +225,13 @@ void DeathTestAbort(const String& message) { } else { fprintf(stderr, "%s", message.c_str()); fflush(stderr); - abort(); + posix::Abort(); } } // A replacement for CHECK that calls DeathTestAbort if the assertion // fails. -#define GTEST_DEATH_TEST_CHECK_(expression) \ +# define GTEST_DEATH_TEST_CHECK_(expression) \ do { \ if (!::testing::internal::IsTrue(expression)) { \ DeathTestAbort(::testing::internal::String::Format( \ @@ -234,7 +247,7 @@ void DeathTestAbort(const String& message) { // evaluates the expression as long as it evaluates to -1 and sets // errno to EINTR. If the expression evaluates to -1 but errno is // something other than EINTR, DeathTestAbort is called. -#define GTEST_DEATH_TEST_CHECK_SYSCALL_(expression) \ +# define GTEST_DEATH_TEST_CHECK_SYSCALL_(expression) \ do { \ int gtest_retval; \ do { \ @@ -388,6 +401,9 @@ void DeathTestImpl::ReadAndInterpretStatusByte() { case kDeathTestReturned: set_outcome(RETURNED); break; + case kDeathTestThrew: + set_outcome(THREW); + break; case kDeathTestLived: set_outcome(LIVED); break; @@ -416,19 +432,46 @@ void DeathTestImpl::Abort(AbortReason reason) { // it finds any data in our pipe. So, here we write a single flag byte // to the pipe, then exit. const char status_ch = - reason == TEST_DID_NOT_DIE ? kDeathTestLived : kDeathTestReturned; + reason == TEST_DID_NOT_DIE ? kDeathTestLived : + reason == TEST_THREW_EXCEPTION ? kDeathTestThrew : kDeathTestReturned; + GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Write(write_fd(), &status_ch, 1)); - GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Close(write_fd())); + // We are leaking the descriptor here because on some platforms (i.e., + // when built as Windows DLL), destructors of global objects will still + // run after calling _exit(). On such systems, write_fd_ will be + // indirectly closed from the destructor of UnitTestImpl, causing double + // close if it is also closed here. On debug configurations, double close + // may assert. As there are no in-process buffers to flush here, we are + // relying on the OS to close the descriptor after the process terminates + // when the destructors are not run. _exit(1); // Exits w/o any normal exit hooks (we were supposed to crash) } +// Returns an indented copy of stderr output for a death test. +// This makes distinguishing death test output lines from regular log lines +// much easier. +static ::std::string FormatDeathTestOutput(const ::std::string& output) { + ::std::string ret; + for (size_t at = 0; ; ) { + const size_t line_end = output.find('\n', at); + ret += "[ DEATH ] "; + if (line_end == ::std::string::npos) { + ret += output.substr(at); + break; + } + ret += output.substr(at, line_end + 1 - at); + at = line_end + 1; + } + return ret; +} + // Assesses the success or failure of a death test, using both private // members which have previously been set, and one argument: // // Private data members: // outcome: An enumeration describing how the death test -// concluded: DIED, LIVED, or RETURNED. The death test fails -// in the latter two cases. +// concluded: DIED, LIVED, THREW, or RETURNED. The death test +// fails in the latter three cases. // status: The exit status of the child process. On *nix, it is in the // in the format specified by wait(2). On Windows, this is the // value supplied to the ExitProcess() API or a numeric code @@ -457,11 +500,15 @@ bool DeathTestImpl::Passed(bool status_ok) { switch (outcome()) { case LIVED: buffer << " Result: failed to die.\n" - << " Error msg: " << error_message; + << " Error msg:\n" << FormatDeathTestOutput(error_message); + break; + case THREW: + buffer << " Result: threw an exception.\n" + << " Error msg:\n" << FormatDeathTestOutput(error_message); break; case RETURNED: buffer << " Result: illegal return in test statement.\n" - << " Error msg: " << error_message; + << " Error msg:\n" << FormatDeathTestOutput(error_message); break; case DIED: if (status_ok) { @@ -471,11 +518,12 @@ bool DeathTestImpl::Passed(bool status_ok) { } else { buffer << " Result: died but not with expected error.\n" << " Expected: " << regex()->pattern() << "\n" - << "Actual msg: " << error_message; + << "Actual msg:\n" << FormatDeathTestOutput(error_message); } } else { buffer << " Result: died but not with expected exit code:\n" - << " " << ExitSummary(status()) << "\n"; + << " " << ExitSummary(status()) << "\n" + << "Actual msg:\n" << FormatDeathTestOutput(error_message); } break; case IN_PROGRESS: @@ -488,7 +536,7 @@ bool DeathTestImpl::Passed(bool status_ok) { return success; } -#if GTEST_OS_WINDOWS +# if GTEST_OS_WINDOWS // WindowsDeathTest implements death tests on Windows. Due to the // specifics of starting new processes on Windows, death tests there are // always threadsafe, and Google Test considers the @@ -519,11 +567,11 @@ bool DeathTestImpl::Passed(bool status_ok) { // class WindowsDeathTest : public DeathTestImpl { public: - WindowsDeathTest(const char* statement, - const RE* regex, + WindowsDeathTest(const char* a_statement, + const RE* a_regex, const char* file, int line) - : DeathTestImpl(statement, regex), file_(file), line_(line) {} + : DeathTestImpl(a_statement, a_regex), file_(file), line_(line) {} // All of these virtual functions are inherited from DeathTest. virtual int Wait(); @@ -580,12 +628,12 @@ int WindowsDeathTest::Wait() { GTEST_DEATH_TEST_CHECK_( WAIT_OBJECT_0 == ::WaitForSingleObject(child_handle_.Get(), INFINITE)); - DWORD status; - GTEST_DEATH_TEST_CHECK_(::GetExitCodeProcess(child_handle_.Get(), &status) - != FALSE); + DWORD status_code; + GTEST_DEATH_TEST_CHECK_( + ::GetExitCodeProcess(child_handle_.Get(), &status_code) != FALSE); child_handle_.Reset(); - set_status(static_cast<int>(status)); - return this->status(); + set_status(static_cast<int>(status_code)); + return status(); } // The AssumeRole process for a Windows death test. It creates a child @@ -684,7 +732,7 @@ DeathTest::TestRole WindowsDeathTest::AssumeRole() { set_spawned(true); return OVERSEE_TEST; } -#else // We are not on Windows. +# else // We are not on Windows. // ForkingDeathTest provides implementations for most of the abstract // methods of the DeathTest interface. Only the AssumeRole method is @@ -832,19 +880,19 @@ struct ExecDeathTestArgs { int close_fd; // File descriptor to close; the read end of a pipe }; -#if GTEST_OS_MAC +# if GTEST_OS_MAC inline char** GetEnviron() { // When Google Test is built as a framework on MacOS X, the environ variable // is unavailable. Apple's documentation (man environ) recommends using // _NSGetEnviron() instead. return *_NSGetEnviron(); } -#else +# else // Some POSIX platforms expect you to declare environ. extern "C" makes // it reside in the global namespace. extern "C" char** environ; inline char** GetEnviron() { return environ; } -#endif // GTEST_OS_MAC +# endif // GTEST_OS_MAC // The main function for a threadsafe-style death test child process. // This function is called in a clone()-ed process and thus must avoid @@ -884,6 +932,11 @@ static int ExecDeathTestChildMain(void* child_arg) { // This could be accomplished more elegantly by a single recursive // function, but we want to guard against the unlikely possibility of // a smart compiler optimizing the recursion away. +// +// GTEST_NO_INLINE_ is required to prevent GCC 4.6 from inlining +// StackLowerThanAddress into StackGrowsDown, which then doesn't give +// correct answer. +bool StackLowerThanAddress(const void* ptr) GTEST_NO_INLINE_; bool StackLowerThanAddress(const void* ptr) { int dummy; return &dummy < ptr; @@ -901,7 +954,7 @@ static pid_t ExecDeathTestFork(char* const* argv, int close_fd) { ExecDeathTestArgs args = { argv, close_fd }; pid_t child_pid = -1; -#if GTEST_HAS_CLONE +# if GTEST_HAS_CLONE const bool use_fork = GTEST_FLAG(death_test_use_fork); if (!use_fork) { @@ -918,9 +971,9 @@ static pid_t ExecDeathTestFork(char* const* argv, int close_fd) { GTEST_DEATH_TEST_CHECK_(munmap(stack, stack_size) != -1); } -#else +# else const bool use_fork = true; -#endif // GTEST_HAS_CLONE +# endif // GTEST_HAS_CLONE if (use_fork && (child_pid = fork()) == 0) { ExecDeathTestChildMain(&args); @@ -981,7 +1034,7 @@ DeathTest::TestRole ExecDeathTest::AssumeRole() { return OVERSEE_TEST; } -#endif // !GTEST_OS_WINDOWS +# endif // !GTEST_OS_WINDOWS // Creates a concrete DeathTest-derived class that depends on the // --gtest_death_test_style flag, and sets the pointer pointed to @@ -1012,18 +1065,23 @@ bool DefaultDeathTestFactory::Create(const char* statement, const RE* regex, } } -#if GTEST_OS_WINDOWS +# if GTEST_OS_WINDOWS + if (GTEST_FLAG(death_test_style) == "threadsafe" || GTEST_FLAG(death_test_style) == "fast") { *test = new WindowsDeathTest(statement, regex, file, line); } -#else + +# else + if (GTEST_FLAG(death_test_style) == "threadsafe") { *test = new ExecDeathTest(statement, regex, file, line); } else if (GTEST_FLAG(death_test_style) == "fast") { *test = new NoExecDeathTest(statement, regex); } -#endif // GTEST_OS_WINDOWS + +# endif // GTEST_OS_WINDOWS + else { // NOLINT - this is more readable than unbalanced brackets inside #if. DeathTest::set_last_death_test_message(String::Format( "Unknown death test style \"%s\" encountered", @@ -1054,7 +1112,7 @@ static void SplitString(const ::std::string& str, char delimiter, dest->swap(parsed); } -#if GTEST_OS_WINDOWS +# if GTEST_OS_WINDOWS // Recreates the pipe and event handles from the provided parameters, // signals the event, and returns a file descriptor wrapped around the pipe // handle. This function is called in the child process only. @@ -1118,7 +1176,7 @@ int GetStatusFileDescriptor(unsigned int parent_process_id, return write_fd; } -#endif // GTEST_OS_WINDOWS +# endif // GTEST_OS_WINDOWS // Returns a newly created InternalRunDeathTestFlag object with fields // initialized from the GTEST_FLAG(internal_run_death_test) flag if @@ -1134,7 +1192,8 @@ InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() { SplitString(GTEST_FLAG(internal_run_death_test).c_str(), '|', &fields); int write_fd = -1; -#if GTEST_OS_WINDOWS +# if GTEST_OS_WINDOWS + unsigned int parent_process_id = 0; size_t write_handle_as_size_t = 0; size_t event_handle_as_size_t = 0; @@ -1152,7 +1211,8 @@ InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() { write_fd = GetStatusFileDescriptor(parent_process_id, write_handle_as_size_t, event_handle_as_size_t); -#else +# else + if (fields.size() != 4 || !ParseNaturalNumber(fields[1], &line) || !ParseNaturalNumber(fields[2], &index) @@ -1161,7 +1221,9 @@ InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() { "Bad --gtest_internal_run_death_test flag: %s", GTEST_FLAG(internal_run_death_test).c_str())); } -#endif // GTEST_OS_WINDOWS + +# endif // GTEST_OS_WINDOWS + return new InternalRunDeathTestFlag(fields[0], line, index, write_fd); } diff --git a/utils/unittest/googletest/gtest-filepath.cc b/utils/unittest/googletest/gtest-filepath.cc index 8d1d67e..bc61009 100644 --- a/utils/unittest/googletest/gtest-filepath.cc +++ b/utils/unittest/googletest/gtest-filepath.cc @@ -29,35 +29,35 @@ // // Authors: keith.ray@gmail.com (Keith Ray) -#include <gtest/internal/gtest-filepath.h> -#include <gtest/internal/gtest-port.h> +#include "gtest/internal/gtest-filepath.h" +#include "gtest/internal/gtest-port.h" #include <stdlib.h> #if GTEST_OS_WINDOWS_MOBILE -#include <windows.h> +# include <windows.h> #elif GTEST_OS_WINDOWS -#include <direct.h> -#include <io.h> -#elif GTEST_OS_SYMBIAN -// Symbian OpenC has PATH_MAX in sys/syslimits.h -#include <sys/syslimits.h> +# include <direct.h> +# include <io.h> +#elif GTEST_OS_SYMBIAN || GTEST_OS_NACL +// Symbian OpenC and NaCl have PATH_MAX in sys/syslimits.h +# include <sys/syslimits.h> #else -#include <limits.h> -#include <climits> // Some Linux distributions define PATH_MAX here. +# include <limits.h> +# include <climits> // Some Linux distributions define PATH_MAX here. #endif // GTEST_OS_WINDOWS_MOBILE #if GTEST_OS_WINDOWS -#define GTEST_PATH_MAX_ _MAX_PATH +# define GTEST_PATH_MAX_ _MAX_PATH #elif defined(PATH_MAX) -#define GTEST_PATH_MAX_ PATH_MAX +# define GTEST_PATH_MAX_ PATH_MAX #elif defined(_XOPEN_PATH_MAX) -#define GTEST_PATH_MAX_ _XOPEN_PATH_MAX +# define GTEST_PATH_MAX_ _XOPEN_PATH_MAX #else -#define GTEST_PATH_MAX_ _POSIX_PATH_MAX +# define GTEST_PATH_MAX_ _POSIX_PATH_MAX #endif // GTEST_OS_WINDOWS -#include <gtest/internal/gtest-string.h> +#include "gtest/internal/gtest-string.h" namespace testing { namespace internal { @@ -71,16 +71,16 @@ const char kPathSeparator = '\\'; const char kAlternatePathSeparator = '/'; const char kPathSeparatorString[] = "\\"; const char kAlternatePathSeparatorString[] = "/"; -#if GTEST_OS_WINDOWS_MOBILE +# if GTEST_OS_WINDOWS_MOBILE // Windows CE doesn't have a current directory. You should not use // the current directory in tests on Windows CE, but this at least // provides a reasonable fallback. const char kCurrentDirectoryString[] = "\\"; // Windows CE doesn't define INVALID_FILE_ATTRIBUTES const DWORD kInvalidFileAttributes = 0xffffffff; -#else +# else const char kCurrentDirectoryString[] = ".\\"; -#endif // GTEST_OS_WINDOWS_MOBILE +# endif // GTEST_OS_WINDOWS_MOBILE #else const char kPathSeparator = '/'; const char kPathSeparatorString[] = "/"; diff --git a/utils/unittest/googletest/gtest-port.cc b/utils/unittest/googletest/gtest-port.cc index 5609599..07e5bb3 100644 --- a/utils/unittest/googletest/gtest-port.cc +++ b/utils/unittest/googletest/gtest-port.cc @@ -29,30 +29,32 @@ // // Author: wan@google.com (Zhanyong Wan) -#include <gtest/internal/gtest-port.h> +#include "gtest/internal/gtest-port.h" #include <limits.h> #include <stdlib.h> #include <stdio.h> +#include <string.h> #if GTEST_OS_WINDOWS_MOBILE -#include <windows.h> // For TerminateProcess() +# include <windows.h> // For TerminateProcess() #elif GTEST_OS_WINDOWS -#include <io.h> -#include <sys/stat.h> +# include <io.h> +# include <sys/stat.h> #else -#include <unistd.h> +# include <unistd.h> #endif // GTEST_OS_WINDOWS_MOBILE #if GTEST_OS_MAC -#include <mach/mach_init.h> -#include <mach/task.h> -#include <mach/vm_map.h> +# include <mach/mach_init.h> +# include <mach/task.h> +# include <mach/vm_map.h> #endif // GTEST_OS_MAC -#include <gtest/gtest-spi.h> -#include <gtest/gtest-message.h> -#include <gtest/internal/gtest-string.h> +#include "gtest/gtest-spi.h" +#include "gtest/gtest-message.h" +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-string.h" // Indicates that this translation unit is part of Google Test's // implementation. It must come before gtest-internal-inl.h is @@ -180,20 +182,20 @@ bool IsInSet(char ch, const char* str) { // Returns true iff ch belongs to the given classification. Unlike // similar functions in <ctype.h>, these aren't affected by the // current locale. -bool IsDigit(char ch) { return '0' <= ch && ch <= '9'; } -bool IsPunct(char ch) { +bool IsAsciiDigit(char ch) { return '0' <= ch && ch <= '9'; } +bool IsAsciiPunct(char ch) { return IsInSet(ch, "^-!\"#$%&'()*+,./:;<=>?@[\\]_`{|}~"); } bool IsRepeat(char ch) { return IsInSet(ch, "?*+"); } -bool IsWhiteSpace(char ch) { return IsInSet(ch, " \f\n\r\t\v"); } -bool IsWordChar(char ch) { +bool IsAsciiWhiteSpace(char ch) { return IsInSet(ch, " \f\n\r\t\v"); } +bool IsAsciiWordChar(char ch) { return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || ('0' <= ch && ch <= '9') || ch == '_'; } // Returns true iff "\\c" is a supported escape sequence. bool IsValidEscape(char c) { - return (IsPunct(c) || IsInSet(c, "dDfnrsStvwW")); + return (IsAsciiPunct(c) || IsInSet(c, "dDfnrsStvwW")); } // Returns true iff the given atom (specified by escaped and pattern) @@ -201,19 +203,19 @@ bool IsValidEscape(char c) { bool AtomMatchesChar(bool escaped, char pattern_char, char ch) { if (escaped) { // "\\p" where p is pattern_char. switch (pattern_char) { - case 'd': return IsDigit(ch); - case 'D': return !IsDigit(ch); + case 'd': return IsAsciiDigit(ch); + case 'D': return !IsAsciiDigit(ch); case 'f': return ch == '\f'; case 'n': return ch == '\n'; case 'r': return ch == '\r'; - case 's': return IsWhiteSpace(ch); - case 'S': return !IsWhiteSpace(ch); + case 's': return IsAsciiWhiteSpace(ch); + case 'S': return !IsAsciiWhiteSpace(ch); case 't': return ch == '\t'; case 'v': return ch == '\v'; - case 'w': return IsWordChar(ch); - case 'W': return !IsWordChar(ch); + case 'w': return IsAsciiWordChar(ch); + case 'W': return !IsAsciiWordChar(ch); } - return IsPunct(pattern_char) && pattern_char == ch; + return IsAsciiPunct(pattern_char) && pattern_char == ch; } return (pattern_char == '.' && ch != '\n') || pattern_char == ch; @@ -422,6 +424,38 @@ void RE::Init(const char* regex) { #endif // GTEST_USES_POSIX_RE +const char kUnknownFile[] = "unknown file"; + +// Formats a source file path and a line number as they would appear +// in an error message from the compiler used to compile this code. +GTEST_API_ ::std::string FormatFileLocation(const char* file, int line) { + const char* const file_name = file == NULL ? kUnknownFile : file; + + if (line < 0) { + return String::Format("%s:", file_name).c_str(); + } +#ifdef _MSC_VER + return String::Format("%s(%d):", file_name, line).c_str(); +#else + return String::Format("%s:%d:", file_name, line).c_str(); +#endif // _MSC_VER +} + +// Formats a file location for compiler-independent XML output. +// Although this function is not platform dependent, we put it next to +// FormatFileLocation in order to contrast the two functions. +// Note that FormatCompilerIndependentFileLocation() does NOT append colon +// to the file location it produces, unlike FormatFileLocation(). +GTEST_API_ ::std::string FormatCompilerIndependentFileLocation( + const char* file, int line) { + const char* const file_name = file == NULL ? kUnknownFile : file; + + if (line < 0) + return file_name; + else + return String::Format("%s:%d", file_name, line).c_str(); +} + GTestLog::GTestLog(GTestLogSeverity severity, const char* file, int line) : severity_(severity) { @@ -444,18 +478,19 @@ GTestLog::~GTestLog() { // Disable Microsoft deprecation warnings for POSIX functions called from // this class (creat, dup, dup2, and close) #ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable: 4996) +# pragma warning(push) +# pragma warning(disable: 4996) #endif // _MSC_VER -#if GTEST_HAS_STREAM_REDIRECTION_ +#if GTEST_HAS_STREAM_REDIRECTION // Object that captures an output stream (stdout/stderr). class CapturedStream { public: // The ctor redirects the stream to a temporary file. CapturedStream(int fd) : fd_(fd), uncaptured_fd_(dup(fd)) { -#if GTEST_OS_WINDOWS + +# if GTEST_OS_WINDOWS char temp_dir_path[MAX_PATH + 1] = { '\0' }; // NOLINT char temp_file_path[MAX_PATH + 1] = { '\0' }; // NOLINT @@ -470,14 +505,14 @@ class CapturedStream { GTEST_CHECK_(captured_fd != -1) << "Unable to open temporary file " << temp_file_path; filename_ = temp_file_path; -#else +# else // There's no guarantee that a test has write access to the // current directory, so we create the temporary file in the /tmp // directory instead. char name_template[] = "/tmp/captured_stream.XXXXXX"; const int captured_fd = mkstemp(name_template); filename_ = name_template; -#endif // GTEST_OS_WINDOWS +# endif // GTEST_OS_WINDOWS fflush(NULL); dup2(captured_fd, fd_); close(captured_fd); @@ -546,9 +581,9 @@ String CapturedStream::ReadEntireFile(FILE* file) { return content; } -#ifdef _MSC_VER -#pragma warning(pop) -#endif // _MSC_VER +# ifdef _MSC_VER +# pragma warning(pop) +# endif // _MSC_VER static CapturedStream* g_captured_stderr = NULL; static CapturedStream* g_captured_stdout = NULL; @@ -588,7 +623,7 @@ String GetCapturedStdout() { return GetCapturedStream(&g_captured_stdout); } // Stops capturing stderr and returns the captured string. String GetCapturedStderr() { return GetCapturedStream(&g_captured_stderr); } -#endif // GTEST_HAS_STREAM_REDIRECTION_ +#endif // GTEST_HAS_STREAM_REDIRECTION #if GTEST_HAS_DEATH_TEST @@ -618,7 +653,7 @@ static String FlagToEnvVar(const char* flag) { Message env_var; for (size_t i = 0; i != full_flag.length(); i++) { - env_var << static_cast<char>(toupper(full_flag.c_str()[i])); + env_var << ToUpper(full_flag.c_str()[i]); } return env_var.GetString(); diff --git a/utils/unittest/googletest/gtest-printers.cc b/utils/unittest/googletest/gtest-printers.cc new file mode 100644 index 0000000..ed63c7b --- /dev/null +++ b/utils/unittest/googletest/gtest-printers.cc @@ -0,0 +1,356 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Test - The Google C++ Testing Framework +// +// This file implements a universal value printer that can print a +// value of any type T: +// +// void ::testing::internal::UniversalPrinter<T>::Print(value, ostream_ptr); +// +// It uses the << operator when possible, and prints the bytes in the +// object otherwise. A user can override its behavior for a class +// type Foo by defining either operator<<(::std::ostream&, const Foo&) +// or void PrintTo(const Foo&, ::std::ostream*) in the namespace that +// defines Foo. + +#include "gtest/gtest-printers.h" +#include <ctype.h> +#include <stdio.h> +#include <ostream> // NOLINT +#include <string> +#include "gtest/internal/gtest-port.h" + +namespace testing { + +namespace { + +using ::std::ostream; + +#if GTEST_OS_WINDOWS_MOBILE // Windows CE does not define _snprintf_s. +# define snprintf _snprintf +#elif _MSC_VER >= 1400 // VC 8.0 and later deprecate snprintf and _snprintf. +# define snprintf _snprintf_s +#elif _MSC_VER +# define snprintf _snprintf +#endif // GTEST_OS_WINDOWS_MOBILE + +// Prints a segment of bytes in the given object. +void PrintByteSegmentInObjectTo(const unsigned char* obj_bytes, size_t start, + size_t count, ostream* os) { + char text[5] = ""; + for (size_t i = 0; i != count; i++) { + const size_t j = start + i; + if (i != 0) { + // Organizes the bytes into groups of 2 for easy parsing by + // human. + if ((j % 2) == 0) + *os << ' '; + else + *os << '-'; + } + snprintf(text, sizeof(text), "%02X", obj_bytes[j]); + *os << text; + } +} + +// Prints the bytes in the given value to the given ostream. +void PrintBytesInObjectToImpl(const unsigned char* obj_bytes, size_t count, + ostream* os) { + // Tells the user how big the object is. + *os << count << "-byte object <"; + + const size_t kThreshold = 132; + const size_t kChunkSize = 64; + // If the object size is bigger than kThreshold, we'll have to omit + // some details by printing only the first and the last kChunkSize + // bytes. + // TODO(wan): let the user control the threshold using a flag. + if (count < kThreshold) { + PrintByteSegmentInObjectTo(obj_bytes, 0, count, os); + } else { + PrintByteSegmentInObjectTo(obj_bytes, 0, kChunkSize, os); + *os << " ... "; + // Rounds up to 2-byte boundary. + const size_t resume_pos = (count - kChunkSize + 1)/2*2; + PrintByteSegmentInObjectTo(obj_bytes, resume_pos, count - resume_pos, os); + } + *os << ">"; +} + +} // namespace + +namespace internal2 { + +// Delegates to PrintBytesInObjectToImpl() to print the bytes in the +// given object. The delegation simplifies the implementation, which +// uses the << operator and thus is easier done outside of the +// ::testing::internal namespace, which contains a << operator that +// sometimes conflicts with the one in STL. +void PrintBytesInObjectTo(const unsigned char* obj_bytes, size_t count, + ostream* os) { + PrintBytesInObjectToImpl(obj_bytes, count, os); +} + +} // namespace internal2 + +namespace internal { + +// Depending on the value of a char (or wchar_t), we print it in one +// of three formats: +// - as is if it's a printable ASCII (e.g. 'a', '2', ' '), +// - as a hexidecimal escape sequence (e.g. '\x7F'), or +// - as a special escape sequence (e.g. '\r', '\n'). +enum CharFormat { + kAsIs, + kHexEscape, + kSpecialEscape +}; + +// Returns true if c is a printable ASCII character. We test the +// value of c directly instead of calling isprint(), which is buggy on +// Windows Mobile. +inline bool IsPrintableAscii(wchar_t c) { + return 0x20 <= c && c <= 0x7E; +} + +// Prints a wide or narrow char c as a character literal without the +// quotes, escaping it when necessary; returns how c was formatted. +// The template argument UnsignedChar is the unsigned version of Char, +// which is the type of c. +template <typename UnsignedChar, typename Char> +static CharFormat PrintAsCharLiteralTo(Char c, ostream* os) { + switch (static_cast<wchar_t>(c)) { + case L'\0': + *os << "\\0"; + break; + case L'\'': + *os << "\\'"; + break; + case L'\\': + *os << "\\\\"; + break; + case L'\a': + *os << "\\a"; + break; + case L'\b': + *os << "\\b"; + break; + case L'\f': + *os << "\\f"; + break; + case L'\n': + *os << "\\n"; + break; + case L'\r': + *os << "\\r"; + break; + case L'\t': + *os << "\\t"; + break; + case L'\v': + *os << "\\v"; + break; + default: + if (IsPrintableAscii(c)) { + *os << static_cast<char>(c); + return kAsIs; + } else { + *os << String::Format("\\x%X", static_cast<UnsignedChar>(c)); + return kHexEscape; + } + } + return kSpecialEscape; +} + +// Prints a char c as if it's part of a string literal, escaping it when +// necessary; returns how c was formatted. +static CharFormat PrintAsWideStringLiteralTo(wchar_t c, ostream* os) { + switch (c) { + case L'\'': + *os << "'"; + return kAsIs; + case L'"': + *os << "\\\""; + return kSpecialEscape; + default: + return PrintAsCharLiteralTo<wchar_t>(c, os); + } +} + +// Prints a char c as if it's part of a string literal, escaping it when +// necessary; returns how c was formatted. +static CharFormat PrintAsNarrowStringLiteralTo(char c, ostream* os) { + return PrintAsWideStringLiteralTo(static_cast<unsigned char>(c), os); +} + +// Prints a wide or narrow character c and its code. '\0' is printed +// as "'\\0'", other unprintable characters are also properly escaped +// using the standard C++ escape sequence. The template argument +// UnsignedChar is the unsigned version of Char, which is the type of c. +template <typename UnsignedChar, typename Char> +void PrintCharAndCodeTo(Char c, ostream* os) { + // First, print c as a literal in the most readable form we can find. + *os << ((sizeof(c) > 1) ? "L'" : "'"); + const CharFormat format = PrintAsCharLiteralTo<UnsignedChar>(c, os); + *os << "'"; + + // To aid user debugging, we also print c's code in decimal, unless + // it's 0 (in which case c was printed as '\\0', making the code + // obvious). + if (c == 0) + return; + *os << " (" << String::Format("%d", c).c_str(); + + // For more convenience, we print c's code again in hexidecimal, + // unless c was already printed in the form '\x##' or the code is in + // [1, 9]. + if (format == kHexEscape || (1 <= c && c <= 9)) { + // Do nothing. + } else { + *os << String::Format(", 0x%X", + static_cast<UnsignedChar>(c)).c_str(); + } + *os << ")"; +} + +void PrintTo(unsigned char c, ::std::ostream* os) { + PrintCharAndCodeTo<unsigned char>(c, os); +} +void PrintTo(signed char c, ::std::ostream* os) { + PrintCharAndCodeTo<unsigned char>(c, os); +} + +// Prints a wchar_t as a symbol if it is printable or as its internal +// code otherwise and also as its code. L'\0' is printed as "L'\\0'". +void PrintTo(wchar_t wc, ostream* os) { + PrintCharAndCodeTo<wchar_t>(wc, os); +} + +// Prints the given array of characters to the ostream. +// The array starts at *begin, the length is len, it may include '\0' characters +// and may not be null-terminated. +static void PrintCharsAsStringTo(const char* begin, size_t len, ostream* os) { + *os << "\""; + bool is_previous_hex = false; + for (size_t index = 0; index < len; ++index) { + const char cur = begin[index]; + if (is_previous_hex && IsXDigit(cur)) { + // Previous character is of '\x..' form and this character can be + // interpreted as another hexadecimal digit in its number. Break string to + // disambiguate. + *os << "\" \""; + } + is_previous_hex = PrintAsNarrowStringLiteralTo(cur, os) == kHexEscape; + } + *os << "\""; +} + +// Prints a (const) char array of 'len' elements, starting at address 'begin'. +void UniversalPrintArray(const char* begin, size_t len, ostream* os) { + PrintCharsAsStringTo(begin, len, os); +} + +// Prints the given array of wide characters to the ostream. +// The array starts at *begin, the length is len, it may include L'\0' +// characters and may not be null-terminated. +static void PrintWideCharsAsStringTo(const wchar_t* begin, size_t len, + ostream* os) { + *os << "L\""; + bool is_previous_hex = false; + for (size_t index = 0; index < len; ++index) { + const wchar_t cur = begin[index]; + if (is_previous_hex && isascii(cur) && IsXDigit(static_cast<char>(cur))) { + // Previous character is of '\x..' form and this character can be + // interpreted as another hexadecimal digit in its number. Break string to + // disambiguate. + *os << "\" L\""; + } + is_previous_hex = PrintAsWideStringLiteralTo(cur, os) == kHexEscape; + } + *os << "\""; +} + +// Prints the given C string to the ostream. +void PrintTo(const char* s, ostream* os) { + if (s == NULL) { + *os << "NULL"; + } else { + *os << ImplicitCast_<const void*>(s) << " pointing to "; + PrintCharsAsStringTo(s, strlen(s), os); + } +} + +// MSVC compiler can be configured to define whar_t as a typedef +// of unsigned short. Defining an overload for const wchar_t* in that case +// would cause pointers to unsigned shorts be printed as wide strings, +// possibly accessing more memory than intended and causing invalid +// memory accesses. MSVC defines _NATIVE_WCHAR_T_DEFINED symbol when +// wchar_t is implemented as a native type. +#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) +// Prints the given wide C string to the ostream. +void PrintTo(const wchar_t* s, ostream* os) { + if (s == NULL) { + *os << "NULL"; + } else { + *os << ImplicitCast_<const void*>(s) << " pointing to "; + PrintWideCharsAsStringTo(s, wcslen(s), os); + } +} +#endif // wchar_t is native + +// Prints a ::string object. +#if GTEST_HAS_GLOBAL_STRING +void PrintStringTo(const ::string& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} +#endif // GTEST_HAS_GLOBAL_STRING + +void PrintStringTo(const ::std::string& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} + +// Prints a ::wstring object. +#if GTEST_HAS_GLOBAL_WSTRING +void PrintWideStringTo(const ::wstring& s, ostream* os) { + PrintWideCharsAsStringTo(s.data(), s.size(), os); +} +#endif // GTEST_HAS_GLOBAL_WSTRING + +#if GTEST_HAS_STD_WSTRING +void PrintWideStringTo(const ::std::wstring& s, ostream* os) { + PrintWideCharsAsStringTo(s.data(), s.size(), os); +} +#endif // GTEST_HAS_STD_WSTRING + +} // namespace internal + +} // namespace testing diff --git a/utils/unittest/googletest/gtest-test-part.cc b/utils/unittest/googletest/gtest-test-part.cc index 8249afe..1612780 100644 --- a/utils/unittest/googletest/gtest-test-part.cc +++ b/utils/unittest/googletest/gtest-test-part.cc @@ -31,7 +31,7 @@ // // The Google C++ Testing Framework (Google Test) -#include <gtest/gtest-test-part.h> +#include "gtest/gtest-test-part.h" // Indicates that this translation unit is part of Google Test's // implementation. It must come before gtest-internal-inl.h is diff --git a/utils/unittest/googletest/gtest-typed-test.cc b/utils/unittest/googletest/gtest-typed-test.cc index 3cc4b5d..a5cc88f 100644 --- a/utils/unittest/googletest/gtest-typed-test.cc +++ b/utils/unittest/googletest/gtest-typed-test.cc @@ -29,8 +29,8 @@ // // Author: wan@google.com (Zhanyong Wan) -#include <gtest/gtest-typed-test.h> -#include <gtest/gtest.h> +#include "gtest/gtest-typed-test.h" +#include "gtest/gtest.h" namespace testing { namespace internal { @@ -40,7 +40,7 @@ namespace internal { // Skips to the first non-space char in str. Returns an empty string if str // contains only whitespace characters. static const char* SkipSpaces(const char* str) { - while (isspace(*str)) + while (IsSpace(*str)) str++; return str; } diff --git a/utils/unittest/googletest/gtest.cc b/utils/unittest/googletest/gtest.cc index 9aa5441..7624497 100644 --- a/utils/unittest/googletest/gtest.cc +++ b/utils/unittest/googletest/gtest.cc @@ -31,8 +31,8 @@ // // The Google C++ Testing Framework (Google Test) -#include <gtest/gtest.h> -#include <gtest/gtest-spi.h> +#include "gtest/gtest.h" +#include "gtest/gtest-spi.h" #include <ctype.h> #include <math.h> @@ -43,7 +43,7 @@ #include <wctype.h> #include <algorithm> -#include <ostream> +#include <ostream> // NOLINT #include <sstream> #include <vector> @@ -51,72 +51,76 @@ // TODO(kenton@google.com): Use autoconf to detect availability of // gettimeofday(). -#define GTEST_HAS_GETTIMEOFDAY_ 1 +# define GTEST_HAS_GETTIMEOFDAY_ 1 -#include <fcntl.h> -#include <limits.h> -#include <sched.h> +# include <fcntl.h> // NOLINT +# include <limits.h> // NOLINT +# include <sched.h> // NOLINT // Declares vsnprintf(). This header is not available on Windows. -#include <strings.h> -#include <sys/mman.h> -#include <sys/time.h> -#include <unistd.h> -#include <string> -#include <vector> +# include <strings.h> // NOLINT +# include <sys/mman.h> // NOLINT +# include <sys/time.h> // NOLINT +# include <unistd.h> // NOLINT +# include <string> #elif GTEST_OS_SYMBIAN -#define GTEST_HAS_GETTIMEOFDAY_ 1 -#include <sys/time.h> // NOLINT +# define GTEST_HAS_GETTIMEOFDAY_ 1 +# include <sys/time.h> // NOLINT #elif GTEST_OS_ZOS -#define GTEST_HAS_GETTIMEOFDAY_ 1 -#include <sys/time.h> // NOLINT +# define GTEST_HAS_GETTIMEOFDAY_ 1 +# include <sys/time.h> // NOLINT // On z/OS we additionally need strings.h for strcasecmp. -#include <strings.h> // NOLINT +# include <strings.h> // NOLINT #elif GTEST_OS_WINDOWS_MOBILE // We are on Windows CE. -#include <windows.h> // NOLINT +# include <windows.h> // NOLINT #elif GTEST_OS_WINDOWS // We are on Windows proper. -#include <io.h> // NOLINT -#include <sys/timeb.h> // NOLINT -#include <sys/types.h> // NOLINT -#include <sys/stat.h> // NOLINT +# include <io.h> // NOLINT +# include <sys/timeb.h> // NOLINT +# include <sys/types.h> // NOLINT +# include <sys/stat.h> // NOLINT -#if GTEST_OS_WINDOWS_MINGW +# if GTEST_OS_WINDOWS_MINGW // MinGW has gettimeofday() but not _ftime64(). // TODO(kenton@google.com): Use autoconf to detect availability of // gettimeofday(). // TODO(kenton@google.com): There are other ways to get the time on // Windows, like GetTickCount() or GetSystemTimeAsFileTime(). MinGW // supports these. consider using them instead. -#define GTEST_HAS_GETTIMEOFDAY_ 1 -#include <sys/time.h> // NOLINT -#endif // GTEST_OS_WINDOWS_MINGW +# define GTEST_HAS_GETTIMEOFDAY_ 1 +# include <sys/time.h> // NOLINT +# endif // GTEST_OS_WINDOWS_MINGW // cpplint thinks that the header is already included, so we want to // silence it. -#include <windows.h> // NOLINT +# include <windows.h> // NOLINT #else // Assume other platforms have gettimeofday(). // TODO(kenton@google.com): Use autoconf to detect availability of // gettimeofday(). -#define GTEST_HAS_GETTIMEOFDAY_ 1 +# define GTEST_HAS_GETTIMEOFDAY_ 1 // cpplint thinks that the header is already included, so we want to // silence it. -#include <sys/time.h> // NOLINT -#include <unistd.h> // NOLINT +# include <sys/time.h> // NOLINT +# include <unistd.h> // NOLINT #endif // GTEST_OS_LINUX #if GTEST_HAS_EXCEPTIONS -#include <stdexcept> +# include <stdexcept> +#endif + +#if GTEST_CAN_STREAM_RESULTS_ +# include <arpa/inet.h> // NOLINT +# include <netdb.h> // NOLINT #endif // Indicates that this translation unit is part of Google Test's @@ -129,7 +133,7 @@ #undef GTEST_IMPLEMENTATION_ #if GTEST_OS_WINDOWS -#define vsnprintf _vsnprintf +# define vsnprintf _vsnprintf #endif // GTEST_OS_WINDOWS namespace testing { @@ -187,7 +191,7 @@ GTEST_DEFINE_bool_( GTEST_DEFINE_bool_( catch_exceptions, - internal::BoolFromGTestEnv("catch_exceptions", false), + internal::BoolFromGTestEnv("catch_exceptions", true), "True iff " GTEST_NAME_ " should catch exceptions and treat them as test failures."); @@ -258,6 +262,13 @@ GTEST_DEFINE_int32_( "The maximum number of stack frames to print when an " "assertion fails. The valid range is 0 through 100, inclusive."); +GTEST_DEFINE_string_( + stream_result_to, + internal::StringFromGTestEnv("stream_result_to", ""), + "This flag specifies the host name and the port number on which to stream " + "test results. Example: \"localhost:555\". The flag is effective only on " + "Linux."); + GTEST_DEFINE_bool_( throw_on_failure, internal::BoolFromGTestEnv("throw_on_failure", false), @@ -490,20 +501,33 @@ bool UnitTestOptions::FilterMatchesTest(const String &test_case_name, !MatchesFilter(full_name, negative.c_str())); } -#if GTEST_OS_WINDOWS +#if GTEST_HAS_SEH // Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the // given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise. // This function is useful as an __except condition. int UnitTestOptions::GTestShouldProcessSEH(DWORD exception_code) { - // Google Test should handle an exception if: + // Google Test should handle a SEH exception if: // 1. the user wants it to, AND - // 2. this is not a breakpoint exception. - return (GTEST_FLAG(catch_exceptions) && - exception_code != EXCEPTION_BREAKPOINT) ? - EXCEPTION_EXECUTE_HANDLER : - EXCEPTION_CONTINUE_SEARCH; + // 2. this is not a breakpoint exception, AND + // 3. this is not a C++ exception (VC++ implements them via SEH, + // apparently). + // + // SEH exception code for C++ exceptions. + // (see http://support.microsoft.com/kb/185294 for more information). + const DWORD kCxxExceptionCode = 0xe06d7363; + + bool should_handle = true; + + if (!GTEST_FLAG(catch_exceptions)) + should_handle = false; + else if (exception_code == EXCEPTION_BREAKPOINT) + should_handle = false; + else if (exception_code == kCxxExceptionCode) + should_handle = false; + + return should_handle ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH; } -#endif // GTEST_OS_WINDOWS +#endif // GTEST_HAS_SEH } // namespace internal @@ -583,7 +607,7 @@ AssertionResult HasOneFailure(const char* /* results_expr */, const char* /* substr_expr */, const TestPartResultArray& results, TestPartResult::Type type, - const char* substr) { + const string& substr) { const String expected(type == TestPartResult::kFatalFailure ? "1 fatal failure" : "1 non-fatal failure"); @@ -594,23 +618,21 @@ AssertionResult HasOneFailure(const char* /* results_expr */, for (int i = 0; i < results.size(); i++) { msg << "\n" << results.GetTestPartResult(i); } - return AssertionFailure(msg); + return AssertionFailure() << msg; } const TestPartResult& r = results.GetTestPartResult(0); if (r.type() != type) { - msg << "Expected: " << expected << "\n" - << " Actual:\n" - << r; - return AssertionFailure(msg); + return AssertionFailure() << "Expected: " << expected << "\n" + << " Actual:\n" + << r; } - if (strstr(r.message(), substr) == NULL) { - msg << "Expected: " << expected << " containing \"" - << substr << "\"\n" - << " Actual:\n" - << r; - return AssertionFailure(msg); + if (strstr(r.message(), substr.c_str()) == NULL) { + return AssertionFailure() << "Expected: " << expected << " containing \"" + << substr << "\"\n" + << " Actual:\n" + << r; } return AssertionSuccess(); @@ -622,7 +644,7 @@ AssertionResult HasOneFailure(const char* /* results_expr */, SingleFailureChecker:: SingleFailureChecker( const TestPartResultArray* results, TestPartResult::Type type, - const char* substr) + const string& substr) : results_(results), type_(type), substr_(substr) {} @@ -632,7 +654,7 @@ SingleFailureChecker:: SingleFailureChecker( // type and contains the given substring. If that's not the case, a // non-fatal failure will be generated. SingleFailureChecker::~SingleFailureChecker() { - EXPECT_PRED_FORMAT3(HasOneFailure, *results_, type_, substr_.c_str()); + EXPECT_PRED_FORMAT3(HasOneFailure, *results_, type_, substr_); } DefaultGlobalTestPartResultReporter::DefaultGlobalTestPartResultReporter( @@ -764,25 +786,30 @@ TimeInMillis GetTimeInMillis() { return 0; #elif GTEST_OS_WINDOWS && !GTEST_HAS_GETTIMEOFDAY_ __timeb64 now; -#ifdef _MSC_VER + +# ifdef _MSC_VER + // MSVC 8 deprecates _ftime64(), so we want to suppress warning 4996 // (deprecated function) there. // TODO(kenton@google.com): Use GetTickCount()? Or use // SystemTimeToFileTime() -#pragma warning(push) // Saves the current warning state. -#pragma warning(disable:4996) // Temporarily disables warning 4996. +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4996) // Temporarily disables warning 4996. _ftime64(&now); -#pragma warning(pop) // Restores the warning state. -#else +# pragma warning(pop) // Restores the warning state. +# else + _ftime64(&now); -#endif // _MSC_VER + +# endif // _MSC_VER + return static_cast<TimeInMillis>(now.time) * 1000 + now.millitm; #elif GTEST_HAS_GETTIMEOFDAY_ struct timeval now; gettimeofday(&now, NULL); return static_cast<TimeInMillis>(now.tv_sec) * 1000 + now.tv_usec / 1000; #else -#error "Don't know how to get the current time on your system." +# error "Don't know how to get the current time on your system." #endif } @@ -918,55 +945,13 @@ Message& Message::operator <<(const ::wstring& wstr) { } #endif // GTEST_HAS_GLOBAL_WSTRING -namespace internal { - -// Formats a value to be used in a failure message. - -// For a char value, we print it as a C++ char literal and as an -// unsigned integer (both in decimal and in hexadecimal). -String FormatForFailureMessage(char ch) { - const unsigned int ch_as_uint = ch; - // A String object cannot contain '\0', so we print "\\0" when ch is - // '\0'. - return String::Format("'%s' (%u, 0x%X)", - ch ? String::Format("%c", ch).c_str() : "\\0", - ch_as_uint, ch_as_uint); -} - -// For a wchar_t value, we print it as a C++ wchar_t literal and as an -// unsigned integer (both in decimal and in hexidecimal). -String FormatForFailureMessage(wchar_t wchar) { - // The C++ standard doesn't specify the exact size of the wchar_t - // type. It just says that it shall have the same size as another - // integral type, called its underlying type. - // - // Therefore, in order to print a wchar_t value in the numeric form, - // we first convert it to the largest integral type (UInt64) and - // then print the converted value. - // - // We use streaming to print the value as "%llu" doesn't work - // correctly with MSVC 7.1. - const UInt64 wchar_as_uint64 = wchar; - Message msg; - // A String object cannot contain '\0', so we print "\\0" when wchar is - // L'\0'. - char buffer[32]; // CodePointToUtf8 requires a buffer that big. - msg << "L'" - << (wchar ? CodePointToUtf8(static_cast<UInt32>(wchar), buffer) : "\\0") - << "' (" << wchar_as_uint64 << ", 0x" << ::std::setbase(16) - << wchar_as_uint64 << ")"; - return msg.GetString(); -} - -} // namespace internal - // AssertionResult constructors. // Used in EXPECT_TRUE/FALSE(assertion_result). AssertionResult::AssertionResult(const AssertionResult& other) : success_(other.success_), message_(other.message_.get() != NULL ? - new internal::String(*other.message_) : - static_cast<internal::String*>(NULL)) { + new ::std::string(*other.message_) : + static_cast< ::std::string*>(NULL)) { } // Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE. @@ -1029,7 +1014,7 @@ AssertionResult EqFailure(const char* expected_expression, msg << "\nWhich is: " << expected_value; } - return AssertionFailure(msg); + return AssertionFailure() << msg; } // Constructs a failure message for Boolean assertions such as EXPECT_TRUE. @@ -1059,13 +1044,12 @@ AssertionResult DoubleNearPredFormat(const char* expr1, // TODO(wan): do not print the value of an expression if it's // already a literal. - Message msg; - msg << "The difference between " << expr1 << " and " << expr2 + return AssertionFailure() + << "The difference between " << expr1 << " and " << expr2 << " is " << diff << ", which exceeds " << abs_error_expr << ", where\n" << expr1 << " evaluates to " << val1 << ",\n" << expr2 << " evaluates to " << val2 << ", and\n" << abs_error_expr << " evaluates to " << abs_error << "."; - return AssertionFailure(msg); } @@ -1090,20 +1074,18 @@ AssertionResult FloatingPointLE(const char* expr1, // val2 is NaN, as the IEEE floating-point standard requires that // any predicate involving a NaN must return false. - StrStream val1_ss; + ::std::stringstream val1_ss; val1_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2) << val1; - StrStream val2_ss; + ::std::stringstream val2_ss; val2_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2) << val2; - Message msg; - msg << "Expected: (" << expr1 << ") <= (" << expr2 << ")\n" - << " Actual: " << StrStreamToString(&val1_ss) << " vs " - << StrStreamToString(&val2_ss); - - return AssertionFailure(msg); + return AssertionFailure() + << "Expected: (" << expr1 << ") <= (" << expr2 << ")\n" + << " Actual: " << StringStreamToString(&val1_ss) << " vs " + << StringStreamToString(&val2_ss); } } // namespace internal @@ -1150,11 +1132,10 @@ AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \ if (val1 op val2) {\ return AssertionSuccess();\ } else {\ - Message msg;\ - msg << "Expected: (" << expr1 << ") " #op " (" << expr2\ + return AssertionFailure() \ + << "Expected: (" << expr1 << ") " #op " (" << expr2\ << "), actual: " << FormatForComparisonFailureMessage(val1, val2)\ << " vs " << FormatForComparisonFailureMessage(val2, val1);\ - return AssertionFailure(msg);\ }\ } @@ -1216,11 +1197,9 @@ AssertionResult CmpHelperSTRNE(const char* s1_expression, if (!String::CStringEquals(s1, s2)) { return AssertionSuccess(); } else { - Message msg; - msg << "Expected: (" << s1_expression << ") != (" - << s2_expression << "), actual: \"" - << s1 << "\" vs \"" << s2 << "\""; - return AssertionFailure(msg); + return AssertionFailure() << "Expected: (" << s1_expression << ") != (" + << s2_expression << "), actual: \"" + << s1 << "\" vs \"" << s2 << "\""; } } @@ -1232,11 +1211,10 @@ AssertionResult CmpHelperSTRCASENE(const char* s1_expression, if (!String::CaseInsensitiveCStringEquals(s1, s2)) { return AssertionSuccess(); } else { - Message msg; - msg << "Expected: (" << s1_expression << ") != (" + return AssertionFailure() + << "Expected: (" << s1_expression << ") != (" << s2_expression << ") (ignoring case), actual: \"" << s1 << "\" vs \"" << s2 << "\""; - return AssertionFailure(msg); } } @@ -1285,13 +1263,12 @@ AssertionResult IsSubstringImpl( const bool is_wide_string = sizeof(needle[0]) > 1; const char* const begin_string_quote = is_wide_string ? "L\"" : "\""; - return AssertionFailure( - Message() + return AssertionFailure() << "Value of: " << needle_expr << "\n" << " Actual: " << begin_string_quote << needle << "\"\n" << "Expected: " << (expected_to_be_substring ? "" : "not ") << "a substring of " << haystack_expr << "\n" - << "Which is: " << begin_string_quote << haystack << "\""); + << "Which is: " << begin_string_quote << haystack << "\""; } } // namespace @@ -1360,10 +1337,13 @@ namespace { AssertionResult HRESULTFailureHelper(const char* expr, const char* expected, long hr) { // NOLINT -#if GTEST_OS_WINDOWS_MOBILE +# if GTEST_OS_WINDOWS_MOBILE + // Windows CE doesn't support FormatMessage. const char error_text[] = ""; -#else + +# else + // Looks up the human-readable system message for the HRESULT code // and since we're not passing any params to FormatMessage, we don't // want inserts expanded. @@ -1380,18 +1360,17 @@ AssertionResult HRESULTFailureHelper(const char* expr, kBufSize, // buf size NULL); // no arguments for inserts // Trims tailing white space (FormatMessage leaves a trailing cr-lf) - for (; message_length && isspace(error_text[message_length - 1]); + for (; message_length && IsSpace(error_text[message_length - 1]); --message_length) { error_text[message_length - 1] = '\0'; } -#endif // GTEST_OS_WINDOWS_MOBILE + +# endif // GTEST_OS_WINDOWS_MOBILE const String error_hex(String::Format("0x%08X ", hr)); - Message msg; - msg << "Expected: " << expr << " " << expected << ".\n" + return ::testing::AssertionFailure() + << "Expected: " << expr << " " << expected << ".\n" << " Actual: " << error_hex << error_text << "\n"; - - return ::testing::AssertionFailure(msg); } } // namespace @@ -1526,7 +1505,7 @@ String WideStringToUtf8(const wchar_t* str, int num_chars) { if (num_chars == -1) num_chars = static_cast<int>(wcslen(str)); - StrStream stream; + ::std::stringstream stream; for (int i = 0; i < num_chars; ++i) { UInt32 unicode_code_point; @@ -1543,7 +1522,7 @@ String WideStringToUtf8(const wchar_t* str, int num_chars) { char buffer[32]; // CodePointToUtf8 requires a buffer this big. stream << CodePointToUtf8(unicode_code_point, buffer); } - return StrStreamToString(&stream); + return StringStreamToString(&stream); } // Converts a wide C string to a String using the UTF-8 encoding. @@ -1602,12 +1581,10 @@ AssertionResult CmpHelperSTRNE(const char* s1_expression, return AssertionSuccess(); } - Message msg; - msg << "Expected: (" << s1_expression << ") != (" - << s2_expression << "), actual: " - << String::ShowWideCStringQuoted(s1) - << " vs " << String::ShowWideCStringQuoted(s2); - return AssertionFailure(msg); + return AssertionFailure() << "Expected: (" << s1_expression << ") != (" + << s2_expression << "), actual: " + << String::ShowWideCStringQuoted(s1) + << " vs " << String::ShowWideCStringQuoted(s2); } // Compares two C strings, ignoring case. Returns true iff they have @@ -1638,17 +1615,17 @@ bool String::CaseInsensitiveCStringEquals(const char * lhs, const char * rhs) { // current locale. bool String::CaseInsensitiveWideCStringEquals(const wchar_t* lhs, const wchar_t* rhs) { - if ( lhs == NULL ) return rhs == NULL; + if (lhs == NULL) return rhs == NULL; - if ( rhs == NULL ) return false; + if (rhs == NULL) return false; #if GTEST_OS_WINDOWS return _wcsicmp(lhs, rhs) == 0; -#elif GTEST_OS_LINUX +#elif GTEST_OS_LINUX && !GTEST_OS_LINUX_ANDROID return wcscasecmp(lhs, rhs) == 0; #else - // Mac OS X and Cygwin don't define wcscasecmp. Other unknown OSes - // may not define it either. + // Android, Mac OS X and Cygwin don't define wcscasecmp. + // Other unknown OSes may not define it either. wint_t left, right; do { left = towlower(*lhs++); @@ -1730,10 +1707,12 @@ String String::Format(const char * format, ...) { // MSVC 8 deprecates vsnprintf(), so we want to suppress warning // 4996 (deprecated function) there. #ifdef _MSC_VER // We are using MSVC. -#pragma warning(push) // Saves the current warning state. -#pragma warning(disable:4996) // Temporarily disables warning 4996. +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4996) // Temporarily disables warning 4996. + const int size = vsnprintf(buffer, kBufferSize, format, args); -#pragma warning(pop) // Restores the warning state. + +# pragma warning(pop) // Restores the warning state. #else // We are not using MSVC. const int size = vsnprintf(buffer, kBufferSize, format, args); #endif // _MSC_VER @@ -1751,16 +1730,16 @@ String String::Format(const char * format, ...) { } } -// Converts the buffer in a StrStream to a String, converting NUL +// Converts the buffer in a stringstream to a String, converting NUL // bytes to "\\0" along the way. -String StrStreamToString(StrStream* ss) { +String StringStreamToString(::std::stringstream* ss) { const ::std::string& str = ss->str(); const char* const start = str.c_str(); const char* const end = start + str.length(); - // We need to use a helper StrStream to do this transformation + // We need to use a helper stringstream to do this transformation // because String doesn't support push_back(). - StrStream helper; + ::std::stringstream helper; for (const char* ch = start; ch != end; ++ch) { if (*ch == '\0') { helper << "\\0"; // Replaces NUL with "\\0"; @@ -1964,22 +1943,6 @@ void ReportFailureInUnknownLocation(TestPartResult::Type result_type, } // namespace internal -#if GTEST_HAS_SEH -// We are on Windows with SEH. - -// Adds an "exception thrown" fatal failure to the current test. -static void AddExceptionThrownFailure(DWORD exception_code, - const char* location) { - Message message; - message << "Exception thrown with code 0x" << std::setbase(16) << - exception_code << std::setbase(10) << " in " << location << "."; - - internal::ReportFailureInUnknownLocation(TestPartResult::kFatalFailure, - message.GetString()); -} - -#endif // GTEST_HAS_SEH - // Google Test requires all tests in the same test case to use the same test // fixture class. This function checks if the current test has the // same fixture class as the first test in the current test case. If @@ -1990,15 +1953,13 @@ bool Test::HasSameFixtureClass() { const TestCase* const test_case = impl->current_test_case(); // Info about the first test in the current test case. - const internal::TestInfoImpl* const first_test_info = - test_case->test_info_list()[0]->impl(); - const internal::TypeId first_fixture_id = first_test_info->fixture_class_id(); + const TestInfo* const first_test_info = test_case->test_info_list()[0]; + const internal::TypeId first_fixture_id = first_test_info->fixture_class_id_; const char* const first_test_name = first_test_info->name(); // Info about the current test. - const internal::TestInfoImpl* const this_test_info = - impl->current_test_info()->impl(); - const internal::TypeId this_fixture_id = this_test_info->fixture_class_id(); + const TestInfo* const this_test_info = impl->current_test_info(); + const internal::TypeId this_fixture_id = this_test_info->fixture_class_id_; const char* const this_test_name = this_test_info->name(); if (this_fixture_id != first_fixture_id) { @@ -2048,62 +2009,167 @@ bool Test::HasSameFixtureClass() { return true; } -// Runs the test and updates the test result. -void Test::Run() { - if (!HasSameFixtureClass()) return; +#if GTEST_HAS_SEH - internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); +// Adds an "exception thrown" fatal failure to the current test. This +// function returns its result via an output parameter pointer because VC++ +// prohibits creation of objects with destructors on stack in functions +// using __try (see error C2712). +static internal::String* FormatSehExceptionMessage(DWORD exception_code, + const char* location) { + Message message; + message << "SEH exception with code 0x" << std::setbase(16) << + exception_code << std::setbase(10) << " thrown in " << location << "."; + + return new internal::String(message.GetString()); +} + +#endif // GTEST_HAS_SEH + +#if GTEST_HAS_EXCEPTIONS + +// Adds an "exception thrown" fatal failure to the current test. +static internal::String FormatCxxExceptionMessage(const char* description, + const char* location) { + Message message; + if (description != NULL) { + message << "C++ exception with description \"" << description << "\""; + } else { + message << "Unknown C++ exception"; + } + message << " thrown in " << location << "."; + + return message.GetString(); +} + +static internal::String PrintTestPartResultToString( + const TestPartResult& test_part_result); + +// A failed Google Test assertion will throw an exception of this type when +// GTEST_FLAG(throw_on_failure) is true (if exceptions are enabled). We +// derive it from std::runtime_error, which is for errors presumably +// detectable only at run time. Since std::runtime_error inherits from +// std::exception, many testing frameworks know how to extract and print the +// message inside it. +class GoogleTestFailureException : public ::std::runtime_error { + public: + explicit GoogleTestFailureException(const TestPartResult& failure) + : ::std::runtime_error(PrintTestPartResultToString(failure).c_str()) {} +}; +#endif // GTEST_HAS_EXCEPTIONS + +namespace internal { +// We put these helper functions in the internal namespace as IBM's xlC +// compiler rejects the code if they were declared static. + +// Runs the given method and handles SEH exceptions it throws, when +// SEH is supported; returns the 0-value for type Result in case of an +// SEH exception. (Microsoft compilers cannot handle SEH and C++ +// exceptions in the same function. Therefore, we provide a separate +// wrapper function for handling SEH exceptions.) +template <class T, typename Result> +Result HandleSehExceptionsInMethodIfSupported( + T* object, Result (T::*method)(), const char* location) { #if GTEST_HAS_SEH - // Catch SEH-style exceptions. - impl->os_stack_trace_getter()->UponLeavingGTest(); __try { - SetUp(); - } __except(internal::UnitTestOptions::GTestShouldProcessSEH( + return (object->*method)(); + } __except (internal::UnitTestOptions::GTestShouldProcessSEH( // NOLINT GetExceptionCode())) { - AddExceptionThrownFailure(GetExceptionCode(), "SetUp()"); + // We create the exception message on the heap because VC++ prohibits + // creation of objects with destructors on stack in functions using __try + // (see error C2712). + internal::String* exception_message = FormatSehExceptionMessage( + GetExceptionCode(), location); + internal::ReportFailureInUnknownLocation(TestPartResult::kFatalFailure, + *exception_message); + delete exception_message; + return static_cast<Result>(0); } +#else + (void)location; + return (object->*method)(); +#endif // GTEST_HAS_SEH +} - // We will run the test only if SetUp() had no fatal failure. - if (!HasFatalFailure()) { - impl->os_stack_trace_getter()->UponLeavingGTest(); - __try { - TestBody(); - } __except(internal::UnitTestOptions::GTestShouldProcessSEH( - GetExceptionCode())) { - AddExceptionThrownFailure(GetExceptionCode(), "the test body"); +// Runs the given method and catches and reports C++ and/or SEH-style +// exceptions, if they are supported; returns the 0-value for type +// Result in case of an SEH exception. +template <class T, typename Result> +Result HandleExceptionsInMethodIfSupported( + T* object, Result (T::*method)(), const char* location) { + // NOTE: The user code can affect the way in which Google Test handles + // exceptions by setting GTEST_FLAG(catch_exceptions), but only before + // RUN_ALL_TESTS() starts. It is technically possible to check the flag + // after the exception is caught and either report or re-throw the + // exception based on the flag's value: + // + // try { + // // Perform the test method. + // } catch (...) { + // if (GTEST_FLAG(catch_exceptions)) + // // Report the exception as failure. + // else + // throw; // Re-throws the original exception. + // } + // + // However, the purpose of this flag is to allow the program to drop into + // the debugger when the exception is thrown. On most platforms, once the + // control enters the catch block, the exception origin information is + // lost and the debugger will stop the program at the point of the + // re-throw in this function -- instead of at the point of the original + // throw statement in the code under test. For this reason, we perform + // the check early, sacrificing the ability to affect Google Test's + // exception handling in the method where the exception is thrown. + if (internal::GetUnitTestImpl()->catch_exceptions()) { +#if GTEST_HAS_EXCEPTIONS + try { + return HandleSehExceptionsInMethodIfSupported(object, method, location); + } catch (const GoogleTestFailureException&) { // NOLINT + // This exception doesn't originate in code under test. It makes no + // sense to report it as a test failure. + throw; + } catch (const std::exception& e) { // NOLINT + internal::ReportFailureInUnknownLocation( + TestPartResult::kFatalFailure, + FormatCxxExceptionMessage(e.what(), location)); + } catch (...) { // NOLINT + internal::ReportFailureInUnknownLocation( + TestPartResult::kFatalFailure, + FormatCxxExceptionMessage(NULL, location)); } + return static_cast<Result>(0); +#else + return HandleSehExceptionsInMethodIfSupported(object, method, location); +#endif // GTEST_HAS_EXCEPTIONS + } else { + return (object->*method)(); } +} - // However, we want to clean up as much as possible. Hence we will - // always call TearDown(), even if SetUp() or the test body has - // failed. - impl->os_stack_trace_getter()->UponLeavingGTest(); - __try { - TearDown(); - } __except(internal::UnitTestOptions::GTestShouldProcessSEH( - GetExceptionCode())) { - AddExceptionThrownFailure(GetExceptionCode(), "TearDown()"); - } +} // namespace internal -#else // We are on a compiler or platform that doesn't support SEH. - impl->os_stack_trace_getter()->UponLeavingGTest(); - SetUp(); +// Runs the test and updates the test result. +void Test::Run() { + if (!HasSameFixtureClass()) return; + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported(this, &Test::SetUp, "SetUp()"); // We will run the test only if SetUp() was successful. if (!HasFatalFailure()) { impl->os_stack_trace_getter()->UponLeavingGTest(); - TestBody(); + internal::HandleExceptionsInMethodIfSupported( + this, &Test::TestBody, "the test body"); } // However, we want to clean up as much as possible. Hence we will // always call TearDown(), even if SetUp() or the test body has // failed. impl->os_stack_trace_getter()->UponLeavingGTest(); - TearDown(); -#endif // GTEST_HAS_SEH + internal::HandleExceptionsInMethodIfSupported( + this, &Test::TearDown, "TearDown()"); } - // Returns true iff the current test has a fatal failure. bool Test::HasFatalFailure() { return internal::GetUnitTestImpl()->current_test_result()->HasFatalFailure(); @@ -2118,22 +2184,28 @@ bool Test::HasNonfatalFailure() { // class TestInfo // Constructs a TestInfo object. It assumes ownership of the test factory -// object via impl_. +// object. +// TODO(vladl@google.com): Make a_test_case_name and a_name const string&'s +// to signify they cannot be NULLs. TestInfo::TestInfo(const char* a_test_case_name, const char* a_name, - const char* a_test_case_comment, - const char* a_comment, + const char* a_type_param, + const char* a_value_param, internal::TypeId fixture_class_id, - internal::TestFactoryBase* factory) { - impl_ = new internal::TestInfoImpl(this, a_test_case_name, a_name, - a_test_case_comment, a_comment, - fixture_class_id, factory); -} + internal::TestFactoryBase* factory) + : test_case_name_(a_test_case_name), + name_(a_name), + type_param_(a_type_param ? new std::string(a_type_param) : NULL), + value_param_(a_value_param ? new std::string(a_value_param) : NULL), + fixture_class_id_(fixture_class_id), + should_run_(false), + is_disabled_(false), + matches_filter_(false), + factory_(factory), + result_() {} // Destructs a TestInfo object. -TestInfo::~TestInfo() { - delete impl_; -} +TestInfo::~TestInfo() { delete factory_; } namespace internal { @@ -2144,10 +2216,10 @@ namespace internal { // // test_case_name: name of the test case // name: name of the test -// test_case_comment: a comment on the test case that will be included in -// the test output -// comment: a comment on the test that will be included in the -// test output +// type_param: the name of the test's type parameter, or NULL if +// this is not a typed or a type-parameterized test. +// value_param: text representation of the test's value parameter, +// or NULL if this is not a value-parameterized test. // fixture_class_id: ID of the test fixture class // set_up_tc: pointer to the function that sets up the test case // tear_down_tc: pointer to the function that tears down the test case @@ -2156,13 +2228,14 @@ namespace internal { // ownership of the factory object. TestInfo* MakeAndRegisterTestInfo( const char* test_case_name, const char* name, - const char* test_case_comment, const char* comment, + const char* type_param, + const char* value_param, TypeId fixture_class_id, SetUpTestCaseFunc set_up_tc, TearDownTestCaseFunc tear_down_tc, TestFactoryBase* factory) { TestInfo* const test_info = - new TestInfo(test_case_name, name, test_case_comment, comment, + new TestInfo(test_case_name, name, type_param, value_param, fixture_class_id, factory); GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info); return test_info; @@ -2189,41 +2262,6 @@ void ReportInvalidTestCaseType(const char* test_case_name, } // namespace internal -// Returns the test case name. -const char* TestInfo::test_case_name() const { - return impl_->test_case_name(); -} - -// Returns the test name. -const char* TestInfo::name() const { - return impl_->name(); -} - -// Returns the test case comment. -const char* TestInfo::test_case_comment() const { - return impl_->test_case_comment(); -} - -// Returns the test comment. -const char* TestInfo::comment() const { - return impl_->comment(); -} - -// Returns true if this test should run. -bool TestInfo::should_run() const { return impl_->should_run(); } - -// Returns true if this test matches the user-specified filter. -bool TestInfo::matches_filter() const { return impl_->matches_filter(); } - -// Returns the result of the test. -const TestResult* TestInfo::result() const { return impl_->result(); } - -// Increments the number of death tests encountered in this test so -// far. -int TestInfo::increment_death_test_count() { - return impl_->result()->increment_death_test_count(); -} - namespace internal { // This method expands all parameterized tests registered with macros TEST_P @@ -2238,70 +2276,54 @@ void UnitTestImpl::RegisterParameterizedTests() { #endif } +} // namespace internal + // Creates the test object, runs it, records its result, and then // deletes it. -void TestInfoImpl::Run() { +void TestInfo::Run() { if (!should_run_) return; // Tells UnitTest where to store test result. - UnitTestImpl* const impl = internal::GetUnitTestImpl(); - impl->set_current_test_info(parent_); + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->set_current_test_info(this); TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); // Notifies the unit test event listeners that a test is about to start. - repeater->OnTestStart(*parent_); + repeater->OnTestStart(*this); - const TimeInMillis start = GetTimeInMillis(); + const TimeInMillis start = internal::GetTimeInMillis(); impl->os_stack_trace_getter()->UponLeavingGTest(); -#if GTEST_HAS_SEH - // Catch SEH-style exceptions. - Test* test = NULL; - - __try { - // Creates the test object. - test = factory_->CreateTest(); - } __except(internal::UnitTestOptions::GTestShouldProcessSEH( - GetExceptionCode())) { - AddExceptionThrownFailure(GetExceptionCode(), - "the test fixture's constructor"); - return; - } -#else // We are on a compiler or platform that doesn't support SEH. - - // TODO(wan): If test->Run() throws, test won't be deleted. This is - // not a problem now as we don't use exceptions. If we were to - // enable exceptions, we should revise the following to be - // exception-safe. // Creates the test object. - Test* test = factory_->CreateTest(); -#endif // GTEST_HAS_SEH - - // Runs the test only if the constructor of the test fixture didn't - // generate a fatal failure. - if (!Test::HasFatalFailure()) { + Test* const test = internal::HandleExceptionsInMethodIfSupported( + factory_, &internal::TestFactoryBase::CreateTest, + "the test fixture's constructor"); + + // Runs the test only if the test object was created and its + // constructor didn't generate a fatal failure. + if ((test != NULL) && !Test::HasFatalFailure()) { + // This doesn't throw as all user code that can throw are wrapped into + // exception handling code. test->Run(); } // Deletes the test object. impl->os_stack_trace_getter()->UponLeavingGTest(); - delete test; - test = NULL; + internal::HandleExceptionsInMethodIfSupported( + test, &Test::DeleteSelf_, "the test fixture's destructor"); - result_.set_elapsed_time(GetTimeInMillis() - start); + result_.set_elapsed_time(internal::GetTimeInMillis() - start); // Notifies the unit test event listener that a test has just finished. - repeater->OnTestEnd(*parent_); + repeater->OnTestEnd(*this); // Tells UnitTest to stop associating assertion results to this // test. impl->set_current_test_info(NULL); } -} // namespace internal - // class TestCase // Gets the number of successful tests in this test case. @@ -2333,13 +2355,15 @@ int TestCase::total_test_count() const { // Arguments: // // name: name of the test case +// a_type_param: the name of the test case's type parameter, or NULL if +// this is not a typed or a type-parameterized test case. // set_up_tc: pointer to the function that sets up the test case // tear_down_tc: pointer to the function that tears down the test case -TestCase::TestCase(const char* a_name, const char* a_comment, +TestCase::TestCase(const char* a_name, const char* a_type_param, Test::SetUpTestCaseFunc set_up_tc, Test::TearDownTestCaseFunc tear_down_tc) : name_(a_name), - comment_(a_comment), + type_param_(a_type_param ? new std::string(a_type_param) : NULL), set_up_tc_(set_up_tc), tear_down_tc_(tear_down_tc), should_run_(false), @@ -2384,45 +2408,26 @@ void TestCase::Run() { repeater->OnTestCaseStart(*this); impl->os_stack_trace_getter()->UponLeavingGTest(); - set_up_tc_(); + internal::HandleExceptionsInMethodIfSupported( + this, &TestCase::RunSetUpTestCase, "SetUpTestCase()"); const internal::TimeInMillis start = internal::GetTimeInMillis(); for (int i = 0; i < total_test_count(); i++) { - GetMutableTestInfo(i)->impl()->Run(); + GetMutableTestInfo(i)->Run(); } elapsed_time_ = internal::GetTimeInMillis() - start; impl->os_stack_trace_getter()->UponLeavingGTest(); - tear_down_tc_(); + internal::HandleExceptionsInMethodIfSupported( + this, &TestCase::RunTearDownTestCase, "TearDownTestCase()"); + repeater->OnTestCaseEnd(*this); impl->set_current_test_case(NULL); } // Clears the results of all tests in this test case. void TestCase::ClearResult() { - ForEach(test_info_list_, internal::TestInfoImpl::ClearTestResult); -} - -// Returns true iff test passed. -bool TestCase::TestPassed(const TestInfo * test_info) { - const internal::TestInfoImpl* const impl = test_info->impl(); - return impl->should_run() && impl->result()->Passed(); -} - -// Returns true iff test failed. -bool TestCase::TestFailed(const TestInfo * test_info) { - const internal::TestInfoImpl* const impl = test_info->impl(); - return impl->should_run() && impl->result()->Failed(); -} - -// Returns true iff test is disabled. -bool TestCase::TestDisabled(const TestInfo * test_info) { - return test_info->impl()->is_disabled(); -} - -// Returns true if the given test should run. -bool TestCase::ShouldRunTest(const TestInfo *test_info) { - return test_info->impl()->should_run(); + ForEach(test_info_list_, TestInfo::ClearTestResult); } // Shuffles the tests in this test case. @@ -2475,9 +2480,9 @@ static const char * TestPartResultTypeToString(TestPartResult::Type type) { #else return "Failure\n"; #endif + default: + return "Unknown result type"; } - - return "Unknown result type"; } // Prints a TestPartResult to a String. @@ -2563,6 +2568,7 @@ bool ShouldUseColor(bool stdout_is_tty) { String::CStringEquals(term, "xterm") || String::CStringEquals(term, "xterm-color") || String::CStringEquals(term, "xterm-256color") || + String::CStringEquals(term, "screen") || String::CStringEquals(term, "linux") || String::CStringEquals(term, "cygwin"); return stdout_is_tty && term_supports_color; @@ -2628,6 +2634,23 @@ void ColoredPrintf(GTestColor color, const char* fmt, ...) { va_end(args); } +void PrintFullTestCommentIfPresent(const TestInfo& test_info) { + const char* const type_param = test_info.type_param(); + const char* const value_param = test_info.value_param(); + + if (type_param != NULL || value_param != NULL) { + printf(", where "); + if (type_param != NULL) { + printf("TypeParam = %s", type_param); + if (value_param != NULL) + printf(" and "); + } + if (value_param != NULL) { + printf("GetParam() = %s", value_param); + } + } +} + // This class implements the TestEventListener interface. // // Class PrettyUnitTestResultPrinter is copyable. @@ -2675,9 +2698,10 @@ void PrettyUnitTestResultPrinter::OnTestIterationStart( } if (internal::ShouldShard(kTestTotalShards, kTestShardIndex, false)) { + const Int32 shard_index = Int32FromEnvOrDie(kTestShardIndex, -1); ColoredPrintf(COLOR_YELLOW, - "Note: This is test shard %s of %s.\n", - internal::posix::GetEnv(kTestShardIndex), + "Note: This is test shard %d of %s.\n", + static_cast<int>(shard_index) + 1, internal::posix::GetEnv(kTestTotalShards)); } @@ -2707,10 +2731,10 @@ void PrettyUnitTestResultPrinter::OnTestCaseStart(const TestCase& test_case) { FormatCountableNoun(test_case.test_to_run_count(), "test", "tests"); ColoredPrintf(COLOR_GREEN, "[----------] "); printf("%s from %s", counts.c_str(), test_case_name_.c_str()); - if (test_case.comment()[0] == '\0') { + if (test_case.type_param() == NULL) { printf("\n"); } else { - printf(", where %s\n", test_case.comment()); + printf(", where TypeParam = %s\n", test_case.type_param()); } fflush(stdout); } @@ -2718,11 +2742,7 @@ void PrettyUnitTestResultPrinter::OnTestCaseStart(const TestCase& test_case) { void PrettyUnitTestResultPrinter::OnTestStart(const TestInfo& test_info) { ColoredPrintf(COLOR_GREEN, "[ RUN ] "); PrintTestName(test_case_name_.c_str(), test_info.name()); - if (test_info.comment()[0] == '\0') { - printf("\n"); - } else { - printf(", where %s\n", test_info.comment()); - } + printf("\n"); fflush(stdout); } @@ -2745,6 +2765,9 @@ void PrettyUnitTestResultPrinter::OnTestEnd(const TestInfo& test_info) { ColoredPrintf(COLOR_RED, "[ FAILED ] "); } PrintTestName(test_case_name_.c_str(), test_info.name()); + if (test_info.result()->Failed()) + PrintFullTestCommentIfPresent(test_info); + if (GTEST_FLAG(print_time)) { printf(" (%s ms)\n", internal::StreamableToString( test_info.result()->elapsed_time()).c_str()); @@ -2793,21 +2816,14 @@ void PrettyUnitTestResultPrinter::PrintFailedTests(const UnitTest& unit_test) { } ColoredPrintf(COLOR_RED, "[ FAILED ] "); printf("%s.%s", test_case.name(), test_info.name()); - if (test_case.comment()[0] != '\0' || - test_info.comment()[0] != '\0') { - printf(", where %s", test_case.comment()); - if (test_case.comment()[0] != '\0' && - test_info.comment()[0] != '\0') { - printf(" and "); - } - } - printf("%s\n", test_info.comment()); + PrintFullTestCommentIfPresent(test_info); + printf("\n"); } } } - void PrettyUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, - int /*iteration*/) { +void PrettyUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, + int /*iteration*/) { ColoredPrintf(COLOR_GREEN, "[==========] "); printf("%s from %s ran.", FormatTestCount(unit_test.test_to_run_count()).c_str(), @@ -2987,7 +3003,7 @@ class XmlUnitTestResultPrinter : public EmptyTestEventListener { static String EscapeXml(const char* str, bool is_attribute); // Returns the given string with all characters invalid in XML removed. - static String RemoveInvalidXmlCharacters(const char* str); + static string RemoveInvalidXmlCharacters(const string& str); // Convenience wrapper around EscapeXml when str is an attribute value. static String EscapeXmlAttribute(const char* str) { @@ -3121,17 +3137,14 @@ String XmlUnitTestResultPrinter::EscapeXml(const char* str, bool is_attribute) { // Returns the given string with all characters invalid in XML removed. // Currently invalid characters are dropped from the string. An // alternative is to replace them with certain characters such as . or ?. -String XmlUnitTestResultPrinter::RemoveInvalidXmlCharacters(const char* str) { - char* const output = new char[strlen(str) + 1]; - char* appender = output; - for (char ch = *str; ch != '\0'; ch = *++str) - if (IsValidXmlCharacter(ch)) - *appender++ = ch; - *appender = '\0'; +string XmlUnitTestResultPrinter::RemoveInvalidXmlCharacters(const string& str) { + string output; + output.reserve(str.size()); + for (string::const_iterator it = str.begin(); it != str.end(); ++it) + if (IsValidXmlCharacter(*it)) + output.push_back(*it); - String ret_value(output); - delete[] output; - return ret_value; + return output; } // The following routines generate an XML representation of a UnitTest @@ -3184,8 +3197,18 @@ void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream, const TestInfo& test_info) { const TestResult& result = *test_info.result(); *stream << " <testcase name=\"" - << EscapeXmlAttribute(test_info.name()).c_str() - << "\" status=\"" + << EscapeXmlAttribute(test_info.name()).c_str() << "\""; + + if (test_info.value_param() != NULL) { + *stream << " value_param=\"" << EscapeXmlAttribute(test_info.value_param()) + << "\""; + } + if (test_info.type_param() != NULL) { + *stream << " type_param=\"" << EscapeXmlAttribute(test_info.type_param()) + << "\""; + } + + *stream << " status=\"" << (test_info.should_run() ? "run" : "notrun") << "\" time=\"" << FormatTimeInMillisAsSeconds(result.elapsed_time()) @@ -3201,11 +3224,11 @@ void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream, *stream << " <failure message=\"" << EscapeXmlAttribute(part.summary()).c_str() << "\" type=\"\">"; - const String message = RemoveInvalidXmlCharacters(String::Format( - "%s:%d\n%s", - part.file_name(), part.line_number(), - part.message()).c_str()); - OutputXmlCDataSection(stream, message.c_str()); + const string location = internal::FormatCompilerIndependentFileLocation( + part.file_name(), part.line_number()); + const string message = location + "\n" + part.message(); + OutputXmlCDataSection(stream, + RemoveInvalidXmlCharacters(message).c_str()); *stream << "</failure>\n"; } } @@ -3230,9 +3253,9 @@ void XmlUnitTestResultPrinter::PrintXmlTestCase(FILE* out, "errors=\"0\" time=\"%s\">\n", FormatTimeInMillisAsSeconds(test_case.elapsed_time()).c_str()); for (int i = 0; i < test_case.total_test_count(); ++i) { - StrStream stream; + ::std::stringstream stream; OutputXmlTestInfo(&stream, test_case.name(), *test_case.GetTestInfo(i)); - fprintf(out, "%s", StrStreamToString(&stream).c_str()); + fprintf(out, "%s", StringStreamToString(&stream).c_str()); } fprintf(out, " </testsuite>\n"); } @@ -3272,6 +3295,182 @@ String XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes( // End XmlUnitTestResultPrinter +#if GTEST_CAN_STREAM_RESULTS_ + +// Streams test results to the given port on the given host machine. +class StreamingListener : public EmptyTestEventListener { + public: + // Escapes '=', '&', '%', and '\n' characters in str as "%xx". + static string UrlEncode(const char* str); + + StreamingListener(const string& host, const string& port) + : sockfd_(-1), host_name_(host), port_num_(port) { + MakeConnection(); + Send("gtest_streaming_protocol_version=1.0\n"); + } + + virtual ~StreamingListener() { + if (sockfd_ != -1) + CloseConnection(); + } + + void OnTestProgramStart(const UnitTest& /* unit_test */) { + Send("event=TestProgramStart\n"); + } + + void OnTestProgramEnd(const UnitTest& unit_test) { + // Note that Google Test current only report elapsed time for each + // test iteration, not for the entire test program. + Send(String::Format("event=TestProgramEnd&passed=%d\n", + unit_test.Passed())); + + // Notify the streaming server to stop. + CloseConnection(); + } + + void OnTestIterationStart(const UnitTest& /* unit_test */, int iteration) { + Send(String::Format("event=TestIterationStart&iteration=%d\n", + iteration)); + } + + void OnTestIterationEnd(const UnitTest& unit_test, int /* iteration */) { + Send(String::Format("event=TestIterationEnd&passed=%d&elapsed_time=%sms\n", + unit_test.Passed(), + StreamableToString(unit_test.elapsed_time()).c_str())); + } + + void OnTestCaseStart(const TestCase& test_case) { + Send(String::Format("event=TestCaseStart&name=%s\n", test_case.name())); + } + + void OnTestCaseEnd(const TestCase& test_case) { + Send(String::Format("event=TestCaseEnd&passed=%d&elapsed_time=%sms\n", + test_case.Passed(), + StreamableToString(test_case.elapsed_time()).c_str())); + } + + void OnTestStart(const TestInfo& test_info) { + Send(String::Format("event=TestStart&name=%s\n", test_info.name())); + } + + void OnTestEnd(const TestInfo& test_info) { + Send(String::Format( + "event=TestEnd&passed=%d&elapsed_time=%sms\n", + (test_info.result())->Passed(), + StreamableToString((test_info.result())->elapsed_time()).c_str())); + } + + void OnTestPartResult(const TestPartResult& test_part_result) { + const char* file_name = test_part_result.file_name(); + if (file_name == NULL) + file_name = ""; + Send(String::Format("event=TestPartResult&file=%s&line=%d&message=", + UrlEncode(file_name).c_str(), + test_part_result.line_number())); + Send(UrlEncode(test_part_result.message()) + "\n"); + } + + private: + // Creates a client socket and connects to the server. + void MakeConnection(); + + // Closes the socket. + void CloseConnection() { + GTEST_CHECK_(sockfd_ != -1) + << "CloseConnection() can be called only when there is a connection."; + + close(sockfd_); + sockfd_ = -1; + } + + // Sends a string to the socket. + void Send(const string& message) { + GTEST_CHECK_(sockfd_ != -1) + << "Send() can be called only when there is a connection."; + + const int len = static_cast<int>(message.length()); + if (write(sockfd_, message.c_str(), len) != len) { + GTEST_LOG_(WARNING) + << "stream_result_to: failed to stream to " + << host_name_ << ":" << port_num_; + } + } + + int sockfd_; // socket file descriptor + const string host_name_; + const string port_num_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamingListener); +}; // class StreamingListener + +// Checks if str contains '=', '&', '%' or '\n' characters. If yes, +// replaces them by "%xx" where xx is their hexadecimal value. For +// example, replaces "=" with "%3D". This algorithm is O(strlen(str)) +// in both time and space -- important as the input str may contain an +// arbitrarily long test failure message and stack trace. +string StreamingListener::UrlEncode(const char* str) { + string result; + result.reserve(strlen(str) + 1); + for (char ch = *str; ch != '\0'; ch = *++str) { + switch (ch) { + case '%': + case '=': + case '&': + case '\n': + result.append(String::Format("%%%02x", static_cast<unsigned char>(ch))); + break; + default: + result.push_back(ch); + break; + } + } + return result; +} + +void StreamingListener::MakeConnection() { + GTEST_CHECK_(sockfd_ == -1) + << "MakeConnection() can't be called when there is already a connection."; + + addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; // To allow both IPv4 and IPv6 addresses. + hints.ai_socktype = SOCK_STREAM; + addrinfo* servinfo = NULL; + + // Use the getaddrinfo() to get a linked list of IP addresses for + // the given host name. + const int error_num = getaddrinfo( + host_name_.c_str(), port_num_.c_str(), &hints, &servinfo); + if (error_num != 0) { + GTEST_LOG_(WARNING) << "stream_result_to: getaddrinfo() failed: " + << gai_strerror(error_num); + } + + // Loop through all the results and connect to the first we can. + for (addrinfo* cur_addr = servinfo; sockfd_ == -1 && cur_addr != NULL; + cur_addr = cur_addr->ai_next) { + sockfd_ = socket( + cur_addr->ai_family, cur_addr->ai_socktype, cur_addr->ai_protocol); + if (sockfd_ != -1) { + // Connect the client socket to the server socket. + if (connect(sockfd_, cur_addr->ai_addr, cur_addr->ai_addrlen) == -1) { + close(sockfd_); + sockfd_ = -1; + } + } + } + + freeaddrinfo(servinfo); // all done with this structure + + if (sockfd_ == -1) { + GTEST_LOG_(WARNING) << "stream_result_to: failed to connect to " + << host_name_ << ":" << port_num_; + } +} + +// End of class Streaming Listener +#endif // GTEST_CAN_STREAM_RESULTS__ + // Class ScopedTrace // Pushes the given source file location and message onto a per-thread @@ -3512,19 +3711,6 @@ Environment* UnitTest::AddEnvironment(Environment* env) { return env; } -#if GTEST_HAS_EXCEPTIONS -// A failed Google Test assertion will throw an exception of this type -// when exceptions are enabled. We derive it from std::runtime_error, -// which is for errors presumably detectable only at run time. Since -// std::runtime_error inherits from std::exception, many testing -// frameworks know how to extract and print the message inside it. -class GoogleTestFailureException : public ::std::runtime_error { - public: - explicit GoogleTestFailureException(const TestPartResult& failure) - : ::std::runtime_error(PrintTestPartResultToString(failure).c_str()) {} -}; -#endif - // Adds a TestPartResult to the current TestResult object. All Google Test // assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) eventually call // this to report their results. The user code should use the @@ -3601,31 +3787,34 @@ void UnitTest::RecordPropertyForCurrentTest(const char* key, // We don't protect this under mutex_, as we only support calling it // from the main thread. int UnitTest::Run() { -#if GTEST_HAS_SEH - // Catch SEH-style exceptions. + // Captures the value of GTEST_FLAG(catch_exceptions). This value will be + // used for the duration of the program. + impl()->set_catch_exceptions(GTEST_FLAG(catch_exceptions)); +#if GTEST_HAS_SEH const bool in_death_test_child_process = internal::GTEST_FLAG(internal_run_death_test).length() > 0; // Either the user wants Google Test to catch exceptions thrown by the // tests or this is executing in the context of death test child // process. In either case the user does not want to see pop-up dialogs - // about crashes - they are expected.. - if (GTEST_FLAG(catch_exceptions) || in_death_test_child_process) { -#if !GTEST_OS_WINDOWS_MOBILE + // about crashes - they are expected. + if (impl()->catch_exceptions() || in_death_test_child_process) { + +# if !GTEST_OS_WINDOWS_MOBILE // SetErrorMode doesn't exist on CE. SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); -#endif // !GTEST_OS_WINDOWS_MOBILE +# endif // !GTEST_OS_WINDOWS_MOBILE -#if (defined(_MSC_VER) || GTEST_OS_WINDOWS_MINGW) && !GTEST_OS_WINDOWS_MOBILE +# if (defined(_MSC_VER) || GTEST_OS_WINDOWS_MINGW) && !GTEST_OS_WINDOWS_MOBILE // Death test children can be terminated with _abort(). On Windows, // _abort() can show a dialog with a warning message. This forces the // abort message to go to stderr instead. _set_error_mode(_OUT_TO_STDERR); -#endif +# endif -#if _MSC_VER >= 1400 && !GTEST_OS_WINDOWS_MOBILE +# if _MSC_VER >= 1400 && !GTEST_OS_WINDOWS_MOBILE // In the debug version, Visual Studio pops up a separate dialog // offering a choice to debug the aborted program. We need to suppress // this dialog or it will pop up for every EXPECT/ASSERT_DEATH statement @@ -3641,22 +3830,15 @@ int UnitTest::Run() { _set_abort_behavior( 0x0, // Clear the following flags: _WRITE_ABORT_MSG | _CALL_REPORTFAULT); // pop-up window, core dump. -#endif - } +# endif - __try { - return impl_->RunAllTests(); - } __except(internal::UnitTestOptions::GTestShouldProcessSEH( - GetExceptionCode())) { - printf("Exception thrown with code 0x%x.\nFAIL\n", GetExceptionCode()); - fflush(stdout); - return 1; } - -#else // We are on a compiler or platform that doesn't support SEH. - - return impl_->RunAllTests(); #endif // GTEST_HAS_SEH + + return internal::HandleExceptionsInMethodIfSupported( + impl(), + &internal::UnitTestImpl::RunAllTests, + "auxiliary test code (environments or event listeners)") ? 0 : 1; } // Returns the working directory when the first TEST() or TEST_F() was @@ -3724,12 +3906,12 @@ namespace internal { UnitTestImpl::UnitTestImpl(UnitTest* parent) : parent_(parent), #ifdef _MSC_VER -#pragma warning(push) // Saves the current warning state. -#pragma warning(disable:4355) // Temporarily disables warning 4355 +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4355) // Temporarily disables warning 4355 // (using this in initializer). default_global_test_part_result_reporter_(this), default_per_thread_test_part_result_reporter_(this), -#pragma warning(pop) // Restores the warning state again. +# pragma warning(pop) // Restores the warning state again. #else default_global_test_part_result_reporter_(this), default_per_thread_test_part_result_reporter_(this), @@ -3750,13 +3932,13 @@ UnitTestImpl::UnitTestImpl(UnitTest* parent) post_flag_parse_init_performed_(false), random_seed_(0), // Will be overridden by the flag before first use. random_(0), // Will be reseeded before first use. -#if GTEST_HAS_DEATH_TEST elapsed_time_(0), +#if GTEST_HAS_DEATH_TEST internal_run_death_test_flag_(NULL), - death_test_factory_(new DefaultDeathTestFactory) { -#else - elapsed_time_(0) { -#endif // GTEST_HAS_DEATH_TEST + death_test_factory_(new DefaultDeathTestFactory), +#endif + // Will be overridden by the flag before first use. + catch_exceptions_(false) { listeners()->SetDefaultResultPrinter(new PrettyUnitTestResultPrinter); } @@ -3793,6 +3975,25 @@ void UnitTestImpl::ConfigureXmlOutput() { } } +#if GTEST_CAN_STREAM_RESULTS_ +// Initializes event listeners for streaming test results in String form. +// Must not be called before InitGoogleTest. +void UnitTestImpl::ConfigureStreamingOutput() { + const string& target = GTEST_FLAG(stream_result_to); + if (!target.empty()) { + const size_t pos = target.find(':'); + if (pos != string::npos) { + listeners()->Append(new StreamingListener(target.substr(0, pos), + target.substr(pos+1))); + } else { + printf("WARNING: unrecognized streaming target \"%s\" ignored.\n", + target.c_str()); + fflush(stdout); + } + } +} +#endif // GTEST_CAN_STREAM_RESULTS_ + // Performs initialization dependent upon flag values obtained in // ParseGoogleTestFlagsOnly. Is called from InitGoogleTest after the call to // ParseGoogleTestFlagsOnly. In case a user neglects to call InitGoogleTest @@ -3816,6 +4017,11 @@ void UnitTestImpl::PostFlagParsingInit() { // Configures listeners for XML output. This makes it possible for users // to shut down the default XML output before invoking RUN_ALL_TESTS. ConfigureXmlOutput(); + +#if GTEST_CAN_STREAM_RESULTS_ + // Configures listeners for streaming test results to the specified server. + ConfigureStreamingOutput(); +#endif // GTEST_CAN_STREAM_RESULTS_ } } @@ -3850,10 +4056,12 @@ class TestCaseNameIs { // Arguments: // // test_case_name: name of the test case +// type_param: the name of the test case's type parameter, or NULL if +// this is not a typed or a type-parameterized test case. // set_up_tc: pointer to the function that sets up the test case // tear_down_tc: pointer to the function that tears down the test case TestCase* UnitTestImpl::GetTestCase(const char* test_case_name, - const char* comment, + const char* type_param, Test::SetUpTestCaseFunc set_up_tc, Test::TearDownTestCaseFunc tear_down_tc) { // Can we find a TestCase with the given name? @@ -3866,7 +4074,7 @@ TestCase* UnitTestImpl::GetTestCase(const char* test_case_name, // No. Let's create one. TestCase* const new_test_case = - new TestCase(test_case_name, comment, set_up_tc, tear_down_tc); + new TestCase(test_case_name, type_param, set_up_tc, tear_down_tc); // Is this a death test case? if (internal::UnitTestOptions::MatchesFilter(String(test_case_name), @@ -3893,27 +4101,26 @@ static void SetUpEnvironment(Environment* env) { env->SetUp(); } static void TearDownEnvironment(Environment* env) { env->TearDown(); } // Runs all tests in this UnitTest object, prints the result, and -// returns 0 if all tests are successful, or 1 otherwise. If any -// exception is thrown during a test on Windows, this test is -// considered to be failed, but the rest of the tests will still be -// run. (We disable exceptions on Linux and Mac OS X, so the issue -// doesn't apply there.) +// returns true if all tests are successful. If any exception is +// thrown during a test, the test is considered to be failed, but the +// rest of the tests will still be run. +// // When parameterized tests are enabled, it expands and registers // parameterized tests first in RegisterParameterizedTests(). // All other functions called from RunAllTests() may safely assume that // parameterized tests are ready to be counted and run. -int UnitTestImpl::RunAllTests() { +bool UnitTestImpl::RunAllTests() { // Makes sure InitGoogleTest() was called. if (!GTestIsInitialized()) { printf("%s", "\nThis test program did NOT call ::testing::InitGoogleTest " "before calling RUN_ALL_TESTS(). Please fix it.\n"); - return 1; + return false; } // Do not run any test if the --help flag was specified. if (g_help_flag) - return 0; + return true; // Repeats the call to the post-flag parsing initialization in case the // user didn't call InitGoogleTest. @@ -3945,7 +4152,7 @@ int UnitTestImpl::RunAllTests() { if (GTEST_FLAG(list_tests)) { // This must be called *after* FilterTests() has been called. ListTestsMatchingFilter(); - return 0; + return true; } random_seed_ = GTEST_FLAG(shuffle) ? @@ -3964,7 +4171,9 @@ int UnitTestImpl::RunAllTests() { // Repeats forever if the repeat count is negative. const bool forever = repeat < 0; for (int i = 0; forever || i != repeat; i++) { - ClearResult(); + // We want to preserve failures generated by ad-hoc test + // assertions executed before RUN_ALL_TESTS(). + ClearNonAdHocTestResult(); const TimeInMillis start = GetTimeInMillis(); @@ -4029,8 +4238,7 @@ int UnitTestImpl::RunAllTests() { repeater->OnTestProgramEnd(*parent_); - // Returns 0 if all tests passed, or 1 other wise. - return failed ? 1 : 0; + return !failed; } // Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file @@ -4104,7 +4312,7 @@ bool ShouldShard(const char* total_shards_env, // Parses the environment variable var as an Int32. If it is unset, // returns default_val. If it is not an Int32, prints an error // and aborts. -Int32 Int32FromEnvOrDie(const char* const var, Int32 default_val) { +Int32 Int32FromEnvOrDie(const char* var, Int32 default_val) { const char* str_val = posix::GetEnv(var); if (str_val == NULL) { return default_val; @@ -4160,12 +4368,12 @@ int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) { kDisableTestFilter) || internal::UnitTestOptions::MatchesFilter(test_name, kDisableTestFilter); - test_info->impl()->set_is_disabled(is_disabled); + test_info->is_disabled_ = is_disabled; const bool matches_filter = internal::UnitTestOptions::FilterMatchesTest(test_case_name, test_name); - test_info->impl()->set_matches_filter(matches_filter); + test_info->matches_filter_ = matches_filter; const bool is_runnable = (GTEST_FLAG(also_run_disabled_tests) || !is_disabled) && @@ -4179,7 +4387,7 @@ int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) { num_runnable_tests += is_runnable; num_selected_tests += is_selected; - test_info->impl()->set_should_run(is_selected); + test_info->should_run_ = is_selected; test_case->set_should_run(test_case->should_run() || is_selected); } } @@ -4195,7 +4403,7 @@ void UnitTestImpl::ListTestsMatchingFilter() { for (size_t j = 0; j < test_case->test_info_list().size(); j++) { const TestInfo* const test_info = test_case->test_info_list()[j]; - if (test_info->matches_filter()) { + if (test_info->matches_filter_) { if (!printed_test_case_name) { printed_test_case_name = true; printf("%s.\n", test_case->name()); @@ -4235,7 +4443,7 @@ OsStackTraceGetterInterface* UnitTestImpl::os_stack_trace_getter() { // the TestResult for the ad hoc test if no test is running. TestResult* UnitTestImpl::current_test_result() { return current_test_info_ ? - current_test_info_->impl()->result() : &ad_hoc_test_result_; + &(current_test_info_->result_) : &ad_hoc_test_result_; } // Shuffles all test cases, and the tests within each test case, @@ -4264,32 +4472,6 @@ void UnitTestImpl::UnshuffleTests() { } } -// TestInfoImpl constructor. The new instance assumes ownership of the test -// factory object. -TestInfoImpl::TestInfoImpl(TestInfo* parent, - const char* a_test_case_name, - const char* a_name, - const char* a_test_case_comment, - const char* a_comment, - TypeId a_fixture_class_id, - internal::TestFactoryBase* factory) : - parent_(parent), - test_case_name_(String(a_test_case_name)), - name_(String(a_name)), - test_case_comment_(String(a_test_case_comment)), - comment_(String(a_comment)), - fixture_class_id_(a_fixture_class_id), - should_run_(false), - is_disabled_(false), - matches_filter_(false), - factory_(factory) { -} - -// TestInfoImpl destructor. -TestInfoImpl::~TestInfoImpl() { - delete factory_; -} - // Returns the current OS stack trace as a String. // // The maximum number of stack frames to be included is specified by @@ -4307,8 +4489,8 @@ String GetCurrentOsStackTraceExceptTop(UnitTest* /*unit_test*/, return GetUnitTestImpl()->CurrentOsStackTraceExceptTop(skip_count + 1); } -// Used by the GTEST_HIDE_UNREACHABLE_CODE_ macro to suppress unreachable -// code warnings. +// Used by the GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_ macro to +// suppress unreachable code warnings. namespace { class ClassUniqueToAlwaysTrue {}; } @@ -4520,6 +4702,10 @@ static const char kColorEncodedHelpMessage[] = GTEST_PATH_SEP_ "@Y|@G:@YFILE_PATH]@D\n" " Generate an XML report in the given directory or with the given file\n" " name. @YFILE_PATH@D defaults to @Gtest_details.xml@D.\n" +#if GTEST_CAN_STREAM_RESULTS_ +" @G--" GTEST_FLAG_PREFIX_ "stream_result_to=@YHOST@G:@YPORT@D\n" +" Stream test results to the given server.\n" +#endif // GTEST_CAN_STREAM_RESULTS_ "\n" "Assertion Behavior:\n" #if GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS @@ -4530,10 +4716,9 @@ static const char kColorEncodedHelpMessage[] = " Turn assertion failures into debugger break-points.\n" " @G--" GTEST_FLAG_PREFIX_ "throw_on_failure@D\n" " Turn assertion failures into C++ exceptions.\n" -#if GTEST_OS_WINDOWS -" @G--" GTEST_FLAG_PREFIX_ "catch_exceptions@D\n" -" Suppress pop-ups caused by exceptions.\n" -#endif // GTEST_OS_WINDOWS +" @G--" GTEST_FLAG_PREFIX_ "catch_exceptions=0@D\n" +" Do not report exceptions as test failures. Instead, allow them\n" +" to crash the program or throw a pop-up (on Windows).\n" "\n" "Except for @G--" GTEST_FLAG_PREFIX_ "list_tests@D, you can alternatively set " "the corresponding\n" @@ -4583,7 +4768,10 @@ void ParseGoogleTestFlagsOnlyImpl(int* argc, CharType** argv) { ParseBoolFlag(arg, kShuffleFlag, >EST_FLAG(shuffle)) || ParseInt32Flag(arg, kStackTraceDepthFlag, >EST_FLAG(stack_trace_depth)) || - ParseBoolFlag(arg, kThrowOnFailureFlag, >EST_FLAG(throw_on_failure)) + ParseStringFlag(arg, kStreamResultToFlag, + >EST_FLAG(stream_result_to)) || + ParseBoolFlag(arg, kThrowOnFailureFlag, + >EST_FLAG(throw_on_failure)) ) { // Yes. Shift the remainder of the argv list left by one. Note // that argv has (*argc + 1) elements, the last one always being @@ -4641,10 +4829,12 @@ void InitGoogleTestImpl(int* argc, CharType** argv) { internal::g_executable_path = internal::StreamableToString(argv[0]); #if GTEST_HAS_DEATH_TEST + g_argvs.clear(); for (int i = 0; i != *argc; i++) { g_argvs.push_back(StreamableToString(argv[i])); } + #endif // GTEST_HAS_DEATH_TEST ParseGoogleTestFlagsOnly(argc, argv); diff --git a/utils/unittest/googletest/include/gtest/gtest-death-test.h b/utils/unittest/googletest/include/gtest/gtest-death-test.h index 121dc1f..a27883f 100644 --- a/utils/unittest/googletest/include/gtest/gtest-death-test.h +++ b/utils/unittest/googletest/include/gtest/gtest-death-test.h @@ -38,7 +38,7 @@ #ifndef GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ #define GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ -#include <gtest/internal/gtest-death-test-internal.h> +#include "gtest/internal/gtest-death-test-internal.h" namespace testing { @@ -154,24 +154,24 @@ GTEST_DECLARE_string_(death_test_style); // Asserts that a given statement causes the program to exit, with an // integer exit status that satisfies predicate, and emitting error output // that matches regex. -#define ASSERT_EXIT(statement, predicate, regex) \ - GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_FATAL_FAILURE_) +# define ASSERT_EXIT(statement, predicate, regex) \ + GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_FATAL_FAILURE_) // Like ASSERT_EXIT, but continues on to successive tests in the // test case, if any: -#define EXPECT_EXIT(statement, predicate, regex) \ - GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_NONFATAL_FAILURE_) +# define EXPECT_EXIT(statement, predicate, regex) \ + GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_NONFATAL_FAILURE_) // Asserts that a given statement causes the program to exit, either by // explicitly exiting with a nonzero exit code or being killed by a // signal, and emitting error output that matches regex. -#define ASSERT_DEATH(statement, regex) \ - ASSERT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex) +# define ASSERT_DEATH(statement, regex) \ + ASSERT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex) // Like ASSERT_DEATH, but continues on to successive tests in the // test case, if any: -#define EXPECT_DEATH(statement, regex) \ - EXPECT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex) +# define EXPECT_DEATH(statement, regex) \ + EXPECT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex) // Two predicate classes that can be used in {ASSERT,EXPECT}_EXIT*: @@ -187,7 +187,7 @@ class GTEST_API_ ExitedWithCode { const int exit_code_; }; -#if !GTEST_OS_WINDOWS +# if !GTEST_OS_WINDOWS // Tests that an exit code describes an exit due to termination by a // given signal. class GTEST_API_ KilledBySignal { @@ -197,7 +197,7 @@ class GTEST_API_ KilledBySignal { private: const int signum_; }; -#endif // !GTEST_OS_WINDOWS +# endif // !GTEST_OS_WINDOWS // EXPECT_DEBUG_DEATH asserts that the given statements die in debug mode. // The death testing framework causes this to have interesting semantics, @@ -242,23 +242,23 @@ class GTEST_API_ KilledBySignal { // EXPECT_EQ(12, DieInDebugOr12(&sideeffect)); // }, "death"); // -#ifdef NDEBUG +# ifdef NDEBUG -#define EXPECT_DEBUG_DEATH(statement, regex) \ +# define EXPECT_DEBUG_DEATH(statement, regex) \ do { statement; } while (::testing::internal::AlwaysFalse()) -#define ASSERT_DEBUG_DEATH(statement, regex) \ +# define ASSERT_DEBUG_DEATH(statement, regex) \ do { statement; } while (::testing::internal::AlwaysFalse()) -#else +# else -#define EXPECT_DEBUG_DEATH(statement, regex) \ +# define EXPECT_DEBUG_DEATH(statement, regex) \ EXPECT_DEATH(statement, regex) -#define ASSERT_DEBUG_DEATH(statement, regex) \ +# define ASSERT_DEBUG_DEATH(statement, regex) \ ASSERT_DEATH(statement, regex) -#endif // NDEBUG for EXPECT_DEBUG_DEATH +# endif // NDEBUG for EXPECT_DEBUG_DEATH #endif // GTEST_HAS_DEATH_TEST // EXPECT_DEATH_IF_SUPPORTED(statement, regex) and @@ -267,14 +267,14 @@ class GTEST_API_ KilledBySignal { // useful when you are combining death test assertions with normal test // assertions in one test. #if GTEST_HAS_DEATH_TEST -#define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ +# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ EXPECT_DEATH(statement, regex) -#define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \ +# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \ ASSERT_DEATH(statement, regex) #else -#define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ +# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, ) -#define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \ +# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \ GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, return) #endif diff --git a/utils/unittest/googletest/include/gtest/gtest-message.h b/utils/unittest/googletest/include/gtest/gtest-message.h index f135b69..9b7142f 100644 --- a/utils/unittest/googletest/include/gtest/gtest-message.h +++ b/utils/unittest/googletest/include/gtest/gtest-message.h @@ -48,8 +48,8 @@ #include <limits> -#include <gtest/internal/gtest-string.h> -#include <gtest/internal/gtest-internal.h> +#include "gtest/internal/gtest-string.h" +#include "gtest/internal/gtest-internal.h" namespace testing { @@ -58,7 +58,7 @@ namespace testing { // Typical usage: // // 1. You stream a bunch of values to a Message object. -// It will remember the text in a StrStream. +// It will remember the text in a stringstream. // 2. Then you stream the Message object to an ostream. // This causes the text in the Message to be streamed // to the ostream. @@ -74,7 +74,7 @@ namespace testing { // Message is not intended to be inherited from. In particular, its // destructor is not virtual. // -// Note that StrStream behaves differently in gcc and in MSVC. You +// Note that stringstream behaves differently in gcc and in MSVC. You // can stream a NULL char pointer to it in the former, but not in the // latter (it causes an access violation if you do). The Message // class hides this difference by treating a NULL char pointer as @@ -87,27 +87,26 @@ class GTEST_API_ Message { public: // Constructs an empty Message. - // We allocate the StrStream separately because it otherwise each use of + // We allocate the stringstream separately because otherwise each use of // ASSERT/EXPECT in a procedure adds over 200 bytes to the procedure's // stack frame leading to huge stack frames in some cases; gcc does not reuse // the stack space. - Message() : ss_(new internal::StrStream) { + Message() : ss_(new ::std::stringstream) { // By default, we want there to be enough precision when printing // a double to a Message. *ss_ << std::setprecision(std::numeric_limits<double>::digits10 + 2); } // Copy constructor. - Message(const Message& msg) : ss_(new internal::StrStream) { // NOLINT + Message(const Message& msg) : ss_(new ::std::stringstream) { // NOLINT *ss_ << msg.GetString(); } // Constructs a Message from a C-string. - explicit Message(const char* str) : ss_(new internal::StrStream) { + explicit Message(const char* str) : ss_(new ::std::stringstream) { *ss_ << str; } - ~Message() { delete ss_; } #if GTEST_OS_SYMBIAN // Streams a value (either a pointer or not) to this object. template <typename T> @@ -119,7 +118,7 @@ class GTEST_API_ Message { // Streams a non-pointer value to this object. template <typename T> inline Message& operator <<(const T& val) { - ::GTestStreamToHelper(ss_, val); + ::GTestStreamToHelper(ss_.get(), val); return *this; } @@ -141,7 +140,7 @@ class GTEST_API_ Message { if (pointer == NULL) { *ss_ << "(null)"; } else { - ::GTestStreamToHelper(ss_, pointer); + ::GTestStreamToHelper(ss_.get(), pointer); } return *this; } @@ -189,10 +188,11 @@ class GTEST_API_ Message { // // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. internal::String GetString() const { - return internal::StrStreamToString(ss_); + return internal::StringStreamToString(ss_.get()); } private: + #if GTEST_OS_SYMBIAN // These are needed as the Nokia Symbian Compiler cannot decide between // const T& and const T* in a function template. The Nokia compiler _can_ @@ -203,17 +203,17 @@ class GTEST_API_ Message { if (pointer == NULL) { *ss_ << "(null)"; } else { - ::GTestStreamToHelper(ss_, pointer); + ::GTestStreamToHelper(ss_.get(), pointer); } } template <typename T> inline void StreamHelper(internal::false_type /*dummy*/, const T& value) { - ::GTestStreamToHelper(ss_, value); + ::GTestStreamToHelper(ss_.get(), value); } #endif // GTEST_OS_SYMBIAN // We'll hold the text streamed to this object here. - internal::StrStream* const ss_; + const internal::scoped_ptr< ::std::stringstream> ss_; // We declare (but don't implement) this to prevent the compiler // from implementing the assignment operator. diff --git a/utils/unittest/googletest/include/gtest/gtest-param-test.h b/utils/unittest/googletest/include/gtest/gtest-param-test.h index 3184d07..b1f8bb9 100644 --- a/utils/unittest/googletest/include/gtest/gtest-param-test.h +++ b/utils/unittest/googletest/include/gtest/gtest-param-test.h @@ -1,4 +1,6 @@ -// This file was GENERATED by a script. DO NOT EDIT BY HAND!!! +// This file was GENERATED by command: +// pump.py gtest-param-test.h.pump +// DO NOT EDIT BY HAND!!! // Copyright 2008, Google Inc. // All rights reserved. @@ -48,10 +50,12 @@ #if 0 // To write value-parameterized tests, first you should define a fixture -// class. It must be derived from testing::TestWithParam<T>, where T is -// the type of your parameter values. TestWithParam<T> is itself derived -// from testing::Test. T can be any copyable type. If it's a raw pointer, -// you are responsible for managing the lifespan of the pointed values. +// class. It is usually derived from testing::TestWithParam<T> (see below for +// another inheritance scheme that's sometimes useful in more complicated +// class hierarchies), where the type of your parameter values. +// TestWithParam<T> is itself derived from testing::Test. T can be any +// copyable type. If it's a raw pointer, you are responsible for managing the +// lifespan of the pointed values. class FooTest : public ::testing::TestWithParam<const char*> { // You can implement all the usual class fixture members here. @@ -146,20 +150,46 @@ INSTANTIATE_TEST_CASE_P(AnotherInstantiationName, FooTest, ValuesIn(pets)); // In the future, we plan to publish the API for defining new parameter // generators. But for now this interface remains part of the internal // implementation and is subject to change. +// +// +// A parameterized test fixture must be derived from testing::Test and from +// testing::WithParamInterface<T>, where T is the type of the parameter +// values. Inheriting from TestWithParam<T> satisfies that requirement because +// TestWithParam<T> inherits from both Test and WithParamInterface. In more +// complicated hierarchies, however, it is occasionally useful to inherit +// separately from Test and WithParamInterface. For example: + +class BaseTest : public ::testing::Test { + // You can inherit all the usual members for a non-parameterized test + // fixture here. +}; + +class DerivedTest : public BaseTest, public ::testing::WithParamInterface<int> { + // The usual test fixture members go here too. +}; + +TEST_F(BaseTest, HasFoo) { + // This is an ordinary non-parameterized test. +} + +TEST_P(DerivedTest, DoesBlah) { + // GetParam works just the same here as if you inherit from TestWithParam. + EXPECT_TRUE(foo.Blah(GetParam())); +} #endif // 0 -#include <gtest/internal/gtest-port.h> +#include "gtest/internal/gtest-port.h" #if !GTEST_OS_SYMBIAN -#include <utility> +# include <utility> #endif // scripts/fuse_gtest.py depends on gtest's own header being #included // *unconditionally*. Therefore these #includes cannot be moved // inside #if GTEST_HAS_PARAM_TEST. -#include <gtest/internal/gtest-internal.h> -#include <gtest/internal/gtest-param-util.h> +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-param-util.h" #if GTEST_HAS_PARAM_TEST @@ -275,11 +305,10 @@ internal::ParamGenerator<T> Range(T start, T end) { // template <typename ForwardIterator> internal::ParamGenerator< - typename ::std::iterator_traits<ForwardIterator>::value_type> ValuesIn( - ForwardIterator begin, - ForwardIterator end) { - typedef typename ::std::iterator_traits<ForwardIterator>::value_type - ParamType; + typename ::testing::internal::IteratorTraits<ForwardIterator>::value_type> +ValuesIn(ForwardIterator begin, ForwardIterator end) { + typedef typename ::testing::internal::IteratorTraits<ForwardIterator> + ::value_type ParamType; return internal::ParamGenerator<ParamType>( new internal::ValuesInIteratorRangeGenerator<ParamType>(begin, end)); } @@ -1197,7 +1226,7 @@ inline internal::ParamGenerator<bool> Bool() { return Values(false, true); } -#if GTEST_HAS_COMBINE +# if GTEST_HAS_COMBINE // Combine() allows the user to combine two or more sequences to produce // values of a Cartesian product of those sequences' elements. // @@ -1349,11 +1378,11 @@ internal::CartesianProductHolder10<Generator1, Generator2, Generator3, Generator10>( g1, g2, g3, g4, g5, g6, g7, g8, g9, g10); } -#endif // GTEST_HAS_COMBINE +# endif // GTEST_HAS_COMBINE -#define TEST_P(test_case_name, test_name) \ +# define TEST_P(test_case_name, test_name) \ class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \ : public test_case_name { \ public: \ @@ -1379,7 +1408,7 @@ internal::CartesianProductHolder10<Generator1, Generator2, Generator3, GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::AddToRegistry(); \ void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody() -#define INSTANTIATE_TEST_CASE_P(prefix, test_case_name, generator) \ +# define INSTANTIATE_TEST_CASE_P(prefix, test_case_name, generator) \ ::testing::internal::ParamGenerator<test_case_name::ParamType> \ gtest_##prefix##test_case_name##_EvalGenerator_() { return generator; } \ int gtest_##prefix##test_case_name##_dummy_ = \ diff --git a/utils/unittest/googletest/include/gtest/gtest-printers.h b/utils/unittest/googletest/include/gtest/gtest-printers.h new file mode 100644 index 0000000..9cbab3f --- /dev/null +++ b/utils/unittest/googletest/include/gtest/gtest-printers.h @@ -0,0 +1,796 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Test - The Google C++ Testing Framework +// +// This file implements a universal value printer that can print a +// value of any type T: +// +// void ::testing::internal::UniversalPrinter<T>::Print(value, ostream_ptr); +// +// A user can teach this function how to print a class type T by +// defining either operator<<() or PrintTo() in the namespace that +// defines T. More specifically, the FIRST defined function in the +// following list will be used (assuming T is defined in namespace +// foo): +// +// 1. foo::PrintTo(const T&, ostream*) +// 2. operator<<(ostream&, const T&) defined in either foo or the +// global namespace. +// +// If none of the above is defined, it will print the debug string of +// the value if it is a protocol buffer, or print the raw bytes in the +// value otherwise. +// +// To aid debugging: when T is a reference type, the address of the +// value is also printed; when T is a (const) char pointer, both the +// pointer value and the NUL-terminated string it points to are +// printed. +// +// We also provide some convenient wrappers: +// +// // Prints a value to a string. For a (const or not) char +// // pointer, the NUL-terminated string (but not the pointer) is +// // printed. +// std::string ::testing::PrintToString(const T& value); +// +// // Prints a value tersely: for a reference type, the referenced +// // value (but not the address) is printed; for a (const or not) char +// // pointer, the NUL-terminated string (but not the pointer) is +// // printed. +// void ::testing::internal::UniversalTersePrint(const T& value, ostream*); +// +// // Prints value using the type inferred by the compiler. The difference +// // from UniversalTersePrint() is that this function prints both the +// // pointer and the NUL-terminated string for a (const or not) char pointer. +// void ::testing::internal::UniversalPrint(const T& value, ostream*); +// +// // Prints the fields of a tuple tersely to a string vector, one +// // element for each field. Tuple support must be enabled in +// // gtest-port.h. +// std::vector<string> UniversalTersePrintTupleFieldsToStrings( +// const Tuple& value); +// +// Known limitation: +// +// The print primitives print the elements of an STL-style container +// using the compiler-inferred type of *iter where iter is a +// const_iterator of the container. When const_iterator is an input +// iterator but not a forward iterator, this inferred type may not +// match value_type, and the print output may be incorrect. In +// practice, this is rarely a problem as for most containers +// const_iterator is a forward iterator. We'll fix this if there's an +// actual need for it. Note that this fix cannot rely on value_type +// being defined as many user-defined container types don't have +// value_type. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ +#define GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ + +#include <ostream> // NOLINT +#include <sstream> +#include <string> +#include <utility> +#include <vector> +#include "gtest/internal/gtest-port.h" +#include "gtest/internal/gtest-internal.h" + +namespace testing { + +// Definitions in the 'internal' and 'internal2' name spaces are +// subject to change without notice. DO NOT USE THEM IN USER CODE! +namespace internal2 { + +// Prints the given number of bytes in the given object to the given +// ostream. +GTEST_API_ void PrintBytesInObjectTo(const unsigned char* obj_bytes, + size_t count, + ::std::ostream* os); + +// For selecting which printer to use when a given type has neither << +// nor PrintTo(). +enum TypeKind { + kProtobuf, // a protobuf type + kConvertibleToInteger, // a type implicitly convertible to BiggestInt + // (e.g. a named or unnamed enum type) + kOtherType // anything else +}; + +// TypeWithoutFormatter<T, kTypeKind>::PrintValue(value, os) is called +// by the universal printer to print a value of type T when neither +// operator<< nor PrintTo() is defined for T, where kTypeKind is the +// "kind" of T as defined by enum TypeKind. +template <typename T, TypeKind kTypeKind> +class TypeWithoutFormatter { + public: + // This default version is called when kTypeKind is kOtherType. + static void PrintValue(const T& value, ::std::ostream* os) { + PrintBytesInObjectTo(reinterpret_cast<const unsigned char*>(&value), + sizeof(value), os); + } +}; + +// We print a protobuf using its ShortDebugString() when the string +// doesn't exceed this many characters; otherwise we print it using +// DebugString() for better readability. +const size_t kProtobufOneLinerMaxLength = 50; + +template <typename T> +class TypeWithoutFormatter<T, kProtobuf> { + public: + static void PrintValue(const T& value, ::std::ostream* os) { + const ::testing::internal::string short_str = value.ShortDebugString(); + const ::testing::internal::string pretty_str = + short_str.length() <= kProtobufOneLinerMaxLength ? + short_str : ("\n" + value.DebugString()); + *os << ("<" + pretty_str + ">"); + } +}; + +template <typename T> +class TypeWithoutFormatter<T, kConvertibleToInteger> { + public: + // Since T has no << operator or PrintTo() but can be implicitly + // converted to BiggestInt, we print it as a BiggestInt. + // + // Most likely T is an enum type (either named or unnamed), in which + // case printing it as an integer is the desired behavior. In case + // T is not an enum, printing it as an integer is the best we can do + // given that it has no user-defined printer. + static void PrintValue(const T& value, ::std::ostream* os) { + const internal::BiggestInt kBigInt = value; + *os << kBigInt; + } +}; + +// Prints the given value to the given ostream. If the value is a +// protocol message, its debug string is printed; if it's an enum or +// of a type implicitly convertible to BiggestInt, it's printed as an +// integer; otherwise the bytes in the value are printed. This is +// what UniversalPrinter<T>::Print() does when it knows nothing about +// type T and T has neither << operator nor PrintTo(). +// +// A user can override this behavior for a class type Foo by defining +// a << operator in the namespace where Foo is defined. +// +// We put this operator in namespace 'internal2' instead of 'internal' +// to simplify the implementation, as much code in 'internal' needs to +// use << in STL, which would conflict with our own << were it defined +// in 'internal'. +// +// Note that this operator<< takes a generic std::basic_ostream<Char, +// CharTraits> type instead of the more restricted std::ostream. If +// we define it to take an std::ostream instead, we'll get an +// "ambiguous overloads" compiler error when trying to print a type +// Foo that supports streaming to std::basic_ostream<Char, +// CharTraits>, as the compiler cannot tell whether +// operator<<(std::ostream&, const T&) or +// operator<<(std::basic_stream<Char, CharTraits>, const Foo&) is more +// specific. +template <typename Char, typename CharTraits, typename T> +::std::basic_ostream<Char, CharTraits>& operator<<( + ::std::basic_ostream<Char, CharTraits>& os, const T& x) { + TypeWithoutFormatter<T, + (internal::IsAProtocolMessage<T>::value ? kProtobuf : + internal::ImplicitlyConvertible<const T&, internal::BiggestInt>::value ? + kConvertibleToInteger : kOtherType)>::PrintValue(x, &os); + return os; +} + +} // namespace internal2 +} // namespace testing + +// This namespace MUST NOT BE NESTED IN ::testing, or the name look-up +// magic needed for implementing UniversalPrinter won't work. +namespace testing_internal { + +// Used to print a value that is not an STL-style container when the +// user doesn't define PrintTo() for it. +template <typename T> +void DefaultPrintNonContainerTo(const T& value, ::std::ostream* os) { + // With the following statement, during unqualified name lookup, + // testing::internal2::operator<< appears as if it was declared in + // the nearest enclosing namespace that contains both + // ::testing_internal and ::testing::internal2, i.e. the global + // namespace. For more details, refer to the C++ Standard section + // 7.3.4-1 [namespace.udir]. This allows us to fall back onto + // testing::internal2::operator<< in case T doesn't come with a << + // operator. + // + // We cannot write 'using ::testing::internal2::operator<<;', which + // gcc 3.3 fails to compile due to a compiler bug. + using namespace ::testing::internal2; // NOLINT + + // Assuming T is defined in namespace foo, in the next statement, + // the compiler will consider all of: + // + // 1. foo::operator<< (thanks to Koenig look-up), + // 2. ::operator<< (as the current namespace is enclosed in ::), + // 3. testing::internal2::operator<< (thanks to the using statement above). + // + // The operator<< whose type matches T best will be picked. + // + // We deliberately allow #2 to be a candidate, as sometimes it's + // impossible to define #1 (e.g. when foo is ::std, defining + // anything in it is undefined behavior unless you are a compiler + // vendor.). + *os << value; +} + +} // namespace testing_internal + +namespace testing { +namespace internal { + +// UniversalPrinter<T>::Print(value, ostream_ptr) prints the given +// value to the given ostream. The caller must ensure that +// 'ostream_ptr' is not NULL, or the behavior is undefined. +// +// We define UniversalPrinter as a class template (as opposed to a +// function template), as we need to partially specialize it for +// reference types, which cannot be done with function templates. +template <typename T> +class UniversalPrinter; + +template <typename T> +void UniversalPrint(const T& value, ::std::ostream* os); + +// Used to print an STL-style container when the user doesn't define +// a PrintTo() for it. +template <typename C> +void DefaultPrintTo(IsContainer /* dummy */, + false_type /* is not a pointer */, + const C& container, ::std::ostream* os) { + const size_t kMaxCount = 32; // The maximum number of elements to print. + *os << '{'; + size_t count = 0; + for (typename C::const_iterator it = container.begin(); + it != container.end(); ++it, ++count) { + if (count > 0) { + *os << ','; + if (count == kMaxCount) { // Enough has been printed. + *os << " ..."; + break; + } + } + *os << ' '; + // We cannot call PrintTo(*it, os) here as PrintTo() doesn't + // handle *it being a native array. + internal::UniversalPrint(*it, os); + } + + if (count > 0) { + *os << ' '; + } + *os << '}'; +} + +// Used to print a pointer that is neither a char pointer nor a member +// pointer, when the user doesn't define PrintTo() for it. (A member +// variable pointer or member function pointer doesn't really point to +// a location in the address space. Their representation is +// implementation-defined. Therefore they will be printed as raw +// bytes.) +template <typename T> +void DefaultPrintTo(IsNotContainer /* dummy */, + true_type /* is a pointer */, + T* p, ::std::ostream* os) { + if (p == NULL) { + *os << "NULL"; + } else { + // C++ doesn't allow casting from a function pointer to any object + // pointer. + // + // IsTrue() silences warnings: "Condition is always true", + // "unreachable code". + if (IsTrue(ImplicitlyConvertible<T*, const void*>::value)) { + // T is not a function type. We just call << to print p, + // relying on ADL to pick up user-defined << for their pointer + // types, if any. + *os << p; + } else { + // T is a function type, so '*os << p' doesn't do what we want + // (it just prints p as bool). We want to print p as a const + // void*. However, we cannot cast it to const void* directly, + // even using reinterpret_cast, as earlier versions of gcc + // (e.g. 3.4.5) cannot compile the cast when p is a function + // pointer. Casting to UInt64 first solves the problem. + *os << reinterpret_cast<const void*>( + reinterpret_cast<internal::UInt64>(p)); + } + } +} + +// Used to print a non-container, non-pointer value when the user +// doesn't define PrintTo() for it. +template <typename T> +void DefaultPrintTo(IsNotContainer /* dummy */, + false_type /* is not a pointer */, + const T& value, ::std::ostream* os) { + ::testing_internal::DefaultPrintNonContainerTo(value, os); +} + +// Prints the given value using the << operator if it has one; +// otherwise prints the bytes in it. This is what +// UniversalPrinter<T>::Print() does when PrintTo() is not specialized +// or overloaded for type T. +// +// A user can override this behavior for a class type Foo by defining +// an overload of PrintTo() in the namespace where Foo is defined. We +// give the user this option as sometimes defining a << operator for +// Foo is not desirable (e.g. the coding style may prevent doing it, +// or there is already a << operator but it doesn't do what the user +// wants). +template <typename T> +void PrintTo(const T& value, ::std::ostream* os) { + // DefaultPrintTo() is overloaded. The type of its first two + // arguments determine which version will be picked. If T is an + // STL-style container, the version for container will be called; if + // T is a pointer, the pointer version will be called; otherwise the + // generic version will be called. + // + // Note that we check for container types here, prior to we check + // for protocol message types in our operator<<. The rationale is: + // + // For protocol messages, we want to give people a chance to + // override Google Mock's format by defining a PrintTo() or + // operator<<. For STL containers, other formats can be + // incompatible with Google Mock's format for the container + // elements; therefore we check for container types here to ensure + // that our format is used. + // + // The second argument of DefaultPrintTo() is needed to bypass a bug + // in Symbian's C++ compiler that prevents it from picking the right + // overload between: + // + // PrintTo(const T& x, ...); + // PrintTo(T* x, ...); + DefaultPrintTo(IsContainerTest<T>(0), is_pointer<T>(), value, os); +} + +// The following list of PrintTo() overloads tells +// UniversalPrinter<T>::Print() how to print standard types (built-in +// types, strings, plain arrays, and pointers). + +// Overloads for various char types. +GTEST_API_ void PrintTo(unsigned char c, ::std::ostream* os); +GTEST_API_ void PrintTo(signed char c, ::std::ostream* os); +inline void PrintTo(char c, ::std::ostream* os) { + // When printing a plain char, we always treat it as unsigned. This + // way, the output won't be affected by whether the compiler thinks + // char is signed or not. + PrintTo(static_cast<unsigned char>(c), os); +} + +// Overloads for other simple built-in types. +inline void PrintTo(bool x, ::std::ostream* os) { + *os << (x ? "true" : "false"); +} + +// Overload for wchar_t type. +// Prints a wchar_t as a symbol if it is printable or as its internal +// code otherwise and also as its decimal code (except for L'\0'). +// The L'\0' char is printed as "L'\\0'". The decimal code is printed +// as signed integer when wchar_t is implemented by the compiler +// as a signed type and is printed as an unsigned integer when wchar_t +// is implemented as an unsigned type. +GTEST_API_ void PrintTo(wchar_t wc, ::std::ostream* os); + +// Overloads for C strings. +GTEST_API_ void PrintTo(const char* s, ::std::ostream* os); +inline void PrintTo(char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_<const char*>(s), os); +} + +// signed/unsigned char is often used for representing binary data, so +// we print pointers to it as void* to be safe. +inline void PrintTo(const signed char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_<const void*>(s), os); +} +inline void PrintTo(signed char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_<const void*>(s), os); +} +inline void PrintTo(const unsigned char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_<const void*>(s), os); +} +inline void PrintTo(unsigned char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_<const void*>(s), os); +} + +// MSVC can be configured to define wchar_t as a typedef of unsigned +// short. It defines _NATIVE_WCHAR_T_DEFINED when wchar_t is a native +// type. When wchar_t is a typedef, defining an overload for const +// wchar_t* would cause unsigned short* be printed as a wide string, +// possibly causing invalid memory accesses. +#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) +// Overloads for wide C strings +GTEST_API_ void PrintTo(const wchar_t* s, ::std::ostream* os); +inline void PrintTo(wchar_t* s, ::std::ostream* os) { + PrintTo(ImplicitCast_<const wchar_t*>(s), os); +} +#endif + +// Overload for C arrays. Multi-dimensional arrays are printed +// properly. + +// Prints the given number of elements in an array, without printing +// the curly braces. +template <typename T> +void PrintRawArrayTo(const T a[], size_t count, ::std::ostream* os) { + UniversalPrint(a[0], os); + for (size_t i = 1; i != count; i++) { + *os << ", "; + UniversalPrint(a[i], os); + } +} + +// Overloads for ::string and ::std::string. +#if GTEST_HAS_GLOBAL_STRING +GTEST_API_ void PrintStringTo(const ::string&s, ::std::ostream* os); +inline void PrintTo(const ::string& s, ::std::ostream* os) { + PrintStringTo(s, os); +} +#endif // GTEST_HAS_GLOBAL_STRING + +GTEST_API_ void PrintStringTo(const ::std::string&s, ::std::ostream* os); +inline void PrintTo(const ::std::string& s, ::std::ostream* os) { + PrintStringTo(s, os); +} + +// Overloads for ::wstring and ::std::wstring. +#if GTEST_HAS_GLOBAL_WSTRING +GTEST_API_ void PrintWideStringTo(const ::wstring&s, ::std::ostream* os); +inline void PrintTo(const ::wstring& s, ::std::ostream* os) { + PrintWideStringTo(s, os); +} +#endif // GTEST_HAS_GLOBAL_WSTRING + +#if GTEST_HAS_STD_WSTRING +GTEST_API_ void PrintWideStringTo(const ::std::wstring&s, ::std::ostream* os); +inline void PrintTo(const ::std::wstring& s, ::std::ostream* os) { + PrintWideStringTo(s, os); +} +#endif // GTEST_HAS_STD_WSTRING + +#if GTEST_HAS_TR1_TUPLE +// Overload for ::std::tr1::tuple. Needed for printing function arguments, +// which are packed as tuples. + +// Helper function for printing a tuple. T must be instantiated with +// a tuple type. +template <typename T> +void PrintTupleTo(const T& t, ::std::ostream* os); + +// Overloaded PrintTo() for tuples of various arities. We support +// tuples of up-to 10 fields. The following implementation works +// regardless of whether tr1::tuple is implemented using the +// non-standard variadic template feature or not. + +inline void PrintTo(const ::std::tr1::tuple<>& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template <typename T1> +void PrintTo(const ::std::tr1::tuple<T1>& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template <typename T1, typename T2> +void PrintTo(const ::std::tr1::tuple<T1, T2>& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template <typename T1, typename T2, typename T3> +void PrintTo(const ::std::tr1::tuple<T1, T2, T3>& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template <typename T1, typename T2, typename T3, typename T4> +void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4>& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5> +void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5>& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6> +void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6>& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7> +void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7>& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8> +void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8>& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9> +void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9>& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10> +void PrintTo( + const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} +#endif // GTEST_HAS_TR1_TUPLE + +// Overload for std::pair. +template <typename T1, typename T2> +void PrintTo(const ::std::pair<T1, T2>& value, ::std::ostream* os) { + *os << '('; + // We cannot use UniversalPrint(value.first, os) here, as T1 may be + // a reference type. The same for printing value.second. + UniversalPrinter<T1>::Print(value.first, os); + *os << ", "; + UniversalPrinter<T2>::Print(value.second, os); + *os << ')'; +} + +// Implements printing a non-reference type T by letting the compiler +// pick the right overload of PrintTo() for T. +template <typename T> +class UniversalPrinter { + public: + // MSVC warns about adding const to a function type, so we want to + // disable the warning. +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4180) // Temporarily disables warning 4180. +#endif // _MSC_VER + + // Note: we deliberately don't call this PrintTo(), as that name + // conflicts with ::testing::internal::PrintTo in the body of the + // function. + static void Print(const T& value, ::std::ostream* os) { + // By default, ::testing::internal::PrintTo() is used for printing + // the value. + // + // Thanks to Koenig look-up, if T is a class and has its own + // PrintTo() function defined in its namespace, that function will + // be visible here. Since it is more specific than the generic ones + // in ::testing::internal, it will be picked by the compiler in the + // following statement - exactly what we want. + PrintTo(value, os); + } + +#ifdef _MSC_VER +# pragma warning(pop) // Restores the warning state. +#endif // _MSC_VER +}; + +// UniversalPrintArray(begin, len, os) prints an array of 'len' +// elements, starting at address 'begin'. +template <typename T> +void UniversalPrintArray(const T* begin, size_t len, ::std::ostream* os) { + if (len == 0) { + *os << "{}"; + } else { + *os << "{ "; + const size_t kThreshold = 18; + const size_t kChunkSize = 8; + // If the array has more than kThreshold elements, we'll have to + // omit some details by printing only the first and the last + // kChunkSize elements. + // TODO(wan@google.com): let the user control the threshold using a flag. + if (len <= kThreshold) { + PrintRawArrayTo(begin, len, os); + } else { + PrintRawArrayTo(begin, kChunkSize, os); + *os << ", ..., "; + PrintRawArrayTo(begin + len - kChunkSize, kChunkSize, os); + } + *os << " }"; + } +} +// This overload prints a (const) char array compactly. +GTEST_API_ void UniversalPrintArray(const char* begin, + size_t len, + ::std::ostream* os); + +// Implements printing an array type T[N]. +template <typename T, size_t N> +class UniversalPrinter<T[N]> { + public: + // Prints the given array, omitting some elements when there are too + // many. + static void Print(const T (&a)[N], ::std::ostream* os) { + UniversalPrintArray(a, N, os); + } +}; + +// Implements printing a reference type T&. +template <typename T> +class UniversalPrinter<T&> { + public: + // MSVC warns about adding const to a function type, so we want to + // disable the warning. +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4180) // Temporarily disables warning 4180. +#endif // _MSC_VER + + static void Print(const T& value, ::std::ostream* os) { + // Prints the address of the value. We use reinterpret_cast here + // as static_cast doesn't compile when T is a function type. + *os << "@" << reinterpret_cast<const void*>(&value) << " "; + + // Then prints the value itself. + UniversalPrint(value, os); + } + +#ifdef _MSC_VER +# pragma warning(pop) // Restores the warning state. +#endif // _MSC_VER +}; + +// Prints a value tersely: for a reference type, the referenced value +// (but not the address) is printed; for a (const) char pointer, the +// NUL-terminated string (but not the pointer) is printed. +template <typename T> +void UniversalTersePrint(const T& value, ::std::ostream* os) { + UniversalPrint(value, os); +} +inline void UniversalTersePrint(const char* str, ::std::ostream* os) { + if (str == NULL) { + *os << "NULL"; + } else { + UniversalPrint(string(str), os); + } +} +inline void UniversalTersePrint(char* str, ::std::ostream* os) { + UniversalTersePrint(static_cast<const char*>(str), os); +} + +// Prints a value using the type inferred by the compiler. The +// difference between this and UniversalTersePrint() is that for a +// (const) char pointer, this prints both the pointer and the +// NUL-terminated string. +template <typename T> +void UniversalPrint(const T& value, ::std::ostream* os) { + UniversalPrinter<T>::Print(value, os); +} + +#if GTEST_HAS_TR1_TUPLE +typedef ::std::vector<string> Strings; + +// This helper template allows PrintTo() for tuples and +// UniversalTersePrintTupleFieldsToStrings() to be defined by +// induction on the number of tuple fields. The idea is that +// TuplePrefixPrinter<N>::PrintPrefixTo(t, os) prints the first N +// fields in tuple t, and can be defined in terms of +// TuplePrefixPrinter<N - 1>. + +// The inductive case. +template <size_t N> +struct TuplePrefixPrinter { + // Prints the first N fields of a tuple. + template <typename Tuple> + static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) { + TuplePrefixPrinter<N - 1>::PrintPrefixTo(t, os); + *os << ", "; + UniversalPrinter<typename ::std::tr1::tuple_element<N - 1, Tuple>::type> + ::Print(::std::tr1::get<N - 1>(t), os); + } + + // Tersely prints the first N fields of a tuple to a string vector, + // one element for each field. + template <typename Tuple> + static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) { + TuplePrefixPrinter<N - 1>::TersePrintPrefixToStrings(t, strings); + ::std::stringstream ss; + UniversalTersePrint(::std::tr1::get<N - 1>(t), &ss); + strings->push_back(ss.str()); + } +}; + +// Base cases. +template <> +struct TuplePrefixPrinter<0> { + template <typename Tuple> + static void PrintPrefixTo(const Tuple&, ::std::ostream*) {} + + template <typename Tuple> + static void TersePrintPrefixToStrings(const Tuple&, Strings*) {} +}; +// We have to specialize the entire TuplePrefixPrinter<> class +// template here, even though the definition of +// TersePrintPrefixToStrings() is the same as the generic version, as +// Embarcadero (formerly CodeGear, formerly Borland) C++ doesn't +// support specializing a method template of a class template. +template <> +struct TuplePrefixPrinter<1> { + template <typename Tuple> + static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) { + UniversalPrinter<typename ::std::tr1::tuple_element<0, Tuple>::type>:: + Print(::std::tr1::get<0>(t), os); + } + + template <typename Tuple> + static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) { + ::std::stringstream ss; + UniversalTersePrint(::std::tr1::get<0>(t), &ss); + strings->push_back(ss.str()); + } +}; + +// Helper function for printing a tuple. T must be instantiated with +// a tuple type. +template <typename T> +void PrintTupleTo(const T& t, ::std::ostream* os) { + *os << "("; + TuplePrefixPrinter< ::std::tr1::tuple_size<T>::value>:: + PrintPrefixTo(t, os); + *os << ")"; +} + +// Prints the fields of a tuple tersely to a string vector, one +// element for each field. See the comment before +// UniversalTersePrint() for how we define "tersely". +template <typename Tuple> +Strings UniversalTersePrintTupleFieldsToStrings(const Tuple& value) { + Strings result; + TuplePrefixPrinter< ::std::tr1::tuple_size<Tuple>::value>:: + TersePrintPrefixToStrings(value, &result); + return result; +} +#endif // GTEST_HAS_TR1_TUPLE + +} // namespace internal + +template <typename T> +::std::string PrintToString(const T& value) { + ::std::stringstream ss; + internal::UniversalTersePrint(value, &ss); + return ss.str(); +} + +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ diff --git a/utils/unittest/googletest/include/gtest/gtest-spi.h b/utils/unittest/googletest/include/gtest/gtest-spi.h index c41da48..b226e55 100644 --- a/utils/unittest/googletest/include/gtest/gtest-spi.h +++ b/utils/unittest/googletest/include/gtest/gtest-spi.h @@ -35,7 +35,7 @@ #ifndef GTEST_INCLUDE_GTEST_GTEST_SPI_H_ #define GTEST_INCLUDE_GTEST_GTEST_SPI_H_ -#include <gtest/gtest.h> +#include "gtest/gtest.h" namespace testing { @@ -98,12 +98,12 @@ class GTEST_API_ SingleFailureChecker { // The constructor remembers the arguments. SingleFailureChecker(const TestPartResultArray* results, TestPartResult::Type type, - const char* substr); + const string& substr); ~SingleFailureChecker(); private: const TestPartResultArray* const results_; const TestPartResult::Type type_; - const String substr_; + const string substr_; GTEST_DISALLOW_COPY_AND_ASSIGN_(SingleFailureChecker); }; diff --git a/utils/unittest/googletest/include/gtest/gtest-test-part.h b/utils/unittest/googletest/include/gtest/gtest-test-part.h index f714759..8aeea14 100644 --- a/utils/unittest/googletest/include/gtest/gtest-test-part.h +++ b/utils/unittest/googletest/include/gtest/gtest-test-part.h @@ -35,8 +35,8 @@ #include <iosfwd> #include <vector> -#include <gtest/internal/gtest-internal.h> -#include <gtest/internal/gtest-string.h> +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-string.h" namespace testing { diff --git a/utils/unittest/googletest/include/gtest/gtest-typed-test.h b/utils/unittest/googletest/include/gtest/gtest-typed-test.h index 1ec8eb8..fe1e83b 100644 --- a/utils/unittest/googletest/include/gtest/gtest-typed-test.h +++ b/utils/unittest/googletest/include/gtest/gtest-typed-test.h @@ -146,8 +146,8 @@ INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes); #endif // 0 -#include <gtest/internal/gtest-port.h> -#include <gtest/internal/gtest-type-util.h> +#include "gtest/internal/gtest-port.h" +#include "gtest/internal/gtest-type-util.h" // Implements typed tests. @@ -157,16 +157,16 @@ INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes); // // Expands to the name of the typedef for the type parameters of the // given test case. -#define GTEST_TYPE_PARAMS_(TestCaseName) gtest_type_params_##TestCaseName##_ +# define GTEST_TYPE_PARAMS_(TestCaseName) gtest_type_params_##TestCaseName##_ // The 'Types' template argument below must have spaces around it // since some compilers may choke on '>>' when passing a template // instance (e.g. Types<int>) -#define TYPED_TEST_CASE(CaseName, Types) \ +# define TYPED_TEST_CASE(CaseName, Types) \ typedef ::testing::internal::TypeList< Types >::type \ GTEST_TYPE_PARAMS_(CaseName) -#define TYPED_TEST(CaseName, TestName) \ +# define TYPED_TEST(CaseName, TestName) \ template <typename gtest_TypeParam_> \ class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \ : public CaseName<gtest_TypeParam_> { \ @@ -175,7 +175,7 @@ INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes); typedef gtest_TypeParam_ TypeParam; \ virtual void TestBody(); \ }; \ - bool gtest_##CaseName##_##TestName##_registered_ = \ + bool gtest_##CaseName##_##TestName##_registered_ GTEST_ATTRIBUTE_UNUSED_ = \ ::testing::internal::TypeParameterizedTest< \ CaseName, \ ::testing::internal::TemplateSel< \ @@ -196,31 +196,31 @@ INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes); // Expands to the namespace name that the type-parameterized tests for // the given type-parameterized test case are defined in. The exact // name of the namespace is subject to change without notice. -#define GTEST_CASE_NAMESPACE_(TestCaseName) \ +# define GTEST_CASE_NAMESPACE_(TestCaseName) \ gtest_case_##TestCaseName##_ // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. // // Expands to the name of the variable used to remember the names of // the defined tests in the given test case. -#define GTEST_TYPED_TEST_CASE_P_STATE_(TestCaseName) \ +# define GTEST_TYPED_TEST_CASE_P_STATE_(TestCaseName) \ gtest_typed_test_case_p_state_##TestCaseName##_ // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE DIRECTLY. // // Expands to the name of the variable used to remember the names of // the registered tests in the given test case. -#define GTEST_REGISTERED_TEST_NAMES_(TestCaseName) \ +# define GTEST_REGISTERED_TEST_NAMES_(TestCaseName) \ gtest_registered_test_names_##TestCaseName##_ // The variables defined in the type-parameterized test macros are // static as typically these macros are used in a .h file that can be // #included in multiple translation units linked together. -#define TYPED_TEST_CASE_P(CaseName) \ +# define TYPED_TEST_CASE_P(CaseName) \ static ::testing::internal::TypedTestCasePState \ GTEST_TYPED_TEST_CASE_P_STATE_(CaseName) -#define TYPED_TEST_P(CaseName, TestName) \ +# define TYPED_TEST_P(CaseName, TestName) \ namespace GTEST_CASE_NAMESPACE_(CaseName) { \ template <typename gtest_TypeParam_> \ class TestName : public CaseName<gtest_TypeParam_> { \ @@ -229,14 +229,14 @@ INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes); typedef gtest_TypeParam_ TypeParam; \ virtual void TestBody(); \ }; \ - static bool gtest_##TestName##_defined_ = \ + static bool gtest_##TestName##_defined_ GTEST_ATTRIBUTE_UNUSED_ = \ GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).AddTestName(\ __FILE__, __LINE__, #CaseName, #TestName); \ } \ template <typename gtest_TypeParam_> \ void GTEST_CASE_NAMESPACE_(CaseName)::TestName<gtest_TypeParam_>::TestBody() -#define REGISTER_TYPED_TEST_CASE_P(CaseName, ...) \ +# define REGISTER_TYPED_TEST_CASE_P(CaseName, ...) \ namespace GTEST_CASE_NAMESPACE_(CaseName) { \ typedef ::testing::internal::Templates<__VA_ARGS__>::type gtest_AllTests_; \ } \ @@ -247,8 +247,8 @@ INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes); // The 'Types' template argument below must have spaces around it // since some compilers may choke on '>>' when passing a template // instance (e.g. Types<int>) -#define INSTANTIATE_TYPED_TEST_CASE_P(Prefix, CaseName, Types) \ - bool gtest_##Prefix##_##CaseName = \ +# define INSTANTIATE_TYPED_TEST_CASE_P(Prefix, CaseName, Types) \ + bool gtest_##Prefix##_##CaseName GTEST_ATTRIBUTE_UNUSED_ = \ ::testing::internal::TypeParameterizedTestCase<CaseName, \ GTEST_CASE_NAMESPACE_(CaseName)::gtest_AllTests_, \ ::testing::internal::TypeList< Types >::type>::Register(\ diff --git a/utils/unittest/googletest/include/gtest/gtest.h b/utils/unittest/googletest/include/gtest/gtest.h index 5470082..1734c44 100644 --- a/utils/unittest/googletest/include/gtest/gtest.h +++ b/utils/unittest/googletest/include/gtest/gtest.h @@ -54,14 +54,15 @@ #include <limits> #include <vector> -#include <gtest/internal/gtest-internal.h> -#include <gtest/internal/gtest-string.h> -#include <gtest/gtest-death-test.h> -#include <gtest/gtest-message.h> -#include <gtest/gtest-param-test.h> -#include <gtest/gtest_prod.h> -#include <gtest/gtest-test-part.h> -#include <gtest/gtest-typed-test.h> +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-string.h" +#include "gtest/gtest-death-test.h" +#include "gtest/gtest-message.h" +#include "gtest/gtest-param-test.h" +#include "gtest/gtest-printers.h" +#include "gtest/gtest_prod.h" +#include "gtest/gtest-test-part.h" +#include "gtest/gtest-typed-test.h" // Depending on the platform, different string classes are available. // On Linux, in addition to ::std::string, Google also makes use of @@ -136,6 +137,11 @@ GTEST_DECLARE_int32_(stack_trace_depth); // non-zero code otherwise. GTEST_DECLARE_bool_(throw_on_failure); +// When this flag is set with a "host:port" string, on supported +// platforms test results are streamed to the specified port on +// the specified host machine. +GTEST_DECLARE_string_(stream_result_to); + // The upper limit for valid stack trace depths. const int kMaxStackTraceDepth = 100; @@ -147,7 +153,6 @@ class ExecDeathTest; class NoExecDeathTest; class FinalSuccessChecker; class GTestFlagSaver; -class TestInfoImpl; class TestResultAccessor; class TestEventListenersAccessor; class TestEventRepeater; @@ -155,8 +160,6 @@ class WindowsDeathTest; class UnitTestImpl* GetUnitTestImpl(); void ReportFailureInUnknownLocation(TestPartResult::Type result_type, const String& message); -class PrettyUnitTestResultPrinter; -class XmlUnitTestResultPrinter; // Converts a streamable value to a String. A NULL pointer is // converted to "(null)". When the input value is a ::string, @@ -172,6 +175,14 @@ String StreamableToString(const T& streamable) { } // namespace internal +// The friend relationship of some of these classes is cyclic. +// If we don't forward declare them the compiler might confuse the classes +// in friendship clauses with same named classes on the scope. +class Test; +class TestCase; +class TestInfo; +class UnitTest; + // A class for indicating whether an assertion was successful. When // the assertion wasn't successful, the AssertionResult object // remembers a non-empty message that describes how it failed. @@ -270,20 +281,33 @@ class GTEST_API_ AssertionResult { // assertion's expectation). When nothing has been streamed into the // object, returns an empty string. const char* message() const { - return message_.get() != NULL && message_->c_str() != NULL ? - message_->c_str() : ""; + return message_.get() != NULL ? message_->c_str() : ""; } // TODO(vladl@google.com): Remove this after making sure no clients use it. // Deprecated; please use message() instead. const char* failure_message() const { return message(); } // Streams a custom failure message into this object. - template <typename T> AssertionResult& operator<<(const T& value); + template <typename T> AssertionResult& operator<<(const T& value) { + AppendMessage(Message() << value); + return *this; + } + + // Allows streaming basic output manipulators such as endl or flush into + // this object. + AssertionResult& operator<<( + ::std::ostream& (*basic_manipulator)(::std::ostream& stream)) { + AppendMessage(Message() << basic_manipulator); + return *this; + } private: - // No implementation - we want AssertionResult to be - // copy-constructible but not assignable. - void operator=(const AssertionResult& other); + // Appends the contents of message to message_. + void AppendMessage(const Message& a_message) { + if (message_.get() == NULL) + message_.reset(new ::std::string); + message_->append(a_message.GetString().c_str()); + } // Stores result of the assertion predicate. bool success_; @@ -291,19 +315,10 @@ class GTEST_API_ AssertionResult { // construct is not satisfied with the predicate's outcome. // Referenced via a pointer to avoid taking too much stack frame space // with test assertions. - internal::scoped_ptr<internal::String> message_; -}; // class AssertionResult + internal::scoped_ptr< ::std::string> message_; -// Streams a custom failure message into this object. -template <typename T> -AssertionResult& AssertionResult::operator<<(const T& value) { - Message msg; - if (message_.get() != NULL) - msg << *message_; - msg << value; - message_.reset(new internal::String(msg.GetString())); - return *this; -} + GTEST_DISALLOW_ASSIGN_(AssertionResult); +}; // Makes a successful assertion result. GTEST_API_ AssertionResult AssertionSuccess(); @@ -340,7 +355,7 @@ GTEST_API_ AssertionResult AssertionFailure(const Message& msg); // Test is not copyable. class GTEST_API_ Test { public: - friend class internal::TestInfoImpl; + friend class TestInfo; // Defines types for pointers to functions that set up and tear down // a test case. @@ -417,6 +432,10 @@ class GTEST_API_ Test { // Sets up, executes, and tears down the test. void Run(); + // Deletes self. We deliberately pick an unusual name for this + // internal method to avoid clashing with names used in user TESTs. + void DeleteSelf_() { delete this; } + // Uses a GTestFlagSaver to save and restore all Google Test flags. const internal::GTestFlagSaver* const gtest_flag_saver_; @@ -531,7 +550,6 @@ class GTEST_API_ TestResult { friend class UnitTest; friend class internal::DefaultGlobalTestPartResultReporter; friend class internal::ExecDeathTest; - friend class internal::TestInfoImpl; friend class internal::TestResultAccessor; friend class internal::UnitTestImpl; friend class internal::WindowsDeathTest; @@ -611,16 +629,26 @@ class GTEST_API_ TestInfo { ~TestInfo(); // Returns the test case name. - const char* test_case_name() const; + const char* test_case_name() const { return test_case_name_.c_str(); } // Returns the test name. - const char* name() const; + const char* name() const { return name_.c_str(); } - // Returns the test case comment. - const char* test_case_comment() const; + // Returns the name of the parameter type, or NULL if this is not a typed + // or a type-parameterized test. + const char* type_param() const { + if (type_param_.get() != NULL) + return type_param_->c_str(); + return NULL; + } - // Returns the test comment. - const char* comment() const; + // Returns the text representation of the value parameter, or NULL if this + // is not a value-parameterized test. + const char* value_param() const { + if (value_param_.get() != NULL) + return value_param_->c_str(); + return NULL; + } // Returns true if this test should run, that is if the test is not disabled // (or it is disabled but the also_run_disabled_tests flag has been specified) @@ -638,47 +666,70 @@ class GTEST_API_ TestInfo { // // For example, *A*:Foo.* is a filter that matches any string that // contains the character 'A' or starts with "Foo.". - bool should_run() const; + bool should_run() const { return should_run_; } // Returns the result of the test. - const TestResult* result() const; + const TestResult* result() const { return &result_; } private: + #if GTEST_HAS_DEATH_TEST friend class internal::DefaultDeathTestFactory; #endif // GTEST_HAS_DEATH_TEST friend class Test; friend class TestCase; - friend class internal::TestInfoImpl; friend class internal::UnitTestImpl; friend TestInfo* internal::MakeAndRegisterTestInfo( const char* test_case_name, const char* name, - const char* test_case_comment, const char* comment, + const char* type_param, + const char* value_param, internal::TypeId fixture_class_id, Test::SetUpTestCaseFunc set_up_tc, Test::TearDownTestCaseFunc tear_down_tc, internal::TestFactoryBase* factory); - // Returns true if this test matches the user-specified filter. - bool matches_filter() const; - - // Increments the number of death tests encountered in this test so - // far. - int increment_death_test_count(); - - // Accessors for the implementation object. - internal::TestInfoImpl* impl() { return impl_; } - const internal::TestInfoImpl* impl() const { return impl_; } - // Constructs a TestInfo object. The newly constructed instance assumes // ownership of the factory object. TestInfo(const char* test_case_name, const char* name, - const char* test_case_comment, const char* comment, + const char* a_type_param, + const char* a_value_param, internal::TypeId fixture_class_id, internal::TestFactoryBase* factory); - // An opaque implementation object. - internal::TestInfoImpl* impl_; + // Increments the number of death tests encountered in this test so + // far. + int increment_death_test_count() { + return result_.increment_death_test_count(); + } + + // Creates the test object, runs it, records its result, and then + // deletes it. + void Run(); + + static void ClearTestResult(TestInfo* test_info) { + test_info->result_.Clear(); + } + + // These fields are immutable properties of the test. + const std::string test_case_name_; // Test case name + const std::string name_; // Test name + // Name of the parameter type, or NULL if this is not a typed or a + // type-parameterized test. + const internal::scoped_ptr<const ::std::string> type_param_; + // Text representation of the value parameter, or NULL if this is not a + // value-parameterized test. + const internal::scoped_ptr<const ::std::string> value_param_; + const internal::TypeId fixture_class_id_; // ID of the test fixture class + bool should_run_; // True iff this test should run + bool is_disabled_; // True iff this test is disabled + bool matches_filter_; // True if this test matches the + // user-specified filter. + internal::TestFactoryBase* const factory_; // The factory that creates + // the test object + + // This field is mutable and needs to be reset before running the + // test for the second time. + TestResult result_; GTEST_DISALLOW_COPY_AND_ASSIGN_(TestInfo); }; @@ -696,9 +747,11 @@ class GTEST_API_ TestCase { // Arguments: // // name: name of the test case + // a_type_param: the name of the test's type parameter, or NULL if + // this is not a type-parameterized test. // set_up_tc: pointer to the function that sets up the test case // tear_down_tc: pointer to the function that tears down the test case - TestCase(const char* name, const char* comment, + TestCase(const char* name, const char* a_type_param, Test::SetUpTestCaseFunc set_up_tc, Test::TearDownTestCaseFunc tear_down_tc); @@ -708,8 +761,13 @@ class GTEST_API_ TestCase { // Gets the name of the TestCase. const char* name() const { return name_.c_str(); } - // Returns the test case comment. - const char* comment() const { return comment_.c_str(); } + // Returns the name of the parameter type, or NULL if this is not a + // type-parameterized test case. + const char* type_param() const { + if (type_param_.get() != NULL) + return type_param_->c_str(); + return NULL; + } // Returns true if any test in this test case should run. bool should_run() const { return should_run_; } @@ -776,17 +834,33 @@ class GTEST_API_ TestCase { // Runs every test in this TestCase. void Run(); + // Runs SetUpTestCase() for this TestCase. This wrapper is needed + // for catching exceptions thrown from SetUpTestCase(). + void RunSetUpTestCase() { (*set_up_tc_)(); } + + // Runs TearDownTestCase() for this TestCase. This wrapper is + // needed for catching exceptions thrown from TearDownTestCase(). + void RunTearDownTestCase() { (*tear_down_tc_)(); } + // Returns true iff test passed. - static bool TestPassed(const TestInfo * test_info); + static bool TestPassed(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Passed(); + } // Returns true iff test failed. - static bool TestFailed(const TestInfo * test_info); + static bool TestFailed(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Failed(); + } // Returns true iff test is disabled. - static bool TestDisabled(const TestInfo * test_info); + static bool TestDisabled(const TestInfo* test_info) { + return test_info->is_disabled_; + } // Returns true if the given test should run. - static bool ShouldRunTest(const TestInfo *test_info); + static bool ShouldRunTest(const TestInfo* test_info) { + return test_info->should_run(); + } // Shuffles the tests in this test case. void ShuffleTests(internal::Random* random); @@ -796,8 +870,9 @@ class GTEST_API_ TestCase { // Name of the test case. internal::String name_; - // Comment on the test case. - internal::String comment_; + // Name of the parameter type, or NULL if this is not a typed or a + // type-parameterized test. + const internal::scoped_ptr<const ::std::string> type_param_; // The vector of TestInfos in their original order. It owns the // elements in the vector. std::vector<TestInfo*> test_info_list_; @@ -876,7 +951,7 @@ class TestEventListener { // Fired before the test starts. virtual void OnTestStart(const TestInfo& test_info) = 0; - // Fired after a failed assertion or a SUCCESS(). + // Fired after a failed assertion or a SUCCEED() invocation. virtual void OnTestPartResult(const TestPartResult& test_part_result) = 0; // Fired after the test ends. @@ -961,10 +1036,10 @@ class GTEST_API_ TestEventListeners { private: friend class TestCase; + friend class TestInfo; friend class internal::DefaultGlobalTestPartResultReporter; friend class internal::NoExecDeathTest; friend class internal::TestEventListenersAccessor; - friend class internal::TestInfoImpl; friend class internal::UnitTestImpl; // Returns repeater that broadcasts the TestEventListener events to all @@ -1206,30 +1281,6 @@ GTEST_API_ void InitGoogleTest(int* argc, wchar_t** argv); namespace internal { -// These overloaded versions handle ::std::string and ::std::wstring. -GTEST_API_ inline String FormatForFailureMessage(const ::std::string& str) { - return (Message() << '"' << str << '"').GetString(); -} - -#if GTEST_HAS_STD_WSTRING -GTEST_API_ inline String FormatForFailureMessage(const ::std::wstring& wstr) { - return (Message() << "L\"" << wstr << '"').GetString(); -} -#endif // GTEST_HAS_STD_WSTRING - -// These overloaded versions handle ::string and ::wstring. -#if GTEST_HAS_GLOBAL_STRING -GTEST_API_ inline String FormatForFailureMessage(const ::string& str) { - return (Message() << '"' << str << '"').GetString(); -} -#endif // GTEST_HAS_GLOBAL_STRING - -#if GTEST_HAS_GLOBAL_WSTRING -GTEST_API_ inline String FormatForFailureMessage(const ::wstring& wstr) { - return (Message() << "L\"" << wstr << '"').GetString(); -} -#endif // GTEST_HAS_GLOBAL_WSTRING - // Formats a comparison assertion (e.g. ASSERT_EQ, EXPECT_LT, and etc) // operand to be used in a failure message. The type (but not value) // of the other operand may affect the format. This allows us to @@ -1245,7 +1296,9 @@ GTEST_API_ inline String FormatForFailureMessage(const ::wstring& wstr) { template <typename T1, typename T2> String FormatForComparisonFailureMessage(const T1& value, const T2& /* other_operand */) { - return FormatForFailureMessage(value); + // C++Builder compiles this incorrectly if the namespace isn't explicitly + // given. + return ::testing::PrintToString(value); } // The helper function for {ASSERT|EXPECT}_EQ. @@ -1255,8 +1308,8 @@ AssertionResult CmpHelperEQ(const char* expected_expression, const T1& expected, const T2& actual) { #ifdef _MSC_VER -#pragma warning(push) // Saves the current warning state. -#pragma warning(disable:4389) // Temporarily disables warning on +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4389) // Temporarily disables warning on // signed/unsigned mismatch. #pragma warning(disable:4805) // Temporarily disables warning on // unsafe mix of types @@ -1267,7 +1320,7 @@ AssertionResult CmpHelperEQ(const char* expected_expression, } #ifdef _MSC_VER -#pragma warning(pop) // Restores the warning state. +# pragma warning(pop) // Restores the warning state. #endif return EqFailure(expected_expression, @@ -1318,7 +1371,7 @@ class EqHelper { }; // This specialization is used when the first argument to ASSERT_EQ() -// is a null pointer literal. +// is a null pointer literal, like NULL, false, or 0. template <> class EqHelper<true> { public: @@ -1327,24 +1380,38 @@ class EqHelper<true> { // NOT a pointer, e.g. ASSERT_EQ(0, AnIntFunction()) or // EXPECT_EQ(false, a_bool). template <typename T1, typename T2> - static AssertionResult Compare(const char* expected_expression, - const char* actual_expression, - const T1& expected, - const T2& actual) { + static AssertionResult Compare( + const char* expected_expression, + const char* actual_expression, + const T1& expected, + const T2& actual, + // The following line prevents this overload from being considered if T2 + // is not a pointer type. We need this because ASSERT_EQ(NULL, my_ptr) + // expands to Compare("", "", NULL, my_ptr), which requires a conversion + // to match the Secret* in the other overload, which would otherwise make + // this template match better. + typename EnableIf<!is_pointer<T2>::value>::type* = 0) { return CmpHelperEQ(expected_expression, actual_expression, expected, actual); } - // This version will be picked when the second argument to - // ASSERT_EQ() is a pointer, e.g. ASSERT_EQ(NULL, a_pointer). - template <typename T1, typename T2> - static AssertionResult Compare(const char* expected_expression, - const char* actual_expression, - const T1& /* expected */, - T2* actual) { + // This version will be picked when the second argument to ASSERT_EQ() is a + // pointer, e.g. ASSERT_EQ(NULL, a_pointer). + template <typename T> + static AssertionResult Compare( + const char* expected_expression, + const char* actual_expression, + // We used to have a second template parameter instead of Secret*. That + // template parameter would deduce to 'long', making this a better match + // than the first overload even without the first overload's EnableIf. + // Unfortunately, gcc with -Wconversion-null warns when "passing NULL to + // non-pointer argument" (even a deduced integral argument), so the old + // implementation caused warnings in user code. + Secret* /* expected (NULL) */, + T* actual) { // We already know that 'expected' is a null pointer. return CmpHelperEQ(expected_expression, actual_expression, - static_cast<T2*>(NULL), actual); + static_cast<T*>(NULL), actual); } }; @@ -1365,11 +1432,10 @@ AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \ if (val1 op val2) {\ return AssertionSuccess();\ } else {\ - Message msg;\ - msg << "Expected: (" << expr1 << ") " #op " (" << expr2\ + return AssertionFailure() \ + << "Expected: (" << expr1 << ") " #op " (" << expr2\ << "), actual: " << FormatForComparisonFailureMessage(val1, val2)\ << " vs " << FormatForComparisonFailureMessage(val2, val1);\ - return AssertionFailure(msg);\ }\ }\ GTEST_API_ AssertionResult CmpHelper##op_name(\ @@ -1497,18 +1563,18 @@ AssertionResult CmpHelperFloatingPointEQ(const char* expected_expression, return AssertionSuccess(); } - StrStream expected_ss; + ::std::stringstream expected_ss; expected_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2) << expected; - StrStream actual_ss; + ::std::stringstream actual_ss; actual_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2) << actual; return EqFailure(expected_expression, actual_expression, - StrStreamToString(&expected_ss), - StrStreamToString(&actual_ss), + StringStreamToString(&expected_ss), + StringStreamToString(&actual_ss), false); } @@ -1566,9 +1632,13 @@ class GTEST_API_ AssertHelper { } // namespace internal #if GTEST_HAS_PARAM_TEST -// The abstract base class that all value-parameterized tests inherit from. +// The pure interface class that all value-parameterized tests inherit from. +// A value-parameterized class must inherit from both ::testing::Test and +// ::testing::WithParamInterface. In most cases that just means inheriting +// from ::testing::TestWithParam, but more complicated test hierarchies +// may need to inherit from Test and WithParamInterface at different levels. // -// This class adds support for accessing the test parameter value via +// This interface has support for accessing the test parameter value via // the GetParam() method. // // Use it with one of the parameter generator defining functions, like Range(), @@ -1597,12 +1667,16 @@ class GTEST_API_ AssertHelper { // INSTANTIATE_TEST_CASE_P(OneToTenRange, FooTest, ::testing::Range(1, 10)); template <typename T> -class TestWithParam : public Test { +class WithParamInterface { public: typedef T ParamType; + virtual ~WithParamInterface() {} // The current parameter value. Is also available in the test fixture's - // constructor. + // constructor. This member function is non-static, even though it only + // references static data, to reduce the opportunity for incorrect uses + // like writing 'WithParamInterface<bool>::GetParam()' for a test that + // uses a fixture whose parameter type is int. const ParamType& GetParam() const { return *parameter_; } private: @@ -1615,12 +1689,19 @@ class TestWithParam : public Test { // Static value used for accessing parameter during a test lifetime. static const ParamType* parameter_; - // TestClass must be a subclass of TestWithParam<T>. + // TestClass must be a subclass of WithParamInterface<T> and Test. template <class TestClass> friend class internal::ParameterizedTestFactory; }; template <typename T> -const T* TestWithParam<T>::parameter_ = NULL; +const T* WithParamInterface<T>::parameter_ = NULL; + +// Most value-parameterized classes can ignore the existence of +// WithParamInterface, and can just inherit from ::testing::TestWithParam. + +template <typename T> +class TestWithParam : public Test, public WithParamInterface<T> { +}; #endif // GTEST_HAS_PARAM_TEST @@ -1652,13 +1733,19 @@ const T* TestWithParam<T>::parameter_ = NULL; // Generates a nonfatal failure with a generic message. #define ADD_FAILURE() GTEST_NONFATAL_FAILURE_("Failed") +// Generates a nonfatal failure at the given source file location with +// a generic message. +#define ADD_FAILURE_AT(file, line) \ + GTEST_MESSAGE_AT_(file, line, "Failed", \ + ::testing::TestPartResult::kNonFatalFailure) + // Generates a fatal failure with a generic message. #define GTEST_FAIL() GTEST_FATAL_FAILURE_("Failed") // Define this macro to 1 to omit the definition of FAIL(), which is a // generic name and clashes with some other libraries. #if !GTEST_DONT_DEFINE_FAIL -#define FAIL() GTEST_FAIL() +# define FAIL() GTEST_FAIL() #endif // Generates a success with a generic message. @@ -1667,7 +1754,7 @@ const T* TestWithParam<T>::parameter_ = NULL; // Define this macro to 1 to omit the definition of SUCCEED(), which // is a generic name and clashes with some other libraries. #if !GTEST_DONT_DEFINE_SUCCEED -#define SUCCEED() GTEST_SUCCEED() +# define SUCCEED() GTEST_SUCCEED() #endif // Macros for testing exceptions. @@ -1710,7 +1797,7 @@ const T* TestWithParam<T>::parameter_ = NULL; // Includes the auto-generated header that implements a family of // generic predicate assertion macros. -#include <gtest/gtest_pred_impl.h> +#include "gtest/gtest_pred_impl.h" // Macros for testing equalities and inequalities. // @@ -1773,21 +1860,48 @@ const T* TestWithParam<T>::parameter_ = NULL; #define EXPECT_GT(val1, val2) \ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) -#define ASSERT_EQ(expected, actual) \ +#define GTEST_ASSERT_EQ(expected, actual) \ ASSERT_PRED_FORMAT2(::testing::internal:: \ EqHelper<GTEST_IS_NULL_LITERAL_(expected)>::Compare, \ expected, actual) -#define ASSERT_NE(val1, val2) \ +#define GTEST_ASSERT_NE(val1, val2) \ ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2) -#define ASSERT_LE(val1, val2) \ +#define GTEST_ASSERT_LE(val1, val2) \ ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2) -#define ASSERT_LT(val1, val2) \ +#define GTEST_ASSERT_LT(val1, val2) \ ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2) -#define ASSERT_GE(val1, val2) \ +#define GTEST_ASSERT_GE(val1, val2) \ ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2) -#define ASSERT_GT(val1, val2) \ +#define GTEST_ASSERT_GT(val1, val2) \ ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) +// Define macro GTEST_DONT_DEFINE_ASSERT_XY to 1 to omit the definition of +// ASSERT_XY(), which clashes with some users' own code. + +#if !GTEST_DONT_DEFINE_ASSERT_EQ +# define ASSERT_EQ(val1, val2) GTEST_ASSERT_EQ(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_NE +# define ASSERT_NE(val1, val2) GTEST_ASSERT_NE(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_LE +# define ASSERT_LE(val1, val2) GTEST_ASSERT_LE(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_LT +# define ASSERT_LT(val1, val2) GTEST_ASSERT_LT(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_GE +# define ASSERT_GE(val1, val2) GTEST_ASSERT_GE(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_GT +# define ASSERT_GT(val1, val2) GTEST_ASSERT_GT(val1, val2) +#endif + // C String Comparisons. All tests treat NULL and any non-NULL string // as different. Two NULLs are equal. // @@ -1884,16 +1998,16 @@ GTEST_API_ AssertionResult DoubleLE(const char* expr1, const char* expr2, // expected result and the actual result with both a human-readable // string representation of the error, if available, as well as the // hex result code. -#define EXPECT_HRESULT_SUCCEEDED(expr) \ +# define EXPECT_HRESULT_SUCCEEDED(expr) \ EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) -#define ASSERT_HRESULT_SUCCEEDED(expr) \ +# define ASSERT_HRESULT_SUCCEEDED(expr) \ ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) -#define EXPECT_HRESULT_FAILED(expr) \ +# define EXPECT_HRESULT_FAILED(expr) \ EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr)) -#define ASSERT_HRESULT_FAILED(expr) \ +# define ASSERT_HRESULT_FAILED(expr) \ ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr)) #endif // GTEST_OS_WINDOWS @@ -1928,17 +2042,6 @@ GTEST_API_ AssertionResult DoubleLE(const char* expr1, const char* expr2, ::testing::internal::ScopedTrace GTEST_CONCAT_TOKEN_(gtest_trace_, __LINE__)(\ __FILE__, __LINE__, ::testing::Message() << (message)) -namespace internal { - -// This template is declared, but intentionally undefined. -template <typename T1, typename T2> -struct StaticAssertTypeEqHelper; - -template <typename T> -struct StaticAssertTypeEqHelper<T, T> {}; - -} // namespace internal - // Compile-time assertion for type equality. // StaticAssertTypeEq<type1, type2>() compiles iff type1 and type2 are // the same type. The value it returns is not interesting. @@ -1971,7 +2074,7 @@ struct StaticAssertTypeEqHelper<T, T> {}; // to cause a compiler error. template <typename T1, typename T2> bool StaticAssertTypeEq() { - internal::StaticAssertTypeEqHelper<T1, T2>(); + (void)internal::StaticAssertTypeEqHelper<T1, T2>(); return true; } @@ -2007,7 +2110,7 @@ bool StaticAssertTypeEq() { // Define this macro to 1 to omit the definition of TEST(), which // is a generic name and clashes with some other libraries. #if !GTEST_DONT_DEFINE_TEST -#define TEST(test_case_name, test_name) GTEST_TEST(test_case_name, test_name) +# define TEST(test_case_name, test_name) GTEST_TEST(test_case_name, test_name) #endif // Defines a test that uses a test fixture. diff --git a/utils/unittest/googletest/include/gtest/gtest_pred_impl.h b/utils/unittest/googletest/include/gtest/gtest_pred_impl.h index e1e2f8c..3805f85 100644 --- a/utils/unittest/googletest/include/gtest/gtest_pred_impl.h +++ b/utils/unittest/googletest/include/gtest/gtest_pred_impl.h @@ -27,7 +27,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// This file is AUTOMATICALLY GENERATED on 10/02/2008 by command +// This file is AUTOMATICALLY GENERATED on 09/24/2010 by command // 'gen_gtest_pred_impl.py 5'. DO NOT EDIT BY HAND! // // Implements a family of generic predicate assertion macros. @@ -37,7 +37,7 @@ // Makes sure this header is not included before gtest.h. #ifndef GTEST_INCLUDE_GTEST_GTEST_H_ -#error Do not include gtest_pred_impl.h directly. Include gtest.h instead. +# error Do not include gtest_pred_impl.h directly. Include gtest.h instead. #endif // GTEST_INCLUDE_GTEST_GTEST_H_ // This header implements a family of generic predicate assertion @@ -90,11 +90,9 @@ AssertionResult AssertPred1Helper(const char* pred_text, const T1& v1) { if (pred(v1)) return AssertionSuccess(); - Message msg; - msg << pred_text << "(" - << e1 << ") evaluates to false, where" - << "\n" << e1 << " evaluates to " << v1; - return AssertionFailure(msg); + return AssertionFailure() << pred_text << "(" + << e1 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1; } // Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT1. @@ -136,13 +134,11 @@ AssertionResult AssertPred2Helper(const char* pred_text, const T2& v2) { if (pred(v1, v2)) return AssertionSuccess(); - Message msg; - msg << pred_text << "(" - << e1 << ", " - << e2 << ") evaluates to false, where" - << "\n" << e1 << " evaluates to " << v1 - << "\n" << e2 << " evaluates to " << v2; - return AssertionFailure(msg); + return AssertionFailure() << pred_text << "(" + << e1 << ", " + << e2 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2; } // Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT2. @@ -189,15 +185,13 @@ AssertionResult AssertPred3Helper(const char* pred_text, const T3& v3) { if (pred(v1, v2, v3)) return AssertionSuccess(); - Message msg; - msg << pred_text << "(" - << e1 << ", " - << e2 << ", " - << e3 << ") evaluates to false, where" - << "\n" << e1 << " evaluates to " << v1 - << "\n" << e2 << " evaluates to " << v2 - << "\n" << e3 << " evaluates to " << v3; - return AssertionFailure(msg); + return AssertionFailure() << pred_text << "(" + << e1 << ", " + << e2 << ", " + << e3 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2 + << "\n" << e3 << " evaluates to " << v3; } // Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT3. @@ -249,17 +243,15 @@ AssertionResult AssertPred4Helper(const char* pred_text, const T4& v4) { if (pred(v1, v2, v3, v4)) return AssertionSuccess(); - Message msg; - msg << pred_text << "(" - << e1 << ", " - << e2 << ", " - << e3 << ", " - << e4 << ") evaluates to false, where" - << "\n" << e1 << " evaluates to " << v1 - << "\n" << e2 << " evaluates to " << v2 - << "\n" << e3 << " evaluates to " << v3 - << "\n" << e4 << " evaluates to " << v4; - return AssertionFailure(msg); + return AssertionFailure() << pred_text << "(" + << e1 << ", " + << e2 << ", " + << e3 << ", " + << e4 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2 + << "\n" << e3 << " evaluates to " << v3 + << "\n" << e4 << " evaluates to " << v4; } // Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT4. @@ -316,19 +308,17 @@ AssertionResult AssertPred5Helper(const char* pred_text, const T5& v5) { if (pred(v1, v2, v3, v4, v5)) return AssertionSuccess(); - Message msg; - msg << pred_text << "(" - << e1 << ", " - << e2 << ", " - << e3 << ", " - << e4 << ", " - << e5 << ") evaluates to false, where" - << "\n" << e1 << " evaluates to " << v1 - << "\n" << e2 << " evaluates to " << v2 - << "\n" << e3 << " evaluates to " << v3 - << "\n" << e4 << " evaluates to " << v4 - << "\n" << e5 << " evaluates to " << v5; - return AssertionFailure(msg); + return AssertionFailure() << pred_text << "(" + << e1 << ", " + << e2 << ", " + << e3 << ", " + << e4 << ", " + << e5 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2 + << "\n" << e3 << " evaluates to " << v3 + << "\n" << e4 << " evaluates to " << v4 + << "\n" << e5 << " evaluates to " << v5; } // Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT5. diff --git a/utils/unittest/googletest/include/gtest/internal/gtest-death-test-internal.h b/utils/unittest/googletest/include/gtest/internal/gtest-death-test-internal.h index e433084..1d9f83b 100644 --- a/utils/unittest/googletest/include/gtest/internal/gtest-death-test-internal.h +++ b/utils/unittest/googletest/include/gtest/internal/gtest-death-test-internal.h @@ -37,7 +37,9 @@ #ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ #define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ -#include <gtest/internal/gtest-internal.h> +#include "gtest/internal/gtest-internal.h" + +#include <stdio.h> namespace testing { namespace internal { @@ -96,8 +98,12 @@ class GTEST_API_ DeathTest { // test, then wait for it to complete. enum TestRole { OVERSEE_TEST, EXECUTE_TEST }; - // An enumeration of the two reasons that a test might be aborted. - enum AbortReason { TEST_ENCOUNTERED_RETURN_STATEMENT, TEST_DID_NOT_DIE }; + // An enumeration of the three reasons that a test might be aborted. + enum AbortReason { + TEST_ENCOUNTERED_RETURN_STATEMENT, + TEST_THREW_EXCEPTION, + TEST_DID_NOT_DIE + }; // Assumes one of the above roles. virtual TestRole AssumeRole() = 0; @@ -149,9 +155,34 @@ class DefaultDeathTestFactory : public DeathTestFactory { // by a signal, or exited normally with a nonzero exit code. GTEST_API_ bool ExitedUnsuccessfully(int exit_status); +// Traps C++ exceptions escaping statement and reports them as test +// failures. Note that trapping SEH exceptions is not implemented here. +# if GTEST_HAS_EXCEPTIONS +# define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } catch (const ::std::exception& gtest_exception) { \ + fprintf(\ + stderr, \ + "\n%s: Caught std::exception-derived exception escaping the " \ + "death test statement. Exception message: %s\n", \ + ::testing::internal::FormatFileLocation(__FILE__, __LINE__).c_str(), \ + gtest_exception.what()); \ + fflush(stderr); \ + death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \ + } catch (...) { \ + death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \ + } + +# else +# define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) + +# endif + // This macro is for implementing ASSERT_DEATH*, EXPECT_DEATH*, // ASSERT_EXIT*, and EXPECT_EXIT*. -#define GTEST_DEATH_TEST_(statement, predicate, regex, fail) \ +# define GTEST_DEATH_TEST_(statement, predicate, regex, fail) \ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ if (::testing::internal::AlwaysTrue()) { \ const ::testing::internal::RE& gtest_regex = (regex); \ @@ -172,10 +203,12 @@ GTEST_API_ bool ExitedUnsuccessfully(int exit_status); case ::testing::internal::DeathTest::EXECUTE_TEST: { \ ::testing::internal::DeathTest::ReturnSentinel \ gtest_sentinel(gtest_dt); \ - GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, gtest_dt); \ gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE); \ break; \ } \ + default: \ + break; \ } \ } \ } else \ @@ -254,7 +287,7 @@ InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag(); // statement unconditionally returns or throws. The Message constructor at // the end allows the syntax of streaming additional messages into the // macro, for compilational compatibility with EXPECT_DEATH/ASSERT_DEATH. -#define GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, terminator) \ +# define GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, terminator) \ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ if (::testing::internal::AlwaysTrue()) { \ GTEST_LOG_(WARNING) \ diff --git a/utils/unittest/googletest/include/gtest/internal/gtest-filepath.h b/utils/unittest/googletest/include/gtest/internal/gtest-filepath.h index efbc176..823c6bd 100644 --- a/utils/unittest/googletest/include/gtest/internal/gtest-filepath.h +++ b/utils/unittest/googletest/include/gtest/internal/gtest-filepath.h @@ -40,7 +40,7 @@ #ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ #define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ -#include <gtest/internal/gtest-string.h> +#include "gtest/internal/gtest-string.h" namespace testing { namespace internal { diff --git a/utils/unittest/googletest/include/gtest/internal/gtest-internal-inl.h b/utils/unittest/googletest/include/gtest/internal/gtest-internal-inl.h index 855b215..65a2101 100644 --- a/utils/unittest/googletest/include/gtest/internal/gtest-internal-inl.h +++ b/utils/unittest/googletest/include/gtest/internal/gtest-internal-inl.h @@ -41,12 +41,12 @@ // part of Google Test's implementation; otherwise it's undefined. #if !GTEST_IMPLEMENTATION_ // A user is trying to include this from his code - just say no. -#error "gtest-internal-inl.h is part of Google Test's internal implementation." -#error "It must not be included except by Google Test itself." +# error "gtest-internal-inl.h is part of Google Test's internal implementation." +# error "It must not be included except by Google Test itself." #endif // GTEST_IMPLEMENTATION_ #ifndef _WIN32_WCE -#include <errno.h> +# include <errno.h> #endif // !_WIN32_WCE #include <stddef.h> #include <stdlib.h> // For strtoll/_strtoul64/malloc/free. @@ -56,14 +56,14 @@ #include <string> #include <vector> -#include <gtest/internal/gtest-port.h> +#include "gtest/internal/gtest-port.h" #if GTEST_OS_WINDOWS -#include <windows.h> // For DWORD. +# include <windows.h> // NOLINT #endif // GTEST_OS_WINDOWS -#include <gtest/gtest.h> // NOLINT -#include <gtest/gtest-spi.h> +#include "gtest/gtest.h" // NOLINT +#include "gtest/gtest-spi.h" namespace testing { @@ -93,6 +93,7 @@ const char kRandomSeedFlag[] = "random_seed"; const char kRepeatFlag[] = "repeat"; const char kShuffleFlag[] = "shuffle"; const char kStackTraceDepthFlag[] = "stack_trace_depth"; +const char kStreamResultToFlag[] = "stream_result_to"; const char kThrowOnFailureFlag[] = "throw_on_failure"; // A valid random seed must be in [1, kMaxRandomSeed]. @@ -165,6 +166,7 @@ class GTestFlagSaver { repeat_ = GTEST_FLAG(repeat); shuffle_ = GTEST_FLAG(shuffle); stack_trace_depth_ = GTEST_FLAG(stack_trace_depth); + stream_result_to_ = GTEST_FLAG(stream_result_to); throw_on_failure_ = GTEST_FLAG(throw_on_failure); } @@ -185,6 +187,7 @@ class GTestFlagSaver { GTEST_FLAG(repeat) = repeat_; GTEST_FLAG(shuffle) = shuffle_; GTEST_FLAG(stack_trace_depth) = stack_trace_depth_; + GTEST_FLAG(stream_result_to) = stream_result_to_; GTEST_FLAG(throw_on_failure) = throw_on_failure_; } private: @@ -205,6 +208,7 @@ class GTestFlagSaver { internal::Int32 repeat_; bool shuffle_; internal::Int32 stack_trace_depth_; + String stream_result_to_; bool throw_on_failure_; } GTEST_ATTRIBUTE_UNUSED_; @@ -267,7 +271,14 @@ GTEST_API_ bool ShouldRunTestOnShard( // the given predicate. template <class Container, typename Predicate> inline int CountIf(const Container& c, Predicate predicate) { - return static_cast<int>(std::count_if(c.begin(), c.end(), predicate)); + // Implemented as an explicit loop since std::count_if() in libCstd on + // Solaris has a non-standard signature. + int count = 0; + for (typename Container::const_iterator it = c.begin(); it != c.end(); ++it) { + if (predicate(*it)) + ++count; + } + return count; } // Applies a function/functor to each element in the container. @@ -340,85 +351,6 @@ class TestPropertyKeyIs { String key_; }; -class TestInfoImpl { - public: - TestInfoImpl(TestInfo* parent, const char* test_case_name, - const char* name, const char* test_case_comment, - const char* comment, TypeId fixture_class_id, - internal::TestFactoryBase* factory); - ~TestInfoImpl(); - - // Returns true if this test should run. - bool should_run() const { return should_run_; } - - // Sets the should_run member. - void set_should_run(bool should) { should_run_ = should; } - - // Returns true if this test is disabled. Disabled tests are not run. - bool is_disabled() const { return is_disabled_; } - - // Sets the is_disabled member. - void set_is_disabled(bool is) { is_disabled_ = is; } - - // Returns true if this test matches the filter specified by the user. - bool matches_filter() const { return matches_filter_; } - - // Sets the matches_filter member. - void set_matches_filter(bool matches) { matches_filter_ = matches; } - - // Returns the test case name. - const char* test_case_name() const { return test_case_name_.c_str(); } - - // Returns the test name. - const char* name() const { return name_.c_str(); } - - // Returns the test case comment. - const char* test_case_comment() const { return test_case_comment_.c_str(); } - - // Returns the test comment. - const char* comment() const { return comment_.c_str(); } - - // Returns the ID of the test fixture class. - TypeId fixture_class_id() const { return fixture_class_id_; } - - // Returns the test result. - TestResult* result() { return &result_; } - const TestResult* result() const { return &result_; } - - // Creates the test object, runs it, records its result, and then - // deletes it. - void Run(); - - // Clears the test result. - void ClearResult() { result_.Clear(); } - - // Clears the test result in the given TestInfo object. - static void ClearTestResult(TestInfo * test_info) { - test_info->impl()->ClearResult(); - } - - private: - // These fields are immutable properties of the test. - TestInfo* const parent_; // The owner of this object - const String test_case_name_; // Test case name - const String name_; // Test name - const String test_case_comment_; // Test case comment - const String comment_; // Test comment - const TypeId fixture_class_id_; // ID of the test fixture class - bool should_run_; // True iff this test should run - bool is_disabled_; // True iff this test is disabled - bool matches_filter_; // True if this test matches the - // user-specified filter. - internal::TestFactoryBase* const factory_; // The factory that creates - // the test object - - // This field is mutable and needs to be reset before running the - // test for the second time. - TestResult result_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(TestInfoImpl); -}; - // Class UnitTestOptions. // // This class contains functions for processing options the user @@ -682,10 +614,12 @@ class GTEST_API_ UnitTestImpl { // Arguments: // // test_case_name: name of the test case + // type_param: the name of the test's type parameter, or NULL if + // this is not a typed or a type-parameterized test. // set_up_tc: pointer to the function that sets up the test case // tear_down_tc: pointer to the function that tears down the test case TestCase* GetTestCase(const char* test_case_name, - const char* comment, + const char* type_param, Test::SetUpTestCaseFunc set_up_tc, Test::TearDownTestCaseFunc tear_down_tc); @@ -698,7 +632,7 @@ class GTEST_API_ UnitTestImpl { // test_info: the TestInfo object void AddTestInfo(Test::SetUpTestCaseFunc set_up_tc, Test::TearDownTestCaseFunc tear_down_tc, - TestInfo * test_info) { + TestInfo* test_info) { // In order to support thread-safe death tests, we need to // remember the original working directory when the test program // was first invoked. We cannot do this in RUN_ALL_TESTS(), as @@ -713,7 +647,7 @@ class GTEST_API_ UnitTestImpl { } GetTestCase(test_info->test_case_name(), - test_info->test_case_comment(), + test_info->type_param(), set_up_tc, tear_down_tc)->AddTestInfo(test_info); } @@ -739,24 +673,26 @@ class GTEST_API_ UnitTestImpl { } // Registers all parameterized tests defined using TEST_P and - // INSTANTIATE_TEST_P, creating regular tests for each test/parameter - // combination. This method can be called more then once; it has - // guards protecting from registering the tests more then once. - // If value-parameterized tests are disabled, RegisterParameterizedTests - // is present but does nothing. + // INSTANTIATE_TEST_CASE_P, creating regular tests for each test/parameter + // combination. This method can be called more then once; it has guards + // protecting from registering the tests more then once. If + // value-parameterized tests are disabled, RegisterParameterizedTests is + // present but does nothing. void RegisterParameterizedTests(); // Runs all tests in this UnitTest object, prints the result, and - // returns 0 if all tests are successful, or 1 otherwise. If any - // exception is thrown during a test on Windows, this test is - // considered to be failed, but the rest of the tests will still be - // run. (We disable exceptions on Linux and Mac OS X, so the issue - // doesn't apply there.) - int RunAllTests(); - - // Clears the results of all tests, including the ad hoc test. - void ClearResult() { + // returns true if all tests are successful. If any exception is + // thrown during a test, this test is considered to be failed, but + // the rest of the tests will still be run. + bool RunAllTests(); + + // Clears the results of all tests, except the ad hoc tests. + void ClearNonAdHocTestResult() { ForEach(test_cases_, TestCase::ClearTestCaseResult); + } + + // Clears the results of ad-hoc test assertions. + void ClearAdHocTestResult() { ad_hoc_test_result_.Clear(); } @@ -818,6 +754,12 @@ class GTEST_API_ UnitTestImpl { // UnitTestOptions. Must not be called before InitGoogleTest. void ConfigureXmlOutput(); +#if GTEST_CAN_STREAM_RESULTS_ + // Initializes the event listener for streaming test results to a socket. + // Must not be called before InitGoogleTest. + void ConfigureStreamingOutput(); +#endif + // Performs initialization dependent upon flag values obtained in // ParseGoogleTestFlagsOnly. Is called from InitGoogleTest after the call to // ParseGoogleTestFlagsOnly. In case a user neglects to call InitGoogleTest @@ -838,9 +780,17 @@ class GTEST_API_ UnitTestImpl { // Restores the test cases and tests to their order before the first shuffle. void UnshuffleTests(); + // Returns the value of GTEST_FLAG(catch_exceptions) at the moment + // UnitTest::Run() starts. + bool catch_exceptions() const { return catch_exceptions_; } + private: friend class ::testing::UnitTest; + // Used by UnitTest::Run() to capture the state of + // GTEST_FLAG(catch_exceptions) at the moment it starts. + void set_catch_exceptions(bool value) { catch_exceptions_ = value; } + // The UnitTest object that owns this implementation object. UnitTest* const parent_; @@ -943,6 +893,10 @@ class GTEST_API_ UnitTestImpl { // A per-thread stack of traces created by the SCOPED_TRACE() macro. internal::ThreadLocal<std::vector<TraceInfo> > gtest_trace_stack_; + // The value of GTEST_FLAG(catch_exceptions) at the moment RunAllTests() + // starts. + bool catch_exceptions_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(UnitTestImpl); }; // class UnitTestImpl @@ -952,14 +906,16 @@ inline UnitTestImpl* GetUnitTestImpl() { return UnitTest::GetInstance()->impl(); } +#if GTEST_USES_SIMPLE_RE + // Internal helper functions for implementing the simple regular // expression matcher. GTEST_API_ bool IsInSet(char ch, const char* str); -GTEST_API_ bool IsDigit(char ch); -GTEST_API_ bool IsPunct(char ch); +GTEST_API_ bool IsAsciiDigit(char ch); +GTEST_API_ bool IsAsciiPunct(char ch); GTEST_API_ bool IsRepeat(char ch); -GTEST_API_ bool IsWhiteSpace(char ch); -GTEST_API_ bool IsWordChar(char ch); +GTEST_API_ bool IsAsciiWhiteSpace(char ch); +GTEST_API_ bool IsAsciiWordChar(char ch); GTEST_API_ bool IsValidEscape(char ch); GTEST_API_ bool AtomMatchesChar(bool escaped, char pattern, char ch); GTEST_API_ bool ValidateRegex(const char* regex); @@ -968,6 +924,8 @@ GTEST_API_ bool MatchRepetitionAndRegexAtHead( bool escaped, char ch, char repeat, const char* regex, const char* str); GTEST_API_ bool MatchRegexAnywhere(const char* regex, const char* str); +#endif // GTEST_USES_SIMPLE_RE + // Parses the command line for Google Test flags, without initializing // other parts of Google Test. GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, char** argv); @@ -977,9 +935,9 @@ GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv); // Returns the message describing the last system error, regardless of the // platform. -String GetLastErrnoDescription(); +GTEST_API_ String GetLastErrnoDescription(); -#if GTEST_OS_WINDOWS +# if GTEST_OS_WINDOWS // Provides leak-safe Windows kernel handle ownership. class AutoHandle { public: @@ -1003,7 +961,7 @@ class AutoHandle { GTEST_DISALLOW_COPY_AND_ASSIGN_(AutoHandle); }; -#endif // GTEST_OS_WINDOWS +# endif // GTEST_OS_WINDOWS // Attempts to parse a string into a positive integer pointed to by the // number parameter. Returns true if that is possible. @@ -1014,7 +972,7 @@ bool ParseNaturalNumber(const ::std::string& str, Integer* number) { // Fail fast if the given string does not begin with a digit; // this bypasses strtoXXX's "optional leading whitespace and plus // or minus sign" semantics, which are undesirable here. - if (str.empty() || !isdigit(str[0])) { + if (str.empty() || !IsDigit(str[0])) { return false; } errno = 0; @@ -1022,14 +980,20 @@ bool ParseNaturalNumber(const ::std::string& str, Integer* number) { char* end; // BiggestConvertible is the largest integer type that system-provided // string-to-number conversion routines can return. -#if GTEST_OS_WINDOWS && !defined(__GNUC__) + +# if GTEST_OS_WINDOWS && !defined(__GNUC__) + // MSVC and C++ Builder define __int64 instead of the standard long long. typedef unsigned __int64 BiggestConvertible; const BiggestConvertible parsed = _strtoui64(str.c_str(), &end, 10); -#else + +# else + typedef unsigned long long BiggestConvertible; // NOLINT const BiggestConvertible parsed = strtoull(str.c_str(), &end, 10); -#endif // GTEST_OS_WINDOWS && !defined(__GNUC__) + +# endif // GTEST_OS_WINDOWS && !defined(__GNUC__) + const bool parse_success = *end == '\0' && errno == 0; // TODO(vladl@google.com): Convert this to compile time assertion when it is diff --git a/utils/unittest/googletest/include/gtest/internal/gtest-internal.h b/utils/unittest/googletest/include/gtest/internal/gtest-internal.h index 0b90132..b198a3d 100644 --- a/utils/unittest/googletest/include/gtest/internal/gtest-internal.h +++ b/utils/unittest/googletest/include/gtest/internal/gtest-internal.h @@ -37,13 +37,13 @@ #ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ #define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ -#include <gtest/internal/gtest-port.h> +#include "gtest/internal/gtest-port.h" #if GTEST_OS_LINUX -#include <stdlib.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <unistd.h> +# include <stdlib.h> +# include <sys/types.h> +# include <sys/wait.h> +# include <unistd.h> #endif // GTEST_OS_LINUX #include <ctype.h> @@ -52,9 +52,9 @@ #include <limits> #include <set> -#include <gtest/internal/gtest-string.h> -#include <gtest/internal/gtest-filepath.h> -#include <gtest/internal/gtest-type-util.h> +#include "gtest/internal/gtest-string.h" +#include "gtest/internal/gtest-filepath.h" +#include "gtest/internal/gtest-type-util.h" #include "llvm/Support/raw_os_ostream.h" @@ -117,9 +117,12 @@ inline void GTestStreamToHelper(std::ostream* os, const T& val) { cos << val; } +class ProtocolMessage; +namespace proto2 { class Message; } + namespace testing { -// Forward declaration of classes. +// Forward declarations. class AssertionResult; // Result of an assertion. class Message; // Represents a failure message. @@ -128,6 +131,9 @@ class TestInfo; // Information about a test. class TestPartResult; // Result of a test part. class UnitTest; // A collection of test cases. +template <typename T> +::std::string PrintToString(const T& value); + namespace internal { struct TraceInfo; // Information about a trace point. @@ -170,9 +176,9 @@ char (&IsNullLiteralHelper(...))[2]; // NOLINT #ifdef GTEST_ELLIPSIS_NEEDS_POD_ // We lose support for NULL detection where the compiler doesn't like // passing non-POD classes through ellipsis (...). -#define GTEST_IS_NULL_LITERAL_(x) false +# define GTEST_IS_NULL_LITERAL_(x) false #else -#define GTEST_IS_NULL_LITERAL_(x) \ +# define GTEST_IS_NULL_LITERAL_(x) \ (sizeof(::testing::internal::IsNullLiteralHelper(x)) == 1) #endif // GTEST_ELLIPSIS_NEEDS_POD_ @@ -209,72 +215,32 @@ class GTEST_API_ ScopedTrace { template <typename T> String StreamableToString(const T& streamable); -// Formats a value to be used in a failure message. - -#ifdef GTEST_NEEDS_IS_POINTER_ - -// These are needed as the Nokia Symbian and IBM XL C/C++ compilers -// cannot decide between const T& and const T* in a function template. -// These compilers _can_ decide between class template specializations -// for T and T*, so a tr1::type_traits-like is_pointer works, and we -// can overload on that. - -// This overload makes sure that all pointers (including -// those to char or wchar_t) are printed as raw pointers. -template <typename T> -inline String FormatValueForFailureMessage(internal::true_type /*dummy*/, - T* pointer) { - return StreamableToString(static_cast<const void*>(pointer)); -} - -template <typename T> -inline String FormatValueForFailureMessage(internal::false_type /*dummy*/, - const T& value) { - return StreamableToString(value); -} - -template <typename T> -inline String FormatForFailureMessage(const T& value) { - return FormatValueForFailureMessage( - typename internal::is_pointer<T>::type(), value); -} - +// The Symbian compiler has a bug that prevents it from selecting the +// correct overload of FormatForComparisonFailureMessage (see below) +// unless we pass the first argument by reference. If we do that, +// however, Visual Age C++ 10.1 generates a compiler error. Therefore +// we only apply the work-around for Symbian. +#if defined(__SYMBIAN32__) +# define GTEST_CREF_WORKAROUND_ const& #else +# define GTEST_CREF_WORKAROUND_ +#endif -// These are needed as the above solution using is_pointer has the -// limitation that T cannot be a type without external linkage, when -// compiled using MSVC. - -template <typename T> -inline String FormatForFailureMessage(const T& value) { - return StreamableToString(value); -} - -// This overload makes sure that all pointers (including -// those to char or wchar_t) are printed as raw pointers. -template <typename T> -inline String FormatForFailureMessage(T* pointer) { - return StreamableToString(static_cast<const void*>(pointer)); -} - -#endif // GTEST_NEEDS_IS_POINTER_ - -// These overloaded versions handle narrow and wide characters. -GTEST_API_ String FormatForFailureMessage(char ch); -GTEST_API_ String FormatForFailureMessage(wchar_t wchar); - -// When this operand is a const char* or char*, and the other operand +// When this operand is a const char* or char*, if the other operand // is a ::std::string or ::string, we print this operand as a C string -// rather than a pointer. We do the same for wide strings. +// rather than a pointer (we do the same for wide strings); otherwise +// we print it as a pointer to be safe. // This internal macro is used to avoid duplicated code. #define GTEST_FORMAT_IMPL_(operand2_type, operand1_printer)\ inline String FormatForComparisonFailureMessage(\ - operand2_type::value_type* str, const operand2_type& /*operand2*/) {\ + operand2_type::value_type* GTEST_CREF_WORKAROUND_ str, \ + const operand2_type& /*operand2*/) {\ return operand1_printer(str);\ }\ inline String FormatForComparisonFailureMessage(\ - const operand2_type::value_type* str, const operand2_type& /*operand2*/) {\ + const operand2_type::value_type* GTEST_CREF_WORKAROUND_ str, \ + const operand2_type& /*operand2*/) {\ return operand1_printer(str);\ } @@ -292,6 +258,24 @@ GTEST_FORMAT_IMPL_(::wstring, String::ShowWideCStringQuoted) #undef GTEST_FORMAT_IMPL_ +// The next four overloads handle the case where the operand being +// printed is a char/wchar_t pointer and the other operand is not a +// string/wstring object. In such cases, we just print the operand as +// a pointer to be safe. +#define GTEST_FORMAT_CHAR_PTR_IMPL_(CharType) \ + template <typename T> \ + String FormatForComparisonFailureMessage(CharType* GTEST_CREF_WORKAROUND_ p, \ + const T&) { \ + return PrintToString(static_cast<const void*>(p)); \ + } + +GTEST_FORMAT_CHAR_PTR_IMPL_(char) +GTEST_FORMAT_CHAR_PTR_IMPL_(const char) +GTEST_FORMAT_CHAR_PTR_IMPL_(wchar_t) +GTEST_FORMAT_CHAR_PTR_IMPL_(const wchar_t) + +#undef GTEST_FORMAT_CHAR_PTR_IMPL_ + // Constructs and returns the message for an equality assertion // (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure. // @@ -578,20 +562,6 @@ GTEST_API_ AssertionResult IsHRESULTFailure(const char* expr, #endif // GTEST_OS_WINDOWS -// Formats a source file path and a line number as they would appear -// in a compiler error message. -inline String FormatFileLocation(const char* file, int line) { - const char* const file_name = file == NULL ? "unknown file" : file; - if (line < 0) { - return String::Format("%s:", file_name); - } -#ifdef _MSC_VER - return String::Format("%s(%d):", file_name, line); -#else - return String::Format("%s:%d:", file_name, line); -#endif // _MSC_VER -} - // Types of SetUpTestCase() and TearDownTestCase() functions. typedef void (*SetUpTestCaseFunc)(); typedef void (*TearDownTestCaseFunc)(); @@ -603,10 +573,10 @@ typedef void (*TearDownTestCaseFunc)(); // // test_case_name: name of the test case // name: name of the test -// test_case_comment: a comment on the test case that will be included in -// the test output -// comment: a comment on the test that will be included in the -// test output +// type_param the name of the test's type parameter, or NULL if +// this is not a typed or a type-parameterized test. +// value_param text representation of the test's value parameter, +// or NULL if this is not a type-parameterized test. // fixture_class_id: ID of the test fixture class // set_up_tc: pointer to the function that sets up the test case // tear_down_tc: pointer to the function that tears down the test case @@ -615,7 +585,8 @@ typedef void (*TearDownTestCaseFunc)(); // ownership of the factory object. GTEST_API_ TestInfo* MakeAndRegisterTestInfo( const char* test_case_name, const char* name, - const char* test_case_comment, const char* comment, + const char* type_param, + const char* value_param, TypeId fixture_class_id, SetUpTestCaseFunc set_up_tc, TearDownTestCaseFunc tear_down_tc, @@ -624,7 +595,7 @@ GTEST_API_ TestInfo* MakeAndRegisterTestInfo( // If *pstr starts with the given prefix, modifies *pstr to be right // past the prefix and returns true; otherwise leaves *pstr unchanged // and returns false. None of pstr, *pstr, and prefix can be NULL. -bool SkipPrefix(const char* prefix, const char** pstr); +GTEST_API_ bool SkipPrefix(const char* prefix, const char** pstr); #if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P @@ -667,7 +638,7 @@ inline const char* SkipComma(const char* str) { if (comma == NULL) { return NULL; } - while (isspace(*(++comma))) {} + while (IsSpace(*(++comma))) {} return comma; } @@ -704,8 +675,8 @@ class TypeParameterizedTest { String::Format("%s%s%s/%d", prefix, prefix[0] == '\0' ? "" : "/", case_name, index).c_str(), GetPrefixUntilComma(test_names).c_str(), - String::Format("TypeParam = %s", GetTypeName<Type>().c_str()).c_str(), - "", + GetTypeName<Type>().c_str(), + NULL, // No value parameter. GetTypeId<FixtureClass>(), TestClass::SetUpTestCase, TestClass::TearDownTestCase, @@ -782,6 +753,15 @@ GTEST_API_ bool AlwaysTrue(); // Always returns false. inline bool AlwaysFalse() { return !AlwaysTrue(); } +// Helper for suppressing false warning from Clang on a const char* +// variable declared in a conditional expression always being NULL in +// the else branch. +struct GTEST_API_ ConstCharPtr { + ConstCharPtr(const char* str) : value(str) {} + operator bool() const { return true; } + const char* value; +}; + // A simple Linear Congruential Generator for generating random // numbers with a uniform distribution. Unlike rand() and srand(), it // doesn't use global state (and therefore can't interfere with user @@ -804,13 +784,338 @@ class GTEST_API_ Random { GTEST_DISALLOW_COPY_AND_ASSIGN_(Random); }; +// Defining a variable of type CompileAssertTypesEqual<T1, T2> will cause a +// compiler error iff T1 and T2 are different types. +template <typename T1, typename T2> +struct CompileAssertTypesEqual; + +template <typename T> +struct CompileAssertTypesEqual<T, T> { +}; + +// Removes the reference from a type if it is a reference type, +// otherwise leaves it unchanged. This is the same as +// tr1::remove_reference, which is not widely available yet. +template <typename T> +struct RemoveReference { typedef T type; }; // NOLINT +template <typename T> +struct RemoveReference<T&> { typedef T type; }; // NOLINT + +// A handy wrapper around RemoveReference that works when the argument +// T depends on template parameters. +#define GTEST_REMOVE_REFERENCE_(T) \ + typename ::testing::internal::RemoveReference<T>::type + +// Removes const from a type if it is a const type, otherwise leaves +// it unchanged. This is the same as tr1::remove_const, which is not +// widely available yet. +template <typename T> +struct RemoveConst { typedef T type; }; // NOLINT +template <typename T> +struct RemoveConst<const T> { typedef T type; }; // NOLINT + +// MSVC 8.0, Sun C++, and IBM XL C++ have a bug which causes the above +// definition to fail to remove the const in 'const int[3]' and 'const +// char[3][4]'. The following specialization works around the bug. +// However, it causes trouble with GCC and thus needs to be +// conditionally compiled. +#if defined(_MSC_VER) || defined(__SUNPRO_CC) || defined(__IBMCPP__) +template <typename T, size_t N> +struct RemoveConst<const T[N]> { + typedef typename RemoveConst<T>::type type[N]; +}; +#endif + +// A handy wrapper around RemoveConst that works when the argument +// T depends on template parameters. +#define GTEST_REMOVE_CONST_(T) \ + typename ::testing::internal::RemoveConst<T>::type + +// Turns const U&, U&, const U, and U all into U. +#define GTEST_REMOVE_REFERENCE_AND_CONST_(T) \ + GTEST_REMOVE_CONST_(GTEST_REMOVE_REFERENCE_(T)) + +// Adds reference to a type if it is not a reference type, +// otherwise leaves it unchanged. This is the same as +// tr1::add_reference, which is not widely available yet. +template <typename T> +struct AddReference { typedef T& type; }; // NOLINT +template <typename T> +struct AddReference<T&> { typedef T& type; }; // NOLINT + +// A handy wrapper around AddReference that works when the argument T +// depends on template parameters. +#define GTEST_ADD_REFERENCE_(T) \ + typename ::testing::internal::AddReference<T>::type + +// Adds a reference to const on top of T as necessary. For example, +// it transforms +// +// char ==> const char& +// const char ==> const char& +// char& ==> const char& +// const char& ==> const char& +// +// The argument T must depend on some template parameters. +#define GTEST_REFERENCE_TO_CONST_(T) \ + GTEST_ADD_REFERENCE_(const GTEST_REMOVE_REFERENCE_(T)) + +// ImplicitlyConvertible<From, To>::value is a compile-time bool +// constant that's true iff type From can be implicitly converted to +// type To. +template <typename From, typename To> +class ImplicitlyConvertible { + private: + // We need the following helper functions only for their types. + // They have no implementations. + + // MakeFrom() is an expression whose type is From. We cannot simply + // use From(), as the type From may not have a public default + // constructor. + static From MakeFrom(); + + // These two functions are overloaded. Given an expression + // Helper(x), the compiler will pick the first version if x can be + // implicitly converted to type To; otherwise it will pick the + // second version. + // + // The first version returns a value of size 1, and the second + // version returns a value of size 2. Therefore, by checking the + // size of Helper(x), which can be done at compile time, we can tell + // which version of Helper() is used, and hence whether x can be + // implicitly converted to type To. + static char Helper(To); + static char (&Helper(...))[2]; // NOLINT + + // We have to put the 'public' section after the 'private' section, + // or MSVC refuses to compile the code. + public: + // MSVC warns about implicitly converting from double to int for + // possible loss of data, so we need to temporarily disable the + // warning. +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4244) // Temporarily disables warning 4244. + + static const bool value = + sizeof(Helper(ImplicitlyConvertible::MakeFrom())) == 1; +# pragma warning(pop) // Restores the warning state. +#elif defined(__BORLANDC__) + // C++Builder cannot use member overload resolution during template + // instantiation. The simplest workaround is to use its C++0x type traits + // functions (C++Builder 2009 and above only). + static const bool value = __is_convertible(From, To); +#else + static const bool value = + sizeof(Helper(ImplicitlyConvertible::MakeFrom())) == 1; +#endif // _MSV_VER +}; +template <typename From, typename To> +const bool ImplicitlyConvertible<From, To>::value; + +// IsAProtocolMessage<T>::value is a compile-time bool constant that's +// true iff T is type ProtocolMessage, proto2::Message, or a subclass +// of those. +template <typename T> +struct IsAProtocolMessage + : public bool_constant< + ImplicitlyConvertible<const T*, const ::ProtocolMessage*>::value || + ImplicitlyConvertible<const T*, const ::proto2::Message*>::value> { +}; + +// When the compiler sees expression IsContainerTest<C>(0), if C is an +// STL-style container class, the first overload of IsContainerTest +// will be viable (since both C::iterator* and C::const_iterator* are +// valid types and NULL can be implicitly converted to them). It will +// be picked over the second overload as 'int' is a perfect match for +// the type of argument 0. If C::iterator or C::const_iterator is not +// a valid type, the first overload is not viable, and the second +// overload will be picked. Therefore, we can determine whether C is +// a container class by checking the type of IsContainerTest<C>(0). +// The value of the expression is insignificant. +// +// Note that we look for both C::iterator and C::const_iterator. The +// reason is that C++ injects the name of a class as a member of the +// class itself (e.g. you can refer to class iterator as either +// 'iterator' or 'iterator::iterator'). If we look for C::iterator +// only, for example, we would mistakenly think that a class named +// iterator is an STL container. +// +// Also note that the simpler approach of overloading +// IsContainerTest(typename C::const_iterator*) and +// IsContainerTest(...) doesn't work with Visual Age C++ and Sun C++. +typedef int IsContainer; +template <class C> +IsContainer IsContainerTest(int /* dummy */, + typename C::iterator* /* it */ = NULL, + typename C::const_iterator* /* const_it */ = NULL) { + return 0; +} + +typedef char IsNotContainer; +template <class C> +IsNotContainer IsContainerTest(long /* dummy */) { return '\0'; } + +// EnableIf<condition>::type is void when 'Cond' is true, and +// undefined when 'Cond' is false. To use SFINAE to make a function +// overload only apply when a particular expression is true, add +// "typename EnableIf<expression>::type* = 0" as the last parameter. +template<bool> struct EnableIf; +template<> struct EnableIf<true> { typedef void type; }; // NOLINT + +// Utilities for native arrays. + +// ArrayEq() compares two k-dimensional native arrays using the +// elements' operator==, where k can be any integer >= 0. When k is +// 0, ArrayEq() degenerates into comparing a single pair of values. + +template <typename T, typename U> +bool ArrayEq(const T* lhs, size_t size, const U* rhs); + +// This generic version is used when k is 0. +template <typename T, typename U> +inline bool ArrayEq(const T& lhs, const U& rhs) { return lhs == rhs; } + +// This overload is used when k >= 1. +template <typename T, typename U, size_t N> +inline bool ArrayEq(const T(&lhs)[N], const U(&rhs)[N]) { + return internal::ArrayEq(lhs, N, rhs); +} + +// This helper reduces code bloat. If we instead put its logic inside +// the previous ArrayEq() function, arrays with different sizes would +// lead to different copies of the template code. +template <typename T, typename U> +bool ArrayEq(const T* lhs, size_t size, const U* rhs) { + for (size_t i = 0; i != size; i++) { + if (!internal::ArrayEq(lhs[i], rhs[i])) + return false; + } + return true; +} + +// Finds the first element in the iterator range [begin, end) that +// equals elem. Element may be a native array type itself. +template <typename Iter, typename Element> +Iter ArrayAwareFind(Iter begin, Iter end, const Element& elem) { + for (Iter it = begin; it != end; ++it) { + if (internal::ArrayEq(*it, elem)) + return it; + } + return end; +} + +// CopyArray() copies a k-dimensional native array using the elements' +// operator=, where k can be any integer >= 0. When k is 0, +// CopyArray() degenerates into copying a single value. + +template <typename T, typename U> +void CopyArray(const T* from, size_t size, U* to); + +// This generic version is used when k is 0. +template <typename T, typename U> +inline void CopyArray(const T& from, U* to) { *to = from; } + +// This overload is used when k >= 1. +template <typename T, typename U, size_t N> +inline void CopyArray(const T(&from)[N], U(*to)[N]) { + internal::CopyArray(from, N, *to); +} + +// This helper reduces code bloat. If we instead put its logic inside +// the previous CopyArray() function, arrays with different sizes +// would lead to different copies of the template code. +template <typename T, typename U> +void CopyArray(const T* from, size_t size, U* to) { + for (size_t i = 0; i != size; i++) { + internal::CopyArray(from[i], to + i); + } +} + +// The relation between an NativeArray object (see below) and the +// native array it represents. +enum RelationToSource { + kReference, // The NativeArray references the native array. + kCopy // The NativeArray makes a copy of the native array and + // owns the copy. +}; + +// Adapts a native array to a read-only STL-style container. Instead +// of the complete STL container concept, this adaptor only implements +// members useful for Google Mock's container matchers. New members +// should be added as needed. To simplify the implementation, we only +// support Element being a raw type (i.e. having no top-level const or +// reference modifier). It's the client's responsibility to satisfy +// this requirement. Element can be an array type itself (hence +// multi-dimensional arrays are supported). +template <typename Element> +class NativeArray { + public: + // STL-style container typedefs. + typedef Element value_type; + typedef Element* iterator; + typedef const Element* const_iterator; + + // Constructs from a native array. + NativeArray(const Element* array, size_t count, RelationToSource relation) { + Init(array, count, relation); + } + + // Copy constructor. + NativeArray(const NativeArray& rhs) { + Init(rhs.array_, rhs.size_, rhs.relation_to_source_); + } + + ~NativeArray() { + // Ensures that the user doesn't instantiate NativeArray with a + // const or reference type. + static_cast<void>(StaticAssertTypeEqHelper<Element, + GTEST_REMOVE_REFERENCE_AND_CONST_(Element)>()); + if (relation_to_source_ == kCopy) + delete[] array_; + } + + // STL-style container methods. + size_t size() const { return size_; } + const_iterator begin() const { return array_; } + const_iterator end() const { return array_ + size_; } + bool operator==(const NativeArray& rhs) const { + return size() == rhs.size() && + ArrayEq(begin(), size(), rhs.begin()); + } + + private: + // Initializes this object; makes a copy of the input array if + // 'relation' is kCopy. + void Init(const Element* array, size_t a_size, RelationToSource relation) { + if (relation == kReference) { + array_ = array; + } else { + Element* const copy = new Element[a_size]; + CopyArray(array, a_size, copy); + array_ = copy; + } + size_ = a_size; + relation_to_source_ = relation; + } + + const Element* array_; + size_t size_; + RelationToSource relation_to_source_; + + GTEST_DISALLOW_ASSIGN_(NativeArray); +}; + } // namespace internal } // namespace testing -#define GTEST_MESSAGE_(message, result_type) \ - ::testing::internal::AssertHelper(result_type, __FILE__, __LINE__, message) \ +#define GTEST_MESSAGE_AT_(file, line, message, result_type) \ + ::testing::internal::AssertHelper(result_type, file, line, message) \ = ::testing::Message() +#define GTEST_MESSAGE_(message, result_type) \ + GTEST_MESSAGE_AT_(__FILE__, __LINE__, message, result_type) + #define GTEST_FATAL_FAILURE_(message) \ return GTEST_MESSAGE_(message, ::testing::TestPartResult::kFatalFailure) @@ -828,7 +1133,7 @@ class GTEST_API_ Random { #define GTEST_TEST_THROW_(statement, expected_exception, fail) \ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ - if (const char* gtest_msg = "") { \ + if (::testing::internal::ConstCharPtr gtest_msg = "") { \ bool gtest_caught_expected = false; \ try { \ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ @@ -837,38 +1142,38 @@ class GTEST_API_ Random { gtest_caught_expected = true; \ } \ catch (...) { \ - gtest_msg = "Expected: " #statement " throws an exception of type " \ - #expected_exception ".\n Actual: it throws a different " \ - "type."; \ + gtest_msg.value = \ + "Expected: " #statement " throws an exception of type " \ + #expected_exception ".\n Actual: it throws a different type."; \ goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ } \ if (!gtest_caught_expected) { \ - gtest_msg = "Expected: " #statement " throws an exception of type " \ - #expected_exception ".\n Actual: it throws nothing."; \ + gtest_msg.value = \ + "Expected: " #statement " throws an exception of type " \ + #expected_exception ".\n Actual: it throws nothing."; \ goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ } \ } else \ GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__): \ - fail(gtest_msg) + fail(gtest_msg.value) #define GTEST_TEST_NO_THROW_(statement, fail) \ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ - if (const char* gtest_msg = "") { \ + if (::testing::internal::AlwaysTrue()) { \ try { \ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ } \ catch (...) { \ - gtest_msg = "Expected: " #statement " doesn't throw an exception.\n" \ - " Actual: it throws."; \ goto GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__); \ } \ } else \ GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__): \ - fail(gtest_msg) + fail("Expected: " #statement " doesn't throw an exception.\n" \ + " Actual: it throws.") #define GTEST_TEST_ANY_THROW_(statement, fail) \ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ - if (const char* gtest_msg = "") { \ + if (::testing::internal::AlwaysTrue()) { \ bool gtest_caught_any = false; \ try { \ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ @@ -877,13 +1182,12 @@ class GTEST_API_ Random { gtest_caught_any = true; \ } \ if (!gtest_caught_any) { \ - gtest_msg = "Expected: " #statement " throws an exception.\n" \ - " Actual: it doesn't."; \ goto GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__); \ } \ } else \ GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__): \ - fail(gtest_msg) + fail("Expected: " #statement " throws an exception.\n" \ + " Actual: it doesn't.") // Implements Boolean test assertions such as EXPECT_TRUE. expression can be @@ -900,18 +1204,17 @@ class GTEST_API_ Random { #define GTEST_TEST_NO_FATAL_FAILURE_(statement, fail) \ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ - if (const char* gtest_msg = "") { \ + if (::testing::internal::AlwaysTrue()) { \ ::testing::internal::HasNewFatalFailureHelper gtest_fatal_failure_checker; \ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ if (gtest_fatal_failure_checker.has_new_fatal_failure()) { \ - gtest_msg = "Expected: " #statement " doesn't generate new fatal " \ - "failures in the current thread.\n" \ - " Actual: it does."; \ goto GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__); \ } \ } else \ GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__): \ - fail(gtest_msg) + fail("Expected: " #statement " doesn't generate new fatal " \ + "failures in the current thread.\n" \ + " Actual: it does.") // Expands to the name of the class that implements the given test. #define GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \ @@ -924,7 +1227,7 @@ class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) : public parent_class {\ GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {}\ private:\ virtual void TestBody();\ - static ::testing::TestInfo* const test_info_;\ + static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_;\ GTEST_DISALLOW_COPY_AND_ASSIGN_(\ GTEST_TEST_CLASS_NAME_(test_case_name, test_name));\ };\ @@ -932,7 +1235,7 @@ class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) : public parent_class {\ ::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_case_name, test_name)\ ::test_info_ =\ ::testing::internal::MakeAndRegisterTestInfo(\ - #test_case_name, #test_name, "", "", \ + #test_case_name, #test_name, NULL, NULL, \ (parent_id), \ parent_class::SetUpTestCase, \ parent_class::TearDownTestCase, \ diff --git a/utils/unittest/googletest/include/gtest/internal/gtest-linked_ptr.h b/utils/unittest/googletest/include/gtest/internal/gtest-linked_ptr.h index 2404ea8..57147b4 100644 --- a/utils/unittest/googletest/include/gtest/internal/gtest-linked_ptr.h +++ b/utils/unittest/googletest/include/gtest/internal/gtest-linked_ptr.h @@ -71,7 +71,7 @@ #include <stdlib.h> #include <assert.h> -#include <gtest/internal/gtest-port.h> +#include "gtest/internal/gtest-port.h" namespace testing { namespace internal { @@ -172,16 +172,6 @@ class linked_ptr { T* get() const { return value_; } T* operator->() const { return value_; } T& operator*() const { return *value_; } - // Release ownership of the pointed object and returns it. - // Sole ownership by this linked_ptr object is required. - T* release() { - bool last = link_.depart(); - (void) last; - assert(last); - T* v = value_; - value_ = NULL; - return v; - } bool operator==(T* p) const { return value_ == p; } bool operator!=(T* p) const { return value_ != p; } diff --git a/utils/unittest/googletest/include/gtest/internal/gtest-param-util-generated.h b/utils/unittest/googletest/include/gtest/internal/gtest-param-util-generated.h index ab4ab56..2582675 100644 --- a/utils/unittest/googletest/include/gtest/internal/gtest-param-util-generated.h +++ b/utils/unittest/googletest/include/gtest/internal/gtest-param-util-generated.h @@ -1,4 +1,6 @@ -// This file was GENERATED by a script. DO NOT EDIT BY HAND!!! +// This file was GENERATED by command: +// pump.py gtest-param-util-generated.h.pump +// DO NOT EDIT BY HAND!!! // Copyright 2008 Google Inc. // All Rights Reserved. @@ -47,8 +49,8 @@ // scripts/fuse_gtest.py depends on gtest's own header being #included // *unconditionally*. Therefore these #includes cannot be moved // inside #if GTEST_HAS_PARAM_TEST. -#include <gtest/internal/gtest-param-util.h> -#include <gtest/internal/gtest-port.h> +#include "gtest/internal/gtest-param-util.h" +#include "gtest/internal/gtest-port.h" #if GTEST_HAS_PARAM_TEST @@ -58,8 +60,8 @@ namespace testing { // include/gtest/gtest-param-test.h. template <typename ForwardIterator> internal::ParamGenerator< - typename ::std::iterator_traits<ForwardIterator>::value_type> ValuesIn( - ForwardIterator begin, ForwardIterator end); + typename ::testing::internal::IteratorTraits<ForwardIterator>::value_type> +ValuesIn(ForwardIterator begin, ForwardIterator end); template <typename T, size_t N> internal::ParamGenerator<T> ValuesIn(const T (&array)[N]); @@ -2826,7 +2828,7 @@ class ValueArray50 { const T50 v50_; }; -#if GTEST_HAS_COMBINE +# if GTEST_HAS_COMBINE // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. // // Generates values from the Cartesian product of values produced @@ -4810,7 +4812,7 @@ CartesianProductHolder10(const Generator1& g1, const Generator2& g2, const Generator10 g10_; }; // class CartesianProductHolder10 -#endif // GTEST_HAS_COMBINE +# endif // GTEST_HAS_COMBINE } // namespace internal } // namespace testing diff --git a/utils/unittest/googletest/include/gtest/internal/gtest-param-util.h b/utils/unittest/googletest/include/gtest/internal/gtest-param-util.h index 0cbb58c..0ef9718 100644 --- a/utils/unittest/googletest/include/gtest/internal/gtest-param-util.h +++ b/utils/unittest/googletest/include/gtest/internal/gtest-param-util.h @@ -41,9 +41,10 @@ // scripts/fuse_gtest.py depends on gtest's own header being #included // *unconditionally*. Therefore these #includes cannot be moved // inside #if GTEST_HAS_PARAM_TEST. -#include <gtest/internal/gtest-internal.h> -#include <gtest/internal/gtest-linked_ptr.h> -#include <gtest/internal/gtest-port.h> +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-linked_ptr.h" +#include "gtest/internal/gtest-port.h" +#include "gtest/gtest-printers.h" #if GTEST_HAS_PARAM_TEST @@ -171,7 +172,7 @@ class ParamGenerator { iterator end() const { return iterator(impl_->End()); } private: - ::testing::internal::linked_ptr<const ParamGeneratorInterface<T> > impl_; + linked_ptr<const ParamGeneratorInterface<T> > impl_; }; // Generates values from a range of two comparable values. Can be used to @@ -285,7 +286,7 @@ class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface<T> { public: Iterator(const ParamGeneratorInterface<T>* base, typename ContainerType::const_iterator iterator) - : base_(base), iterator_(iterator) {} + : base_(base), iterator_(iterator) {} virtual ~Iterator() {} virtual const ParamGeneratorInterface<T>* BaseGenerator() const { @@ -416,7 +417,7 @@ class ParameterizedTestCaseInfoBase { virtual ~ParameterizedTestCaseInfoBase() {} // Base part of test case name for display purposes. - virtual const String& GetTestCaseName() const = 0; + virtual const string& GetTestCaseName() const = 0; // Test case id to verify identity. virtual TypeId GetTestCaseTypeId() const = 0; // UnitTest class invokes this method to register tests in this @@ -453,7 +454,7 @@ class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase { : test_case_name_(name) {} // Test case base name for display purposes. - virtual const String& GetTestCaseName() const { return test_case_name_; } + virtual const string& GetTestCaseName() const { return test_case_name_; } // Test case id to verify identity. virtual TypeId GetTestCaseTypeId() const { return GetTypeId<TestCase>(); } // TEST_P macro uses AddTestPattern() to record information @@ -471,7 +472,7 @@ class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase { } // INSTANTIATE_TEST_CASE_P macro uses AddGenerator() to record information // about a generator. - int AddTestCaseInstantiation(const char* instantiation_name, + int AddTestCaseInstantiation(const string& instantiation_name, GeneratorCreationFunc* func, const char* /* file */, int /* line */) { @@ -490,26 +491,25 @@ class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase { for (typename InstantiationContainer::iterator gen_it = instantiations_.begin(); gen_it != instantiations_.end(); ++gen_it) { - const String& instantiation_name = gen_it->first; + const string& instantiation_name = gen_it->first; ParamGenerator<ParamType> generator((*gen_it->second)()); Message test_case_name_stream; if ( !instantiation_name.empty() ) - test_case_name_stream << instantiation_name.c_str() << "/"; - test_case_name_stream << test_info->test_case_base_name.c_str(); + test_case_name_stream << instantiation_name << "/"; + test_case_name_stream << test_info->test_case_base_name; int i = 0; for (typename ParamGenerator<ParamType>::iterator param_it = generator.begin(); param_it != generator.end(); ++param_it, ++i) { Message test_name_stream; - test_name_stream << test_info->test_base_name.c_str() << "/" << i; - ::testing::internal::MakeAndRegisterTestInfo( + test_name_stream << test_info->test_base_name << "/" << i; + MakeAndRegisterTestInfo( test_case_name_stream.GetString().c_str(), test_name_stream.GetString().c_str(), - "", // test_case_comment - "", // comment; TODO(vladl@google.com): provide parameter value - // representation. + NULL, // No type parameter. + PrintToString(*param_it).c_str(), GetTestCaseTypeId(), TestCase::SetUpTestCase, TestCase::TearDownTestCase, @@ -530,17 +530,17 @@ class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase { test_base_name(a_test_base_name), test_meta_factory(a_test_meta_factory) {} - const String test_case_base_name; - const String test_base_name; + const string test_case_base_name; + const string test_base_name; const scoped_ptr<TestMetaFactoryBase<ParamType> > test_meta_factory; }; typedef ::std::vector<linked_ptr<TestInfo> > TestInfoContainer; // Keeps pairs of <Instantiation name, Sequence generator creation function> // received from INSTANTIATE_TEST_CASE_P macros. - typedef ::std::vector<std::pair<String, GeneratorCreationFunc*> > + typedef ::std::vector<std::pair<string, GeneratorCreationFunc*> > InstantiationContainer; - const String test_case_name_; + const string test_case_name_; TestInfoContainer tests_; InstantiationContainer instantiations_; @@ -579,7 +579,7 @@ class ParameterizedTestCaseRegistry { // and terminate the program since we cannot guaranty correct // test case setup and tear-down in this case. ReportInvalidTestCaseType(test_case_name, file, line); - abort(); + posix::Abort(); } else { // At this point we are sure that the object we found is of the same // type we are looking for, so we downcast it to that type diff --git a/utils/unittest/googletest/include/gtest/internal/gtest-port.h b/utils/unittest/googletest/include/gtest/internal/gtest-port.h index 3d076eb..8ef5d7d 100644 --- a/utils/unittest/googletest/include/gtest/internal/gtest-port.h +++ b/utils/unittest/googletest/include/gtest/internal/gtest-port.h @@ -50,6 +50,8 @@ // GTEST_HAS_GLOBAL_WSTRING - Define it to 1/0 to indicate that ::string // is/isn't available (some systems define // ::wstring, which is different to std::wstring). +// GTEST_HAS_POSIX_RE - Define it to 1/0 to indicate that POSIX regular +// expressions are/aren't available. // GTEST_HAS_PTHREAD - Define it to 1/0 to indicate that <pthread.h> // is/isn't available. // GTEST_HAS_RTTI - Define it to 1/0 to indicate that RTTI is/isn't @@ -62,6 +64,10 @@ // GTEST_HAS_SEH - Define it to 1/0 to indicate whether the // compiler supports Microsoft's "Structured // Exception Handling". +// GTEST_HAS_STREAM_REDIRECTION +// - Define it to 1/0 to indicate whether the +// platform supports I/O stream redirection using +// dup() and dup2(). // GTEST_USE_OWN_TR1_TUPLE - Define it to 1/0 to indicate whether Google // Test's own tr1 tuple implementation should be // used. Unused when the user sets @@ -81,8 +87,11 @@ // GTEST_OS_AIX - IBM AIX // GTEST_OS_CYGWIN - Cygwin // GTEST_OS_HAIKU - Haiku +// GTEST_OS_HPUX - HP-UX // GTEST_OS_LINUX - Linux +// GTEST_OS_LINUX_ANDROID - Google Android // GTEST_OS_MAC - Mac OS X +// GTEST_OS_NACL - Google Native Client (NaCl) // GTEST_OS_SOLARIS - Sun Solaris // GTEST_OS_SYMBIAN - Symbian // GTEST_OS_WINDOWS - Windows (Desktop, MinGW, or Mobile) @@ -108,7 +117,9 @@ // GTEST_HAS_PARAM_TEST - value-parameterized tests // GTEST_HAS_TYPED_TEST - typed tests // GTEST_HAS_TYPED_TEST_P - type-parameterized tests -// GTEST_USES_POSIX_RE - enhanced POSIX regex is used. +// GTEST_USES_POSIX_RE - enhanced POSIX regex is used. Do not confuse with +// GTEST_HAS_POSIX_RE (see above) which users can +// define themselves. // GTEST_USES_SIMPLE_RE - our own simple regex is used; // the above two are mutually exclusive. // GTEST_CAN_COMPARE_NULL - accepts untyped NULL in EXPECT_EQ(). @@ -130,14 +141,17 @@ // // Template meta programming: // is_pointer - as in TR1; needed on Symbian and IBM XL C/C++ only. +// IteratorTraits - partial implementation of std::iterator_traits, which +// is not available in libCstd when compiled with Sun C++. // // Smart pointers: // scoped_ptr - as in TR2. // // Regular expressions: // RE - a simple regular expression class using the POSIX -// Extended Regular Expression syntax. Not available on -// Windows. +// Extended Regular Expression syntax on UNIX-like +// platforms, or a reduced regular exception syntax on +// other platforms, including Windows. // // Logging: // GTEST_LOG_() - logs messages at the specified severity level. @@ -170,12 +184,14 @@ // Int32FromGTestEnv() - parses an Int32 environment variable. // StringFromGTestEnv() - parses a string environment variable. -#include <stddef.h> // For ptrdiff_t +#include <ctype.h> // for isspace, etc +#include <stddef.h> // for ptrdiff_t #include <stdlib.h> #include <stdio.h> #include <string.h> #ifndef _WIN32_WCE -#include <sys/stat.h> +# include <sys/types.h> +# include <sys/stat.h> #endif // !_WIN32_WCE #include <iostream> // NOLINT @@ -192,116 +208,140 @@ // Determines the version of gcc that is used to compile this. #ifdef __GNUC__ // 40302 means version 4.3.2. -#define GTEST_GCC_VER_ \ +# define GTEST_GCC_VER_ \ (__GNUC__*10000 + __GNUC_MINOR__*100 + __GNUC_PATCHLEVEL__) #endif // __GNUC__ // Determines the platform on which Google Test is compiled. #ifdef __CYGWIN__ -#define GTEST_OS_CYGWIN 1 +# define GTEST_OS_CYGWIN 1 #elif defined __SYMBIAN32__ -#define GTEST_OS_SYMBIAN 1 +# define GTEST_OS_SYMBIAN 1 #elif defined _WIN32 -#define GTEST_OS_WINDOWS 1 -#ifdef _WIN32_WCE -#define GTEST_OS_WINDOWS_MOBILE 1 -#elif defined(__MINGW__) || defined(__MINGW32__) -#define GTEST_OS_WINDOWS_MINGW 1 -#else -#define GTEST_OS_WINDOWS_DESKTOP 1 -#endif // _WIN32_WCE +# define GTEST_OS_WINDOWS 1 +# ifdef _WIN32_WCE +# define GTEST_OS_WINDOWS_MOBILE 1 +# elif defined(__MINGW__) || defined(__MINGW32__) +# define GTEST_OS_WINDOWS_MINGW 1 +# else +# define GTEST_OS_WINDOWS_DESKTOP 1 +# endif // _WIN32_WCE #elif defined __APPLE__ -#define GTEST_OS_MAC 1 +# define GTEST_OS_MAC 1 #elif defined __linux__ -#define GTEST_OS_LINUX 1 +# define GTEST_OS_LINUX 1 +# ifdef ANDROID +# define GTEST_OS_LINUX_ANDROID 1 +# endif // ANDROID #elif defined __MVS__ -#define GTEST_OS_ZOS 1 +# define GTEST_OS_ZOS 1 #elif defined(__sun) && defined(__SVR4) -#define GTEST_OS_SOLARIS 1 +# define GTEST_OS_SOLARIS 1 #elif defined(_AIX) -#define GTEST_OS_AIX 1 +# define GTEST_OS_AIX 1 +#elif defined(__hpux) +# define GTEST_OS_HPUX 1 +#elif defined __native_client__ +# define GTEST_OS_NACL 1 #elif defined(__HAIKU__) -#define GTEST_OS_HAIKU 1 +# define GTEST_OS_HAIKU 1 #endif // __CYGWIN__ -#if GTEST_OS_CYGWIN || GTEST_OS_HAIKU || GTEST_OS_LINUX || GTEST_OS_MAC || \ - GTEST_OS_SYMBIAN || GTEST_OS_SOLARIS || GTEST_OS_AIX +// Brings in definitions for functions used in the testing::internal::posix +// namespace (read, write, close, chdir, isatty, stat). We do not currently +// use them on Windows Mobile. +#if !GTEST_OS_WINDOWS +// This assumes that non-Windows OSes provide unistd.h. For OSes where this +// is not the case, we need to include headers that provide the functions +// mentioned above. +# include <unistd.h> +# if !GTEST_OS_NACL +// TODO(vladl@google.com): Remove this condition when Native Client SDK adds +// strings.h (tracked in +// http://code.google.com/p/nativeclient/issues/detail?id=1175). +# include <strings.h> // Native Client doesn't provide strings.h. +# endif +#elif !GTEST_OS_WINDOWS_MOBILE +# include <direct.h> +# include <io.h> +#endif + +// Defines this to true iff Google Test can use POSIX regular expressions. +#ifndef GTEST_HAS_POSIX_RE +# define GTEST_HAS_POSIX_RE (!GTEST_OS_WINDOWS) +#endif + +#if GTEST_HAS_POSIX_RE // On some platforms, <regex.h> needs someone to define size_t, and // won't compile otherwise. We can #include it here as we already // included <stdlib.h>, which is guaranteed to define size_t through // <stddef.h>. -#include <regex.h> // NOLINT -#include <strings.h> // NOLINT -#include <sys/types.h> // NOLINT -#include <time.h> // NOLINT -#include <unistd.h> // NOLINT +# include <regex.h> // NOLINT -#define GTEST_USES_POSIX_RE 1 +# define GTEST_USES_POSIX_RE 1 #elif GTEST_OS_WINDOWS -#if !GTEST_OS_WINDOWS_MOBILE -#include <direct.h> // NOLINT -#include <io.h> // NOLINT -#endif - // <regex.h> is not available on Windows. Use our own simple regex // implementation instead. -#define GTEST_USES_SIMPLE_RE 1 +# define GTEST_USES_SIMPLE_RE 1 #else // <regex.h> may not be available on this platform. Use our own // simple regex implementation instead. -#define GTEST_USES_SIMPLE_RE 1 +# define GTEST_USES_SIMPLE_RE 1 -#endif // GTEST_OS_CYGWIN || GTEST_OS_LINUX || GTEST_OS_MAC || - // GTEST_OS_SYMBIAN || GTEST_OS_SOLARIS || GTEST_OS_AIX +#endif // GTEST_HAS_POSIX_RE #ifndef GTEST_HAS_EXCEPTIONS // The user didn't tell us whether exceptions are enabled, so we need // to figure it out. -#if defined(_MSC_VER) || defined(__BORLANDC__) +# if defined(_MSC_VER) || defined(__BORLANDC__) // MSVC's and C++Builder's implementations of the STL use the _HAS_EXCEPTIONS // macro to enable exceptions, so we'll do the same. // Assumes that exceptions are enabled by default. -#ifndef _HAS_EXCEPTIONS -#define _HAS_EXCEPTIONS 1 -#endif // _HAS_EXCEPTIONS -#define GTEST_HAS_EXCEPTIONS _HAS_EXCEPTIONS -#elif defined(__GNUC__) && __EXCEPTIONS +# ifndef _HAS_EXCEPTIONS +# define _HAS_EXCEPTIONS 1 +# endif // _HAS_EXCEPTIONS +# define GTEST_HAS_EXCEPTIONS _HAS_EXCEPTIONS +# elif defined(__GNUC__) && __EXCEPTIONS // gcc defines __EXCEPTIONS to 1 iff exceptions are enabled. -#define GTEST_HAS_EXCEPTIONS 1 -#elif defined(__SUNPRO_CC) +# define GTEST_HAS_EXCEPTIONS 1 +# elif defined(__SUNPRO_CC) // Sun Pro CC supports exceptions. However, there is no compile-time way of // detecting whether they are enabled or not. Therefore, we assume that // they are enabled unless the user tells us otherwise. -#define GTEST_HAS_EXCEPTIONS 1 -#elif defined(__IBMCPP__) && __EXCEPTIONS +# define GTEST_HAS_EXCEPTIONS 1 +# elif defined(__IBMCPP__) && __EXCEPTIONS // xlC defines __EXCEPTIONS to 1 iff exceptions are enabled. -#define GTEST_HAS_EXCEPTIONS 1 -#else +# define GTEST_HAS_EXCEPTIONS 1 +# elif defined(__HP_aCC) +// Exception handling is in effect by default in HP aCC compiler. It has to +// be turned of by +noeh compiler option if desired. +# define GTEST_HAS_EXCEPTIONS 1 +# else // For other compilers, we assume exceptions are disabled to be // conservative. -#define GTEST_HAS_EXCEPTIONS 0 -#endif // defined(_MSC_VER) || defined(__BORLANDC__) +# define GTEST_HAS_EXCEPTIONS 0 +# endif // defined(_MSC_VER) || defined(__BORLANDC__) #endif // GTEST_HAS_EXCEPTIONS #if !defined(GTEST_HAS_STD_STRING) // Even though we don't use this macro any longer, we keep it in case // some clients still depend on it. -#define GTEST_HAS_STD_STRING 1 +# define GTEST_HAS_STD_STRING 1 #elif !GTEST_HAS_STD_STRING // The user told us that ::std::string isn't available. -#error "Google Test cannot be used where ::std::string isn't available." +# error "Google Test cannot be used where ::std::string isn't available." #endif // !defined(GTEST_HAS_STD_STRING) #ifndef GTEST_HAS_GLOBAL_STRING // The user didn't tell us whether ::string is available, so we need // to figure it out. -#define GTEST_HAS_GLOBAL_STRING 0 +# define GTEST_HAS_GLOBAL_STRING 0 #endif // GTEST_HAS_GLOBAL_STRING @@ -311,18 +351,19 @@ // TODO(wan@google.com): uses autoconf to detect whether ::std::wstring // is available. -// Cygwin 1.5 and below doesn't support ::std::wstring. -// Cygwin 1.7 might add wstring support; this should be updated when clear. -// Solaris' libc++ doesn't support it either. +// Cygwin 1.7 and below doesn't support ::std::wstring. +// Solaris' libc++ doesn't support it either. Android has +// no support for it at least as recent as Froyo (2.2). // Minix currently doesn't support it either. -#define GTEST_HAS_STD_WSTRING (!(GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || GTEST_OS_HAIKU || defined(_MINIX))) +# define GTEST_HAS_STD_WSTRING \ + (!(GTEST_OS_LINUX_ANDROID || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || GTEST_OS_HAIKU || defined(_MINIX))) #endif // GTEST_HAS_STD_WSTRING #ifndef GTEST_HAS_GLOBAL_WSTRING // The user didn't tell us whether ::wstring is available, so we need // to figure it out. -#define GTEST_HAS_GLOBAL_WSTRING \ +# define GTEST_HAS_GLOBAL_WSTRING \ (GTEST_HAS_STD_WSTRING && GTEST_HAS_GLOBAL_STRING) #endif // GTEST_HAS_GLOBAL_WSTRING @@ -331,46 +372,46 @@ // The user didn't tell us whether RTTI is enabled, so we need to // figure it out. -#ifdef _MSC_VER +# ifdef _MSC_VER -#ifdef _CPPRTTI // MSVC defines this macro iff RTTI is enabled. -#define GTEST_HAS_RTTI 1 -#else -#define GTEST_HAS_RTTI 0 -#endif +# ifdef _CPPRTTI // MSVC defines this macro iff RTTI is enabled. +# define GTEST_HAS_RTTI 1 +# else +# define GTEST_HAS_RTTI 0 +# endif // Starting with version 4.3.2, gcc defines __GXX_RTTI iff RTTI is enabled. -#elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40302) +# elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40302) -#ifdef __GXX_RTTI -#define GTEST_HAS_RTTI 1 -#else -#define GTEST_HAS_RTTI 0 -#endif // __GXX_RTTI +# ifdef __GXX_RTTI +# define GTEST_HAS_RTTI 1 +# else +# define GTEST_HAS_RTTI 0 +# endif // __GXX_RTTI // Starting with version 9.0 IBM Visual Age defines __RTTI_ALL__ to 1 if // both the typeid and dynamic_cast features are present. -#elif defined(__IBMCPP__) && (__IBMCPP__ >= 900) +# elif defined(__IBMCPP__) && (__IBMCPP__ >= 900) -#ifdef __RTTI_ALL__ -#define GTEST_HAS_RTTI 1 -#else -#define GTEST_HAS_RTTI 0 -#endif +# ifdef __RTTI_ALL__ +# define GTEST_HAS_RTTI 1 +# else +# define GTEST_HAS_RTTI 0 +# endif -#else +# else // For all other compilers, we assume RTTI is enabled. -#define GTEST_HAS_RTTI 1 +# define GTEST_HAS_RTTI 1 -#endif // _MSC_VER +# endif // _MSC_VER #endif // GTEST_HAS_RTTI // It's this header's responsibility to #include <typeinfo> when RTTI // is enabled. #if GTEST_HAS_RTTI -#include <typeinfo> +# include <typeinfo> #endif // Determines whether Google Test can use the pthreads library. @@ -380,15 +421,24 @@ // // To disable threading support in Google Test, add -DGTEST_HAS_PTHREAD=0 // to your compiler flags. -#define GTEST_HAS_PTHREAD (GTEST_OS_LINUX || GTEST_OS_MAC) +# define GTEST_HAS_PTHREAD (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_HPUX) #endif // GTEST_HAS_PTHREAD +#if GTEST_HAS_PTHREAD +// gtest-port.h guarantees to #include <pthread.h> when GTEST_HAS_PTHREAD is +// true. +# include <pthread.h> // NOLINT + +// For timespec and nanosleep, used below. +# include <time.h> // NOLINT +#endif + // Determines whether Google Test can use tr1/tuple. You can define // this macro to 0 to prevent Google Test from using tuple (any // feature depending on tuple with be disabled in this mode). #ifndef GTEST_HAS_TR1_TUPLE // The user didn't tell us not to do it, so we assume it's OK. -#define GTEST_HAS_TR1_TUPLE 1 +# define GTEST_HAS_TR1_TUPLE 1 #endif // GTEST_HAS_TR1_TUPLE // Determines whether Google Test's own tr1 tuple implementation @@ -403,13 +453,13 @@ // defining __GNUC__ and friends, but cannot compile GCC's tuple // implementation. MSVC 2008 (9.0) provides TR1 tuple in a 323 MB // Feature Pack download, which we cannot assume the user has. -#if (defined(__GNUC__) && !(defined(__CUDACC__) || defined(__clang__)) \ - && (GTEST_GCC_VER_ >= 40000)) \ +# if (defined(__GNUC__) && !(defined(__CUDACC__) || defined(__clang__)) \ + && (GTEST_GCC_VER_ >= 40000)) \ || _MSC_VER >= 1600 -#define GTEST_USE_OWN_TR1_TUPLE 0 -#else -#define GTEST_USE_OWN_TR1_TUPLE 1 -#endif +# define GTEST_USE_OWN_TR1_TUPLE 0 +# else +# define GTEST_USE_OWN_TR1_TUPLE 1 +# endif #endif // GTEST_USE_OWN_TR1_TUPLE @@ -418,47 +468,47 @@ // tr1/tuple. #if GTEST_HAS_TR1_TUPLE -#if GTEST_USE_OWN_TR1_TUPLE -#include <gtest/internal/gtest-tuple.h> -#elif GTEST_OS_SYMBIAN +# if GTEST_USE_OWN_TR1_TUPLE +# include "gtest/internal/gtest-tuple.h" +# elif GTEST_OS_SYMBIAN // On Symbian, BOOST_HAS_TR1_TUPLE causes Boost's TR1 tuple library to // use STLport's tuple implementation, which unfortunately doesn't // work as the copy of STLport distributed with Symbian is incomplete. // By making sure BOOST_HAS_TR1_TUPLE is undefined, we force Boost to // use its own tuple implementation. -#ifdef BOOST_HAS_TR1_TUPLE -#undef BOOST_HAS_TR1_TUPLE -#endif // BOOST_HAS_TR1_TUPLE +# ifdef BOOST_HAS_TR1_TUPLE +# undef BOOST_HAS_TR1_TUPLE +# endif // BOOST_HAS_TR1_TUPLE // This prevents <boost/tr1/detail/config.hpp>, which defines // BOOST_HAS_TR1_TUPLE, from being #included by Boost's <tuple>. -#define BOOST_TR1_DETAIL_CONFIG_HPP_INCLUDED -#include <tuple> +# define BOOST_TR1_DETAIL_CONFIG_HPP_INCLUDED +# include <tuple> -#elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40000) +# elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40000) // GCC 4.0+ implements tr1/tuple in the <tr1/tuple> header. This does // not conform to the TR1 spec, which requires the header to be <tuple>. -#if !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302 +# if !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302 // Until version 4.3.2, gcc has a bug that causes <tr1/functional>, // which is #included by <tr1/tuple>, to not compile when RTTI is // disabled. _TR1_FUNCTIONAL is the header guard for // <tr1/functional>. Hence the following #define is a hack to prevent // <tr1/functional> from being included. -#define _TR1_FUNCTIONAL 1 -#include <tr1/tuple> -#undef _TR1_FUNCTIONAL // Allows the user to #include +# define _TR1_FUNCTIONAL 1 +# include <tr1/tuple> +# undef _TR1_FUNCTIONAL // Allows the user to #include // <tr1/functional> if he chooses to. -#else -#include <tr1/tuple> // NOLINT -#endif // !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302 +# else +# include <tr1/tuple> // NOLINT +# endif // !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302 -#else +# else // If the compiler is not GCC 4.0+, we assume the user is using a // spec-conforming TR1 implementation. -#include <tuple> // NOLINT -#endif // GTEST_USE_OWN_TR1_TUPLE +# include <tuple> // NOLINT +# endif // GTEST_USE_OWN_TR1_TUPLE #endif // GTEST_HAS_TR1_TUPLE @@ -469,19 +519,25 @@ #ifndef GTEST_HAS_CLONE // The user didn't tell us, so we need to figure it out. -#if GTEST_OS_LINUX && !defined(__ia64__) -#define GTEST_HAS_CLONE 1 -#else -#define GTEST_HAS_CLONE 0 -#endif // GTEST_OS_LINUX && !defined(__ia64__) +# if GTEST_OS_LINUX && !defined(__ia64__) +# define GTEST_HAS_CLONE 1 +# else +# define GTEST_HAS_CLONE 0 +# endif // GTEST_OS_LINUX && !defined(__ia64__) #endif // GTEST_HAS_CLONE // Determines whether to support stream redirection. This is used to test // output correctness and to implement death tests. -#if !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_SYMBIAN -#define GTEST_HAS_STREAM_REDIRECTION_ 1 -#endif // !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_SYMBIAN +#ifndef GTEST_HAS_STREAM_REDIRECTION +// By default, we assume that stream redirection is supported on all +// platforms except known mobile ones. +# if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN +# define GTEST_HAS_STREAM_REDIRECTION 0 +# else +# define GTEST_HAS_STREAM_REDIRECTION 1 +# endif // !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_SYMBIAN +#endif // GTEST_HAS_STREAM_REDIRECTION // Determines whether to support death tests. // Google Test does not support death tests for VC 7.1 and earlier as @@ -489,9 +545,9 @@ // pops up a dialog window that cannot be suppressed programmatically. #if (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \ (GTEST_OS_WINDOWS_DESKTOP && _MSC_VER >= 1400) || \ - GTEST_OS_WINDOWS_MINGW || GTEST_OS_AIX) -#define GTEST_HAS_DEATH_TEST 1 -#include <vector> // NOLINT + GTEST_OS_WINDOWS_MINGW || GTEST_OS_AIX || GTEST_OS_HPUX) +# define GTEST_HAS_DEATH_TEST 1 +# include <vector> // NOLINT #endif // We don't support MSVC 7.1 with exceptions disabled now. Therefore @@ -502,11 +558,11 @@ // Determines whether to support type-driven tests. // Typed tests need <typeinfo> and variadic macros, which GCC, VC++ 8.0, -// Sun Pro CC, and IBM Visual Age support. +// Sun Pro CC, IBM Visual Age, and HP aCC support. #if defined(__GNUC__) || (_MSC_VER >= 1400) || defined(__SUNPRO_CC) || \ - defined(__IBMCPP__) -#define GTEST_HAS_TYPED_TEST 1 -#define GTEST_HAS_TYPED_TEST_P 1 + defined(__IBMCPP__) || defined(__HP_aCC) +# define GTEST_HAS_TYPED_TEST 1 +# define GTEST_HAS_TYPED_TEST_P 1 #endif // Determines whether to support Combine(). This only makes sense when @@ -514,13 +570,18 @@ // work on Sun Studio since it doesn't understand templated conversion // operators. #if GTEST_HAS_PARAM_TEST && GTEST_HAS_TR1_TUPLE && !defined(__SUNPRO_CC) -#define GTEST_HAS_COMBINE 1 +# define GTEST_HAS_COMBINE 1 #endif // Determines whether the system compiler uses UTF-16 for encoding wide strings. #define GTEST_WIDE_STRING_USES_UTF16_ \ (GTEST_OS_WINDOWS || GTEST_OS_CYGWIN || GTEST_OS_SYMBIAN || GTEST_OS_AIX) +// Determines whether test results can be streamed to a socket. +#if GTEST_OS_LINUX +# define GTEST_CAN_STREAM_RESULTS_ 1 +#endif + // Defines some utility macros. // The GNU compiler emits a warning if nested "if" statements are followed by @@ -532,9 +593,9 @@ // // The "switch (0) case 0:" idiom is used to suppress this. #ifdef __INTEL_COMPILER -#define GTEST_AMBIGUOUS_ELSE_BLOCKER_ +# define GTEST_AMBIGUOUS_ELSE_BLOCKER_ #else -#define GTEST_AMBIGUOUS_ELSE_BLOCKER_ switch (0) case 0: // NOLINT +# define GTEST_AMBIGUOUS_ELSE_BLOCKER_ switch (0) case 0: default: // NOLINT #endif // Use this annotation at the end of a struct/class definition to @@ -549,9 +610,9 @@ // Also use it after a variable or parameter declaration to tell the // compiler the variable/parameter does not have to be used. #if defined(__GNUC__) && !defined(COMPILER_ICC) -#define GTEST_ATTRIBUTE_UNUSED_ __attribute__ ((unused)) +# define GTEST_ATTRIBUTE_UNUSED_ __attribute__ ((unused)) #else -#define GTEST_ATTRIBUTE_UNUSED_ +# define GTEST_ATTRIBUTE_UNUSED_ #endif // A macro to disallow operator= @@ -571,9 +632,9 @@ // // Sprocket* AllocateSprocket() GTEST_MUST_USE_RESULT_; #if defined(__GNUC__) && (GTEST_GCC_VER_ >= 30400) && !defined(COMPILER_ICC) -#define GTEST_MUST_USE_RESULT_ __attribute__ ((warn_unused_result)) +# define GTEST_MUST_USE_RESULT_ __attribute__ ((warn_unused_result)) #else -#define GTEST_MUST_USE_RESULT_ +# define GTEST_MUST_USE_RESULT_ #endif // __GNUC__ && (GTEST_GCC_VER_ >= 30400) && !COMPILER_ICC // Determine whether the compiler supports Microsoft's Structured Exception @@ -582,28 +643,35 @@ #ifndef GTEST_HAS_SEH // The user didn't tell us, so we need to figure it out. -#if defined(_MSC_VER) || defined(__BORLANDC__) +# if defined(_MSC_VER) || defined(__BORLANDC__) // These two compilers are known to support SEH. -#define GTEST_HAS_SEH 1 -#else +# define GTEST_HAS_SEH 1 +# else // Assume no SEH. -#define GTEST_HAS_SEH 0 -#endif +# define GTEST_HAS_SEH 0 +# endif #endif // GTEST_HAS_SEH #ifdef _MSC_VER -#if GTEST_LINKED_AS_SHARED_LIBRARY -#define GTEST_API_ __declspec(dllimport) -#elif GTEST_CREATE_SHARED_LIBRARY -#define GTEST_API_ __declspec(dllexport) -#endif +# if GTEST_LINKED_AS_SHARED_LIBRARY +# define GTEST_API_ __declspec(dllimport) +# elif GTEST_CREATE_SHARED_LIBRARY +# define GTEST_API_ __declspec(dllexport) +# endif #endif // _MSC_VER #ifndef GTEST_API_ -#define GTEST_API_ +# define GTEST_API_ +#endif + +#ifdef __GNUC__ +// Ask the compiler to never inline a given function. +# define GTEST_NO_INLINE_ __attribute__((noinline)) +#else +# define GTEST_NO_INLINE_ #endif namespace testing { @@ -614,7 +682,90 @@ namespace internal { class String; -typedef ::std::stringstream StrStream; +// The GTEST_COMPILE_ASSERT_ macro can be used to verify that a compile time +// expression is true. For example, you could use it to verify the +// size of a static array: +// +// GTEST_COMPILE_ASSERT_(ARRAYSIZE(content_type_names) == CONTENT_NUM_TYPES, +// content_type_names_incorrect_size); +// +// or to make sure a struct is smaller than a certain size: +// +// GTEST_COMPILE_ASSERT_(sizeof(foo) < 128, foo_too_large); +// +// The second argument to the macro is the name of the variable. If +// the expression is false, most compilers will issue a warning/error +// containing the name of the variable. + +template <bool> +struct CompileAssert { +}; + +#define GTEST_COMPILE_ASSERT_(expr, msg) \ + typedef ::testing::internal::CompileAssert<(bool(expr))> \ + msg[bool(expr) ? 1 : -1] + +// Implementation details of GTEST_COMPILE_ASSERT_: +// +// - GTEST_COMPILE_ASSERT_ works by defining an array type that has -1 +// elements (and thus is invalid) when the expression is false. +// +// - The simpler definition +// +// #define GTEST_COMPILE_ASSERT_(expr, msg) typedef char msg[(expr) ? 1 : -1] +// +// does not work, as gcc supports variable-length arrays whose sizes +// are determined at run-time (this is gcc's extension and not part +// of the C++ standard). As a result, gcc fails to reject the +// following code with the simple definition: +// +// int foo; +// GTEST_COMPILE_ASSERT_(foo, msg); // not supposed to compile as foo is +// // not a compile-time constant. +// +// - By using the type CompileAssert<(bool(expr))>, we ensures that +// expr is a compile-time constant. (Template arguments must be +// determined at compile-time.) +// +// - The outter parentheses in CompileAssert<(bool(expr))> are necessary +// to work around a bug in gcc 3.4.4 and 4.0.1. If we had written +// +// CompileAssert<bool(expr)> +// +// instead, these compilers will refuse to compile +// +// GTEST_COMPILE_ASSERT_(5 > 0, some_message); +// +// (They seem to think the ">" in "5 > 0" marks the end of the +// template argument list.) +// +// - The array size is (bool(expr) ? 1 : -1), instead of simply +// +// ((expr) ? 1 : -1). +// +// This is to avoid running into a bug in MS VC 7.1, which +// causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1. + +// StaticAssertTypeEqHelper is used by StaticAssertTypeEq defined in gtest.h. +// +// This template is declared, but intentionally undefined. +template <typename T1, typename T2> +struct StaticAssertTypeEqHelper; + +template <typename T> +struct StaticAssertTypeEqHelper<T, T> {}; + +#if GTEST_HAS_GLOBAL_STRING +typedef ::string string; +#else +typedef ::std::string string; +#endif // GTEST_HAS_GLOBAL_STRING + +#if GTEST_HAS_GLOBAL_WSTRING +typedef ::wstring wstring; +#elif GTEST_HAS_STD_WSTRING +typedef ::std::wstring wstring; +#endif // GTEST_HAS_GLOBAL_WSTRING // A helper for suppressing warnings on constant condition. It just // returns 'condition'. @@ -670,7 +821,9 @@ class GTEST_API_ RE { RE(const ::std::string& regex) { Init(regex.c_str()); } // NOLINT #if GTEST_HAS_GLOBAL_STRING + RE(const ::string& regex) { Init(regex.c_str()); } // NOLINT + #endif // GTEST_HAS_GLOBAL_STRING RE(const char* regex) { Init(regex); } // NOLINT @@ -694,12 +847,14 @@ class GTEST_API_ RE { } #if GTEST_HAS_GLOBAL_STRING + static bool FullMatch(const ::string& str, const RE& re) { return FullMatch(str.c_str(), re); } static bool PartialMatch(const ::string& str, const RE& re) { return PartialMatch(str.c_str(), re); } + #endif // GTEST_HAS_GLOBAL_STRING static bool FullMatch(const char* str, const RE& re); @@ -714,16 +869,31 @@ class GTEST_API_ RE { // files. const char* pattern_; bool is_valid_; + #if GTEST_USES_POSIX_RE + regex_t full_regex_; // For FullMatch(). regex_t partial_regex_; // For PartialMatch(). + #else // GTEST_USES_SIMPLE_RE + const char* full_pattern_; // For FullMatch(); + #endif GTEST_DISALLOW_ASSIGN_(RE); }; +// Formats a source file path and a line number as they would appear +// in an error message from the compiler used to compile this code. +GTEST_API_ ::std::string FormatFileLocation(const char* file, int line); + +// Formats a file location for compiler-independent XML output. +// Although this function is not platform dependent, we put it next to +// FormatFileLocation in order to contrast the two functions. +GTEST_API_ ::std::string FormatCompilerIndependentFileLocation(const char* file, + int line); + // Defines logging utilities: // GTEST_LOG_(severity) - logs messages at the specified severity level. The // message itself is streamed into the macro. @@ -795,6 +965,66 @@ inline void FlushInfoLog() { fflush(NULL); } // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. // +// Use ImplicitCast_ as a safe version of static_cast for upcasting in +// the type hierarchy (e.g. casting a Foo* to a SuperclassOfFoo* or a +// const Foo*). When you use ImplicitCast_, the compiler checks that +// the cast is safe. Such explicit ImplicitCast_s are necessary in +// surprisingly many situations where C++ demands an exact type match +// instead of an argument type convertable to a target type. +// +// The syntax for using ImplicitCast_ is the same as for static_cast: +// +// ImplicitCast_<ToType>(expr) +// +// ImplicitCast_ would have been part of the C++ standard library, +// but the proposal was submitted too late. It will probably make +// its way into the language in the future. +// +// This relatively ugly name is intentional. It prevents clashes with +// similar functions users may have (e.g., implicit_cast). The internal +// namespace alone is not enough because the function can be found by ADL. +template<typename To> +inline To ImplicitCast_(To x) { return x; } + +// When you upcast (that is, cast a pointer from type Foo to type +// SuperclassOfFoo), it's fine to use ImplicitCast_<>, since upcasts +// always succeed. When you downcast (that is, cast a pointer from +// type Foo to type SubclassOfFoo), static_cast<> isn't safe, because +// how do you know the pointer is really of type SubclassOfFoo? It +// could be a bare Foo, or of type DifferentSubclassOfFoo. Thus, +// when you downcast, you should use this macro. In debug mode, we +// use dynamic_cast<> to double-check the downcast is legal (we die +// if it's not). In normal mode, we do the efficient static_cast<> +// instead. Thus, it's important to test in debug mode to make sure +// the cast is legal! +// This is the only place in the code we should use dynamic_cast<>. +// In particular, you SHOULDN'T be using dynamic_cast<> in order to +// do RTTI (eg code like this: +// if (dynamic_cast<Subclass1>(foo)) HandleASubclass1Object(foo); +// if (dynamic_cast<Subclass2>(foo)) HandleASubclass2Object(foo); +// You should design the code some other way not to need this. +// +// This relatively ugly name is intentional. It prevents clashes with +// similar functions users may have (e.g., down_cast). The internal +// namespace alone is not enough because the function can be found by ADL. +template<typename To, typename From> // use like this: DownCast_<T*>(foo); +inline To DownCast_(From* f) { // so we only accept pointers + // Ensures that To is a sub-type of From *. This test is here only + // for compile-time type checking, and has no overhead in an + // optimized build at run-time, as it will be optimized away + // completely. + if (false) { + const To to = NULL; + ::testing::internal::ImplicitCast_<From*>(to); + } + +#if GTEST_HAS_RTTI + // RTTI: debug mode only! + GTEST_CHECK_(f == NULL || dynamic_cast<To>(f) != NULL); +#endif + return static_cast<To>(f); +} + // Downcasts the pointer of type Base to Derived. // Derived must be a subclass of Base. The parameter MUST // point to a class of type Derived, not any subclass of it. @@ -810,7 +1040,7 @@ Derived* CheckedDowncastToActualType(Base* base) { #endif } -#if GTEST_HAS_STREAM_REDIRECTION_ +#if GTEST_HAS_STREAM_REDIRECTION // Defines the stderr capturer: // CaptureStdout - starts capturing stdout. @@ -823,7 +1053,7 @@ GTEST_API_ String GetCapturedStdout(); GTEST_API_ void CaptureStderr(); GTEST_API_ String GetCapturedStderr(); -#endif // GTEST_HAS_STREAM_REDIRECTION_ +#endif // GTEST_HAS_STREAM_REDIRECTION #if GTEST_HAS_DEATH_TEST @@ -957,10 +1187,6 @@ class ThreadWithParam : public ThreadWithParamBase { GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParam); }; -// gtest-port.h guarantees to #include <pthread.h> when GTEST_HAS_PTHREAD is -// true. -#include <pthread.h> - // MutexBase and Mutex implement mutex on pthreads-based platforms. They // are used in conjunction with class MutexLock: // @@ -1015,11 +1241,11 @@ class MutexBase { }; // Forward-declares a static mutex. -#define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ +# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ extern ::testing::internal::MutexBase mutex // Defines and statically (i.e. at link time) initializes a static mutex. -#define GTEST_DEFINE_STATIC_MUTEX_(mutex) \ +# define GTEST_DEFINE_STATIC_MUTEX_(mutex) \ ::testing::internal::MutexBase mutex = { PTHREAD_MUTEX_INITIALIZER, 0 } // The Mutex class can only be used for mutexes created at runtime. It @@ -1166,7 +1392,7 @@ class ThreadLocal { GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal); }; -#define GTEST_IS_THREADSAFE 1 +# define GTEST_IS_THREADSAFE 1 #else // GTEST_HAS_PTHREAD @@ -1181,10 +1407,10 @@ class Mutex { void AssertHeld() const {} }; -#define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ +# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ extern ::testing::internal::Mutex mutex -#define GTEST_DEFINE_STATIC_MUTEX_(mutex) ::testing::internal::Mutex mutex +# define GTEST_DEFINE_STATIC_MUTEX_(mutex) ::testing::internal::Mutex mutex class GTestMutexLock { public: @@ -1208,7 +1434,7 @@ class ThreadLocal { // The above synchronization primitives have dummy implementations. // Therefore Google Test is not thread-safe. -#define GTEST_IS_THREADSAFE 0 +# define GTEST_IS_THREADSAFE 0 #endif // GTEST_HAS_PTHREAD @@ -1225,9 +1451,9 @@ GTEST_API_ size_t GetThreadCount(); #if defined(__SYMBIAN32__) || defined(__IBMCPP__) || defined(__SUNPRO_CC) // We lose support for NULL detection where the compiler doesn't like // passing non-POD classes through ellipsis (...). -#define GTEST_ELLIPSIS_NEEDS_POD_ 1 +# define GTEST_ELLIPSIS_NEEDS_POD_ 1 #else -#define GTEST_CAN_COMPARE_NULL 1 +# define GTEST_CAN_COMPARE_NULL 1 #endif // The Nokia Symbian and IBM XL C/C++ compilers cannot decide between @@ -1235,7 +1461,7 @@ GTEST_API_ size_t GetThreadCount(); // _can_ decide between class template specializations for T and T*, // so a tr1::type_traits-like is_pointer works. #if defined(__SYMBIAN32__) || defined(__IBMCPP__) -#define GTEST_NEEDS_IS_POINTER_ 1 +# define GTEST_NEEDS_IS_POINTER_ 1 #endif template <bool bool_value> @@ -1254,17 +1480,68 @@ struct is_pointer : public false_type {}; template <typename T> struct is_pointer<T*> : public true_type {}; +template <typename Iterator> +struct IteratorTraits { + typedef typename Iterator::value_type value_type; +}; + +template <typename T> +struct IteratorTraits<T*> { + typedef T value_type; +}; + +template <typename T> +struct IteratorTraits<const T*> { + typedef T value_type; +}; + #if GTEST_OS_WINDOWS -#define GTEST_PATH_SEP_ "\\" -#define GTEST_HAS_ALT_PATH_SEP_ 1 +# define GTEST_PATH_SEP_ "\\" +# define GTEST_HAS_ALT_PATH_SEP_ 1 // The biggest signed integer type the compiler supports. typedef __int64 BiggestInt; #else -#define GTEST_PATH_SEP_ "/" -#define GTEST_HAS_ALT_PATH_SEP_ 0 +# define GTEST_PATH_SEP_ "/" +# define GTEST_HAS_ALT_PATH_SEP_ 0 typedef long long BiggestInt; // NOLINT #endif // GTEST_OS_WINDOWS +// Utilities for char. + +// isspace(int ch) and friends accept an unsigned char or EOF. char +// may be signed, depending on the compiler (or compiler flags). +// Therefore we need to cast a char to unsigned char before calling +// isspace(), etc. + +inline bool IsAlpha(char ch) { + return isalpha(static_cast<unsigned char>(ch)) != 0; +} +inline bool IsAlNum(char ch) { + return isalnum(static_cast<unsigned char>(ch)) != 0; +} +inline bool IsDigit(char ch) { + return isdigit(static_cast<unsigned char>(ch)) != 0; +} +inline bool IsLower(char ch) { + return islower(static_cast<unsigned char>(ch)) != 0; +} +inline bool IsSpace(char ch) { + return isspace(static_cast<unsigned char>(ch)) != 0; +} +inline bool IsUpper(char ch) { + return isupper(static_cast<unsigned char>(ch)) != 0; +} +inline bool IsXDigit(char ch) { + return isxdigit(static_cast<unsigned char>(ch)) != 0; +} + +inline char ToLower(char ch) { + return static_cast<char>(tolower(static_cast<unsigned char>(ch))); +} +inline char ToUpper(char ch) { + return static_cast<char>(toupper(static_cast<unsigned char>(ch))); +} + // The testing::internal::posix namespace holds wrappers for common // POSIX functions. These wrappers hide the differences between // Windows/MSVC and POSIX systems. Since some compilers define these @@ -1279,36 +1556,36 @@ namespace posix { typedef struct _stat StatStruct; -#ifdef __BORLANDC__ +# ifdef __BORLANDC__ inline int IsATTY(int fd) { return isatty(fd); } inline int StrCaseCmp(const char* s1, const char* s2) { return stricmp(s1, s2); } inline char* StrDup(const char* src) { return strdup(src); } -#else // !__BORLANDC__ -#if GTEST_OS_WINDOWS_MOBILE +# else // !__BORLANDC__ +# if GTEST_OS_WINDOWS_MOBILE inline int IsATTY(int /* fd */) { return 0; } -#else +# else inline int IsATTY(int fd) { return _isatty(fd); } -#endif // GTEST_OS_WINDOWS_MOBILE +# endif // GTEST_OS_WINDOWS_MOBILE inline int StrCaseCmp(const char* s1, const char* s2) { return _stricmp(s1, s2); } inline char* StrDup(const char* src) { return _strdup(src); } -#endif // __BORLANDC__ +# endif // __BORLANDC__ -#if GTEST_OS_WINDOWS_MOBILE +# if GTEST_OS_WINDOWS_MOBILE inline int FileNo(FILE* file) { return reinterpret_cast<int>(_fileno(file)); } // Stat(), RmDir(), and IsDir() are not needed on Windows CE at this // time and thus not defined there. -#else +# else inline int FileNo(FILE* file) { return _fileno(file); } inline int Stat(const char* path, StatStruct* buf) { return _stat(path, buf); } inline int RmDir(const char* dir) { return _rmdir(dir); } inline bool IsDir(const StatStruct& st) { return (_S_IFDIR & st.st_mode) != 0; } -#endif // GTEST_OS_WINDOWS_MOBILE +# endif // GTEST_OS_WINDOWS_MOBILE #else @@ -1330,8 +1607,8 @@ inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); } #ifdef _MSC_VER // Temporarily disable warning 4996 (deprecated function). -#pragma warning(push) -#pragma warning(disable:4996) +# pragma warning(push) +# pragma warning(disable:4996) #endif inline const char* StrNCpy(char* dest, const char* src, size_t n) { @@ -1380,7 +1657,7 @@ inline const char* GetEnv(const char* name) { } #ifdef _MSC_VER -#pragma warning(pop) // Restores the warning state. +# pragma warning(pop) // Restores the warning state. #endif #if GTEST_OS_WINDOWS_MOBILE @@ -1446,6 +1723,7 @@ class TypeWithSize<4> { template <> class TypeWithSize<8> { public: + #if GTEST_OS_WINDOWS typedef __int64 Int; typedef unsigned __int64 UInt; diff --git a/utils/unittest/googletest/include/gtest/internal/gtest-string.h b/utils/unittest/googletest/include/gtest/internal/gtest-string.h index aff093d..dc3a07b 100644 --- a/utils/unittest/googletest/include/gtest/internal/gtest-string.h +++ b/utils/unittest/googletest/include/gtest/internal/gtest-string.h @@ -43,11 +43,11 @@ #ifdef __BORLANDC__ // string.h is not guaranteed to provide strcpy on C++ Builder. -#include <mem.h> +# include <mem.h> #endif #include <string.h> -#include <gtest/internal/gtest-port.h> +#include "gtest/internal/gtest-port.h" #include <string> @@ -296,7 +296,7 @@ class GTEST_API_ String { private: // Constructs a non-NULL String from the given content. This - // function can only be called when data_ has not been allocated. + // function can only be called when c_str_ has not been allocated. // ConstructNonNull(NULL, 0) results in an empty string (""). // ConstructNonNull(NULL, non_zero) is undefined behavior. void ConstructNonNull(const char* buffer, size_t a_length) { @@ -329,9 +329,9 @@ inline ::std::ostream& operator<<(::std::ostream& os, const String& str) { return os; } -// Gets the content of the StrStream's buffer as a String. Each '\0' +// Gets the content of the stringstream's buffer as a String. Each '\0' // character in the buffer is replaced with "\\0". -GTEST_API_ String StrStreamToString(StrStream* stream); +GTEST_API_ String StringStreamToString(::std::stringstream* stream); // Converts a streamable value to a String. A NULL pointer is // converted to "(null)". When the input value is a ::string, diff --git a/utils/unittest/googletest/include/gtest/internal/gtest-tuple.h b/utils/unittest/googletest/include/gtest/internal/gtest-tuple.h index 16178fc..d1af50e 100644 --- a/utils/unittest/googletest/include/gtest/internal/gtest-tuple.h +++ b/utils/unittest/googletest/include/gtest/internal/gtest-tuple.h @@ -44,9 +44,9 @@ // private as public. // Sun Studio versions < 12 also have the above bug. #if defined(__SYMBIAN32__) || (defined(__SUNPRO_CC) && __SUNPRO_CC < 0x590) -#define GTEST_DECLARE_TUPLE_AS_FRIEND_ public: +# define GTEST_DECLARE_TUPLE_AS_FRIEND_ public: #else -#define GTEST_DECLARE_TUPLE_AS_FRIEND_ \ +# define GTEST_DECLARE_TUPLE_AS_FRIEND_ \ template <GTEST_10_TYPENAMES_(U)> friend class tuple; \ private: #endif diff --git a/utils/unittest/googletest/include/gtest/internal/gtest-type-util.h b/utils/unittest/googletest/include/gtest/internal/gtest-type-util.h index 093eee6..b7b01b0 100644 --- a/utils/unittest/googletest/include/gtest/internal/gtest-type-util.h +++ b/utils/unittest/googletest/include/gtest/internal/gtest-type-util.h @@ -44,55 +44,64 @@ #ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ #define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ -#include <gtest/internal/gtest-port.h> -#include <gtest/internal/gtest-string.h> - -#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P +#include "gtest/internal/gtest-port.h" +#include "gtest/internal/gtest-string.h" // #ifdef __GNUC__ is too general here. It is possible to use gcc without using // libstdc++ (which is where cxxabi.h comes from). -#ifdef __GLIBCXX__ -#include <cxxabi.h> -#endif // __GLIBCXX__ +# ifdef __GLIBCXX__ +# include <cxxabi.h> +# elif defined(__HP_aCC) +# include <acxx_demangle.h> +# endif // __GLIBCXX__ namespace testing { namespace internal { -// AssertyTypeEq<T1, T2>::type is defined iff T1 and T2 are the same -// type. This can be used as a compile-time assertion to ensure that -// two types are equal. - -template <typename T1, typename T2> -struct AssertTypeEq; - -template <typename T> -struct AssertTypeEq<T, T> { - typedef bool type; -}; - // GetTypeName<T>() returns a human-readable name of type T. +// NB: This function is also used in Google Mock, so don't move it inside of +// the typed-test-only section below. template <typename T> String GetTypeName() { -#if GTEST_HAS_RTTI +# if GTEST_HAS_RTTI const char* const name = typeid(T).name(); -#ifdef __GLIBCXX__ +# if defined(__GLIBCXX__) || defined(__HP_aCC) int status = 0; // gcc's implementation of typeid(T).name() mangles the type name, // so we have to demangle it. - char* const readable_name = abi::__cxa_demangle(name, 0, 0, &status); +# ifdef __GLIBCXX__ + using abi::__cxa_demangle; +# endif // __GLIBCXX__ + char* const readable_name = __cxa_demangle(name, 0, 0, &status); const String name_str(status == 0 ? readable_name : name); free(readable_name); return name_str; -#else +# else return name; -#endif // __GLIBCXX__ +# endif // __GLIBCXX__ || __HP_aCC + +# else -#else return "<type>"; -#endif // GTEST_HAS_RTTI + +# endif // GTEST_HAS_RTTI } +#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P + +// AssertyTypeEq<T1, T2>::type is defined iff T1 and T2 are the same +// type. This can be used as a compile-time assertion to ensure that +// two types are equal. + +template <typename T1, typename T2> +struct AssertTypeEq; + +template <typename T> +struct AssertTypeEq<T, T> { + typedef bool type; +}; + // A unique type used as the default value for the arguments of class // template Types. This allows us to simulate variadic templates // (e.g. Types<int>, Type<int, double>, and etc), which C++ doesn't @@ -1611,7 +1620,7 @@ struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, namespace internal { -#define GTEST_TEMPLATE_ template <typename T> class +# define GTEST_TEMPLATE_ template <typename T> class // The template "selector" struct TemplateSel<Tmpl> is used to // represent Tmpl, which must be a class template with one type @@ -1629,7 +1638,7 @@ struct TemplateSel { }; }; -#define GTEST_BIND_(TmplSel, T) \ +# define GTEST_BIND_(TmplSel, T) \ TmplSel::template Bind<T>::type // A unique struct template used as the default value for the @@ -3313,9 +3322,9 @@ struct TypeList<Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T41, T42, T43, T44, T45, T46, T47, T48, T49, T50>::type type; }; +#endif // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P + } // namespace internal } // namespace testing -#endif // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P - #endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ diff --git a/utils/valgrind/x86_64-pc-linux-gnu.supp b/utils/valgrind/x86_64-pc-linux-gnu.supp index 3d15d71..fc863b8 100644 --- a/utils/valgrind/x86_64-pc-linux-gnu.supp +++ b/utils/valgrind/x86_64-pc-linux-gnu.supp @@ -51,3 +51,14 @@ obj:/lib/ld*.so } +{ + suppress optimized strcasecmp, to be fixed in valgrind 3.6.1 + Memcheck:Value8 + fun:__GI___strcasecmp_l +} + +{ + suppress optimized strcasecmp, to be fixed in valgrind 3.6.1 + Memcheck:Addr8 + fun:__GI___strcasecmp_l +} diff --git a/utils/vim/llvm.vim b/utils/vim/llvm.vim index 83e4c23..f35f855 100644 --- a/utils/vim/llvm.vim +++ b/utils/vim/llvm.vim @@ -1,7 +1,7 @@ " Vim syntax file " Language: llvm " Maintainer: The LLVM team, http://llvm.org/ -" Version: $Revision: 114788 $ +" Version: $Revision: 137806 $ if version < 600 syntax clear @@ -51,7 +51,7 @@ syn keyword llvmKeyword volatile fastcc coldcc cc ccc syn keyword llvmKeyword x86_stdcallcc x86_fastcallcc syn keyword llvmKeyword ptx_kernel ptx_device syn keyword llvmKeyword signext zeroext inreg sret nounwind noreturn -syn keyword llvmKeyword nocapture byval nest readnone readonly noalias +syn keyword llvmKeyword nocapture byval nest readnone readonly noalias uwtable syn keyword llvmKeyword inlinehint noinline alwaysinline optsize ssp sspreq syn keyword llvmKeyword noredzone noimplicitfloat naked alignstack syn keyword llvmKeyword module asm align tail to diff --git a/utils/vim/tablegen.vim b/utils/vim/tablegen.vim index 11a4d80..fed619a 100644 --- a/utils/vim/tablegen.vim +++ b/utils/vim/tablegen.vim @@ -1,7 +1,7 @@ " Vim syntax file " Language: TableGen " Maintainer: The LLVM team, http://llvm.org/ -" Version: $Revision: 97271 $ +" Version: $Revision: 141378 $ if version < 600 syntax clear |