diff options
Diffstat (limited to 'utils')
48 files changed, 1479 insertions, 1833 deletions
diff --git a/utils/FileCheck/FileCheck.cpp b/utils/FileCheck/FileCheck.cpp index 563cab0d5d..cd7bb5a44f 100644 --- a/utils/FileCheck/FileCheck.cpp +++ b/utils/FileCheck/FileCheck.cpp @@ -85,6 +85,9 @@ public: Pattern(bool matchEOF = false) : MatchEOF(matchEOF) { } + /// getLoc - Return the location in source code. + SMLoc getLoc() const { return PatternLoc; } + /// ParsePattern - Parse the given string into the Pattern. SM provides the /// SourceMgr used for error reports, and LineNumber is the line number in /// the input file from which the pattern string was read. @@ -581,7 +584,7 @@ struct CheckString { /// NotStrings - These are all of the strings that are disallowed from /// occurring between this match string and the previous one (or start of /// file). - std::vector<std::pair<SMLoc, Pattern> > NotStrings; + std::vector<Pattern> NotStrings; CheckString(const Pattern &P, SMLoc L, bool isCheckNext) : Pat(P), Loc(L), IsCheckNext(isCheckNext) {} @@ -604,7 +607,7 @@ static MemoryBuffer *CanonicalizeInputFile(MemoryBuffer *MB, continue; } - // If current char is not a horizontal whitespace or if horizontal + // If current char is not a horizontal whitespace or if horizontal // whitespace canonicalization is disabled, dump it to output as is. if (PreserveHorizontal || (*Ptr != ' ' && *Ptr != '\t')) { NewFile.push_back(*Ptr); @@ -639,17 +642,17 @@ static bool ReadCheckFile(SourceMgr &SM, << ec.message() << '\n'; return true; } - MemoryBuffer *F = File.take(); // If we want to canonicalize whitespace, strip excess whitespace from the // buffer containing the CHECK lines. Remove DOS style line endings. - F = CanonicalizeInputFile(F, NoCanonicalizeWhiteSpace); + MemoryBuffer *F = + CanonicalizeInputFile(File.take(), NoCanonicalizeWhiteSpace); SM.AddNewSourceBuffer(F, SMLoc()); // Find all instances of CheckPrefix followed by : in the file. StringRef Buffer = F->getBuffer(); - std::vector<std::pair<SMLoc, Pattern> > NotMatches; + std::vector<Pattern> NotMatches; // LineNumber keeps track of the line on which CheckPrefix instances are // found. @@ -716,8 +719,7 @@ static bool ReadCheckFile(SourceMgr &SM, // Handle CHECK-NOT. if (IsCheckNot) { - NotMatches.push_back(std::make_pair(SMLoc::getFromPointer(Buffer.data()), - P)); + NotMatches.push_back(P); continue; } @@ -803,16 +805,16 @@ int main(int argc, char **argv) { << ec.message() << '\n'; return 2; } - MemoryBuffer *F = File.take(); - if (F->getBufferSize() == 0) { + if (File->getBufferSize() == 0) { errs() << "FileCheck error: '" << InputFilename << "' is empty.\n"; return 2; } - + // Remove duplicate spaces in the input file if requested. // Remove DOS style line endings. - F = CanonicalizeInputFile(F, NoCanonicalizeWhiteSpace); + MemoryBuffer *F = + CanonicalizeInputFile(File.take(), NoCanonicalizeWhiteSpace); SM.AddNewSourceBuffer(F, SMLoc()); @@ -877,14 +879,13 @@ int main(int argc, char **argv) { for (unsigned ChunkNo = 0, e = CheckStr.NotStrings.size(); ChunkNo != e; ++ChunkNo) { size_t MatchLen = 0; - size_t Pos = CheckStr.NotStrings[ChunkNo].second.Match(SkippedRegion, - MatchLen, - VariableTable); + size_t Pos = CheckStr.NotStrings[ChunkNo].Match(SkippedRegion, MatchLen, + VariableTable); if (Pos == StringRef::npos) continue; SM.PrintMessage(SMLoc::getFromPointer(LastMatch+Pos), SourceMgr::DK_Error, CheckPrefix+"-NOT: string occurred!"); - SM.PrintMessage(CheckStr.NotStrings[ChunkNo].first, SourceMgr::DK_Note, + SM.PrintMessage(CheckStr.NotStrings[ChunkNo].getLoc(), SourceMgr::DK_Note, CheckPrefix+"-NOT: pattern specified here"); return 1; } diff --git a/utils/Makefile b/utils/Makefile index f972b6596f..d117b5a87f 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -9,7 +9,7 @@ LEVEL = .. PARALLEL_DIRS := FileCheck FileUpdate TableGen PerfectShuffle \ - count fpcmp llvm-lit not unittest yaml2obj + count fpcmp llvm-lit not unittest ifeq ($(NACL_SANDBOX),1) # In sandboxed mode, just build the bare minimum diff --git a/utils/TableGen/AsmMatcherEmitter.cpp b/utils/TableGen/AsmMatcherEmitter.cpp index 6faf819529..218af219a8 100644 --- a/utils/TableGen/AsmMatcherEmitter.cpp +++ b/utils/TableGen/AsmMatcherEmitter.cpp @@ -1322,27 +1322,6 @@ void AsmMatcherInfo::buildInfo() { if (CGI.TheDef->getValueAsBit("isCodeGenOnly")) continue; - // Validate the operand list to ensure we can handle this instruction. - for (unsigned i = 0, e = CGI.Operands.size(); i != e; ++i) { - const CGIOperandList::OperandInfo &OI = CGI.Operands[i]; - - // Validate tied operands. - if (OI.getTiedRegister() != -1) { - // If we have a tied operand that consists of multiple MCOperands, - // reject it. We reject aliases and ignore instructions for now. - if (OI.MINumOperands != 1) { - // FIXME: Should reject these. The ARM backend hits this with $lane - // in a bunch of instructions. The right answer is unclear. - DEBUG({ - errs() << "warning: '" << CGI.TheDef->getName() << "': " - << "ignoring instruction with multi-operand tied operand '" - << OI.Name << "'\n"; - }); - continue; - } - } - } - OwningPtr<MatchableInfo> II(new MatchableInfo(CGI)); II->initialize(*this, SingletonRegisters, AsmVariantNo, RegisterPrefix); @@ -1529,7 +1508,9 @@ buildInstructionOperandReference(MatchableInfo *II, // we want to canonicalize to: // "inc $dst" // so that we know how to provide the $dst operand when filling in the result. - int OITied = Operands[Idx].getTiedRegister(); + int OITied = -1; + if (Operands[Idx].MINumOperands == 1) + OITied = Operands[Idx].getTiedRegister(); if (OITied != -1) { // The tied operand index is an MIOperand index, find the operand that // contains it. @@ -1578,7 +1559,9 @@ void MatchableInfo::buildInstructionResultOperands() { const CGIOperandList::OperandInfo &OpInfo = ResultInst->Operands[i]; // If this is a tied operand, just copy from the previously handled operand. - int TiedOp = OpInfo.getTiedRegister(); + int TiedOp = -1; + if (OpInfo.MINumOperands == 1) + TiedOp = OpInfo.getTiedRegister(); if (TiedOp != -1) { ResOperands.push_back(ResOperand::getTiedOp(TiedOp)); continue; @@ -1586,10 +1569,15 @@ void MatchableInfo::buildInstructionResultOperands() { // Find out what operand from the asmparser this MCInst operand comes from. int SrcOperand = findAsmOperandNamed(OpInfo.Name); - if (OpInfo.Name.empty() || SrcOperand == -1) - PrintFatalError(TheDef->getLoc(), "Instruction '" + - TheDef->getName() + "' has operand '" + OpInfo.Name + - "' that doesn't appear in asm string!"); + if (OpInfo.Name.empty() || SrcOperand == -1) { + // This may happen for operands that are tied to a suboperand of a + // complex operand. Simply use a dummy value here; nobody should + // use this operand slot. + // FIXME: The long term goal is for the MCOperand list to not contain + // tied operands at all. + ResOperands.push_back(ResOperand::getImmOp(0)); + continue; + } // Check if the one AsmOperand populates the entire operand. unsigned NumOperands = OpInfo.MINumOperands; @@ -1620,7 +1608,9 @@ void MatchableInfo::buildAliasResultOperands() { const CGIOperandList::OperandInfo *OpInfo = &ResultInst->Operands[i]; // If this is a tied operand, just copy from the previously handled operand. - int TiedOp = OpInfo->getTiedRegister(); + int TiedOp = -1; + if (OpInfo->MINumOperands == 1) + TiedOp = OpInfo->getTiedRegister(); if (TiedOp != -1) { ResOperands.push_back(ResOperand::getTiedOp(TiedOp)); continue; @@ -1843,13 +1833,12 @@ static void emitConvertFuncs(CodeGenTarget &Target, StringRef ClassName, case MatchableInfo::ResOperand::TiedOperand: { // If this operand is tied to a previous one, just copy the MCInst // operand from the earlier one.We can only tie single MCOperand values. - //assert(OpInfo.MINumOperands == 1 && "Not a singular MCOperand"); + assert(OpInfo.MINumOperands == 1 && "Not a singular MCOperand"); unsigned TiedOp = OpInfo.TiedOperandNum; assert(i > TiedOp && "Tied operand precedes its target!"); Signature += "__Tie" + utostr(TiedOp); ConversionRow.push_back(CVT_Tied); ConversionRow.push_back(TiedOp); - // FIXME: Handle the operand number lookup for tied operands. break; } case MatchableInfo::ResOperand::ImmOperand: { @@ -2296,29 +2285,25 @@ static std::string GetAliasRequiredFeatures(Record *R, return Result; } -/// emitMnemonicAliases - If the target has any MnemonicAlias<> definitions, -/// emit a function for them and return true, otherwise return false. -static bool emitMnemonicAliases(raw_ostream &OS, const AsmMatcherInfo &Info) { - // Ignore aliases when match-prefix is set. - if (!MatchPrefix.empty()) - return false; - - std::vector<Record*> Aliases = - Info.getRecords().getAllDerivedDefinitions("MnemonicAlias"); - if (Aliases.empty()) return false; - - OS << "static void applyMnemonicAliases(StringRef &Mnemonic, " - "unsigned Features) {\n"; - +static void emitMnemonicAliasVariant(raw_ostream &OS,const AsmMatcherInfo &Info, + std::vector<Record*> &Aliases, + unsigned Indent = 0, + StringRef AsmParserVariantName = StringRef()){ // Keep track of all the aliases from a mnemonic. Use an std::map so that the // iteration order of the map is stable. std::map<std::string, std::vector<Record*> > AliasesFromMnemonic; for (unsigned i = 0, e = Aliases.size(); i != e; ++i) { Record *R = Aliases[i]; + // FIXME: Allow AssemblerVariantName to be a comma separated list. + std::string AsmVariantName = R->getValueAsString("AsmVariantName"); + if (AsmVariantName != AsmParserVariantName) + continue; AliasesFromMnemonic[R->getValueAsString("FromMnemonic")].push_back(R); } - + if (AliasesFromMnemonic.empty()) + return; + // Process each alias a "from" mnemonic at a time, building the code executed // by the string remapper. std::vector<StringMatcher::StringPair> Cases; @@ -2370,8 +2355,39 @@ static bool emitMnemonicAliases(raw_ostream &OS, const AsmMatcherInfo &Info) { Cases.push_back(std::make_pair(I->first, MatchCode)); } + StringMatcher("Mnemonic", Cases, OS).Emit(Indent); +} + +/// emitMnemonicAliases - If the target has any MnemonicAlias<> definitions, +/// emit a function for them and return true, otherwise return false. +static bool emitMnemonicAliases(raw_ostream &OS, const AsmMatcherInfo &Info, + CodeGenTarget &Target) { + // Ignore aliases when match-prefix is set. + if (!MatchPrefix.empty()) + return false; + + std::vector<Record*> Aliases = + Info.getRecords().getAllDerivedDefinitions("MnemonicAlias"); + if (Aliases.empty()) return false; + + OS << "static void applyMnemonicAliases(StringRef &Mnemonic, " + "unsigned Features, unsigned VariantID) {\n"; + OS << " switch (VariantID) {\n"; + unsigned VariantCount = Target.getAsmParserVariantCount(); + for (unsigned VC = 0; VC != VariantCount; ++VC) { + Record *AsmVariant = Target.getAsmParserVariant(VC); + int AsmParserVariantNo = AsmVariant->getValueAsInt("Variant"); + std::string AsmParserVariantName = AsmVariant->getValueAsString("Name"); + OS << " case " << AsmParserVariantNo << ":\n"; + emitMnemonicAliasVariant(OS, Info, Aliases, /*Indent=*/2, + AsmParserVariantName); + OS << " break;\n"; + } + OS << " }\n"; + + // Emit aliases that apply to all variants. + emitMnemonicAliasVariant(OS, Info, Aliases); - StringMatcher("Mnemonic", Cases, OS).Emit(); OS << "}\n\n"; return true; @@ -2674,7 +2690,7 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { OS << "#undef GET_MATCHER_IMPLEMENTATION\n\n"; // Generate the function that remaps for mnemonic aliases. - bool HasMnemonicAliases = emitMnemonicAliases(OS, Info); + bool HasMnemonicAliases = emitMnemonicAliases(OS, Info, Target); // Generate the convertToMCInst function to convert operands into an MCInst. // Also, generate the convertToMapAndConstraints function for MS-style inline @@ -2832,9 +2848,7 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { if (HasMnemonicAliases) { OS << " // Process all MnemonicAliases to remap the mnemonic.\n"; - OS << " // FIXME : Add an entry in AsmParserVariant to check this.\n"; - OS << " if (!VariantID)\n"; - OS << " applyMnemonicAliases(Mnemonic, AvailableFeatures);\n\n"; + OS << " applyMnemonicAliases(Mnemonic, AvailableFeatures, VariantID);\n\n"; } // Emit code to compute the class list for this operand vector. diff --git a/utils/TableGen/CMakeLists.txt b/utils/TableGen/CMakeLists.txt index d0f44be69a..3ee1974431 100644 --- a/utils/TableGen/CMakeLists.txt +++ b/utils/TableGen/CMakeLists.txt @@ -33,4 +33,5 @@ add_tablegen(llvm-tblgen LLVM X86DisassemblerTables.cpp X86ModRMFilters.cpp X86RecognizableInstr.cpp + CTagsEmitter.cpp ) diff --git a/utils/TableGen/CTagsEmitter.cpp b/utils/TableGen/CTagsEmitter.cpp new file mode 100644 index 0000000000..8bf777839b --- /dev/null +++ b/utils/TableGen/CTagsEmitter.cpp @@ -0,0 +1,99 @@ +//===- CTagsEmitter.cpp - Generate ctags-compatible index ------------------===// +// +// 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 an index of definitions in ctags(1) format. +// A helper script, utils/TableGen/tdtags, provides an easier-to-use +// interface; run 'tdtags -H' for documentation. +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "ctags-emitter" + +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/TableGenBackend.h" +#include <algorithm> +#include <string> +#include <vector> +using namespace llvm; + +namespace llvm { extern SourceMgr SrcMgr; } + +namespace { + +class Tag { +private: + const std::string *Id; + SMLoc Loc; +public: + Tag(const std::string &Name, const SMLoc Location) + : Id(&Name), Loc(Location) {} + int operator<(const Tag &B) const { return *Id < *B.Id; } + void emit(raw_ostream &OS) const { + int BufferID = SrcMgr.FindBufferContainingLoc(Loc); + MemoryBuffer *CurMB = SrcMgr.getBufferInfo(BufferID).Buffer; + const char *BufferName = CurMB->getBufferIdentifier(); + std::pair<unsigned, unsigned> LineAndColumn = SrcMgr.getLineAndColumn(Loc); + OS << *Id << "\t" << BufferName << "\t" << LineAndColumn.first << "\n"; + } +}; + +class CTagsEmitter { +private: + RecordKeeper &Records; +public: + CTagsEmitter(RecordKeeper &R) : Records(R) {} + + void run(raw_ostream &OS); + +private: + static SMLoc locate(const Record *R); +}; + +} // End anonymous namespace. + +SMLoc CTagsEmitter::locate(const Record *R) { + ArrayRef<SMLoc> Locs = R->getLoc(); + if (Locs.empty()) { + SMLoc NullLoc; + return NullLoc; + } + return Locs.front(); +} + +void CTagsEmitter::run(raw_ostream &OS) { + const std::map<std::string, Record *> &Classes = Records.getClasses(); + const std::map<std::string, Record *> &Defs = Records.getDefs(); + std::vector<Tag> Tags; + // Collect tags. + Tags.reserve(Classes.size() + Defs.size()); + for (std::map<std::string, Record *>::const_iterator I = Classes.begin(), + E = Classes.end(); + I != E; ++I) + Tags.push_back(Tag(I->first, locate(I->second))); + for (std::map<std::string, Record *>::const_iterator I = Defs.begin(), + E = Defs.end(); + I != E; ++I) + Tags.push_back(Tag(I->first, locate(I->second))); + // Emit tags. + std::sort(Tags.begin(), Tags.end()); + OS << "!_TAG_FILE_FORMAT\t1\t/original ctags format/\n"; + OS << "!_TAG_FILE_SORTED\t1\t/0=unsorted, 1=sorted, 2=foldcase/\n"; + for (std::vector<Tag>::const_iterator I = Tags.begin(), E = Tags.end(); + I != E; ++I) + I->emit(OS); +} + +namespace llvm { + +void EmitCTags(RecordKeeper &RK, raw_ostream &OS) { CTagsEmitter(RK).run(OS); } + +} // End llvm namespace. diff --git a/utils/TableGen/CodeGenDAGPatterns.cpp b/utils/TableGen/CodeGenDAGPatterns.cpp index 72fb77150c..8e5bb7760f 100644 --- a/utils/TableGen/CodeGenDAGPatterns.cpp +++ b/utils/TableGen/CodeGenDAGPatterns.cpp @@ -57,7 +57,7 @@ EEVT::TypeSet::TypeSet(MVT::SimpleValueType VT, TreePattern &TP) { } -EEVT::TypeSet::TypeSet(const std::vector<MVT::SimpleValueType> &VTList) { +EEVT::TypeSet::TypeSet(ArrayRef<MVT::SimpleValueType> VTList) { assert(!VTList.empty() && "empty list?"); TypeVec.append(VTList.begin(), VTList.end()); @@ -76,7 +76,7 @@ bool EEVT::TypeSet::FillWithPossibleTypes(TreePattern &TP, bool (*Pred)(MVT::SimpleValueType), const char *PredicateName) { assert(isCompletelyUnknown()); - const std::vector<MVT::SimpleValueType> &LegalTypes = + ArrayRef<MVT::SimpleValueType> LegalTypes = TP.getDAGPatterns().getTargetInfo().getLegalValueTypes(); if (TP.hasError()) @@ -956,6 +956,40 @@ bool SDTypeConstraint::ApplyTypeConstraint(TreePatternNode *N, llvm_unreachable("Invalid ConstraintType!"); } +// Update the node type to match an instruction operand or result as specified +// in the ins or outs lists on the instruction definition. Return true if the +// type was actually changed. +bool TreePatternNode::UpdateNodeTypeFromInst(unsigned ResNo, + Record *Operand, + TreePattern &TP) { + // The 'unknown' operand indicates that types should be inferred from the + // context. + if (Operand->isSubClassOf("unknown_class")) + return false; + + // The Operand class specifies a type directly. + if (Operand->isSubClassOf("Operand")) + return UpdateNodeType(ResNo, getValueType(Operand->getValueAsDef("Type")), + TP); + + // PointerLikeRegClass has a type that is determined at runtime. + if (Operand->isSubClassOf("PointerLikeRegClass")) + return UpdateNodeType(ResNo, MVT::iPTR, TP); + + // Both RegisterClass and RegisterOperand operands derive their types from a + // register class def. + Record *RC = 0; + if (Operand->isSubClassOf("RegisterClass")) + RC = Operand; + else if (Operand->isSubClassOf("RegisterOperand")) + RC = Operand->getValueAsDef("RegClass"); + + assert(RC && "Unknown operand type"); + CodeGenTarget &Tgt = TP.getDAGPatterns().getTargetInfo(); + return UpdateNodeType(ResNo, Tgt.getRegisterClass(RC).getValueTypes(), TP); +} + + //===----------------------------------------------------------------------===// // SDNodeInfo implementation // @@ -1287,8 +1321,18 @@ TreePatternNode *TreePatternNode::InlinePatternFragments(TreePattern &TP) { /// type which should be applied to it. This will infer the type of register /// references from the register file information, for example. /// +/// When Unnamed is set, return the type of a DAG operand with no name, such as +/// the F8RC register class argument in: +/// +/// (COPY_TO_REGCLASS GPR:$src, F8RC) +/// +/// When Unnamed is false, return the type of a named DAG operand such as the +/// GPR:$src operand above. +/// static EEVT::TypeSet getImplicitType(Record *R, unsigned ResNo, - bool NotRegisters, TreePattern &TP) { + bool NotRegisters, + bool Unnamed, + TreePattern &TP) { // Check to see if this is a register operand. if (R->isSubClassOf("RegisterOperand")) { assert(ResNo == 0 && "Regoperand ref only has one result!"); @@ -1302,6 +1346,13 @@ static EEVT::TypeSet getImplicitType(Record *R, unsigned ResNo, // Check to see if this is a register or a register class. if (R->isSubClassOf("RegisterClass")) { assert(ResNo == 0 && "Regclass ref only has one result!"); + // An unnamed register class represents itself as an i32 immediate, for + // example on a COPY_TO_REGCLASS instruction. + if (Unnamed) + return EEVT::TypeSet(MVT::i32, TP); + + // In a named operand, the register class provides the possible set of + // types. if (NotRegisters) return EEVT::TypeSet(); // Unknown. const CodeGenTarget &T = TP.getDAGPatterns().getTargetInfo(); @@ -1327,9 +1378,27 @@ static EEVT::TypeSet getImplicitType(Record *R, unsigned ResNo, return EEVT::TypeSet(); } - if (R->isSubClassOf("ValueType") || R->isSubClassOf("CondCode")) { + if (R->isSubClassOf("ValueType")) { assert(ResNo == 0 && "This node only has one result!"); - // Using a VTSDNode or CondCodeSDNode. + // An unnamed VTSDNode represents itself as an MVT::Other immediate. + // + // (sext_inreg GPR:$src, i16) + // ~~~ + if (Unnamed) + return EEVT::TypeSet(MVT::Other, TP); + // With a name, the ValueType simply provides the type of the named + // variable. + // + // (sext_inreg i32:$src, i16) + // ~~~~~~~~ + if (NotRegisters) + return EEVT::TypeSet(); // Unknown. + return EEVT::TypeSet(getValueType(R), TP); + } + + if (R->isSubClassOf("CondCode")) { + assert(ResNo == 0 && "This node only has one result!"); + // Using a CondCodeSDNode. return EEVT::TypeSet(MVT::Other, TP); } @@ -1435,7 +1504,8 @@ bool TreePatternNode::ApplyTypeConstraints(TreePattern &TP, bool NotRegisters) { bool MadeChange = false; for (unsigned i = 0, e = Types.size(); i != e; ++i) MadeChange |= UpdateNodeType(i, getImplicitType(DI->getDef(), i, - NotRegisters, TP), TP); + NotRegisters, + !hasName(), TP), TP); return MadeChange; } @@ -1498,25 +1568,6 @@ bool TreePatternNode::ApplyTypeConstraints(TreePattern &TP, bool NotRegisters) { return MadeChange; } - if (getOperator()->getName() == "COPY_TO_REGCLASS") { - bool MadeChange = false; - MadeChange |= getChild(0)->ApplyTypeConstraints(TP, NotRegisters); - MadeChange |= getChild(1)->ApplyTypeConstraints(TP, NotRegisters); - - assert(getChild(0)->getNumTypes() == 1 && - getChild(1)->getNumTypes() == 1 && "Unhandled case"); - - // child #1 of COPY_TO_REGCLASS should be a register class. We don't care - // what type it gets, so if it didn't get a concrete type just give it the - // first viable type from the reg class. - if (!getChild(1)->hasTypeSet(0) && - !getChild(1)->getExtType(0).isCompletelyUnknown()) { - MVT::SimpleValueType RCVT = getChild(1)->getExtType(0).getTypeList()[0]; - MadeChange |= getChild(1)->UpdateNodeType(0, RCVT, TP); - } - return MadeChange; - } - if (const CodeGenIntrinsic *Int = getIntrinsicInfo(CDP)) { bool MadeChange = false; @@ -1575,26 +1626,8 @@ bool TreePatternNode::ApplyTypeConstraints(TreePattern &TP, bool NotRegisters) { // (outs) list of the instruction. // FIXME: Cap at one result so far. unsigned NumResultsToAdd = InstInfo.Operands.NumDefs ? 1 : 0; - for (unsigned ResNo = 0; ResNo != NumResultsToAdd; ++ResNo) { - Record *ResultNode = Inst.getResult(ResNo); - - if (ResultNode->isSubClassOf("PointerLikeRegClass")) { - MadeChange |= UpdateNodeType(ResNo, MVT::iPTR, TP); - } else if (ResultNode->isSubClassOf("RegisterOperand")) { - Record *RegClass = ResultNode->getValueAsDef("RegClass"); - const CodeGenRegisterClass &RC = - CDP.getTargetInfo().getRegisterClass(RegClass); - MadeChange |= UpdateNodeType(ResNo, RC.getValueTypes(), TP); - } else if (ResultNode->isSubClassOf("unknown_class")) { - // Nothing to do. - } else { - assert(ResultNode->isSubClassOf("RegisterClass") && - "Operands should be register classes!"); - const CodeGenRegisterClass &RC = - CDP.getTargetInfo().getRegisterClass(ResultNode); - MadeChange |= UpdateNodeType(ResNo, RC.getValueTypes(), TP); - } - } + for (unsigned ResNo = 0; ResNo != NumResultsToAdd; ++ResNo) + MadeChange |= UpdateNodeTypeFromInst(ResNo, Inst.getResult(ResNo), TP); // If the instruction has implicit defs, we apply the first one as a result. // FIXME: This sucks, it should apply all implicit defs. @@ -1636,30 +1669,44 @@ bool TreePatternNode::ApplyTypeConstraints(TreePattern &TP, bool NotRegisters) { return false; } - MVT::SimpleValueType VT; TreePatternNode *Child = getChild(ChildNo++); unsigned ChildResNo = 0; // Instructions always use res #0 of their op. - if (OperandNode->isSubClassOf("RegisterClass")) { - const CodeGenRegisterClass &RC = - CDP.getTargetInfo().getRegisterClass(OperandNode); - MadeChange |= Child->UpdateNodeType(ChildResNo, RC.getValueTypes(), TP); - } else if (OperandNode->isSubClassOf("RegisterOperand")) { - Record *RegClass = OperandNode->getValueAsDef("RegClass"); - const CodeGenRegisterClass &RC = - CDP.getTargetInfo().getRegisterClass(RegClass); - MadeChange |= Child->UpdateNodeType(ChildResNo, RC.getValueTypes(), TP); - } else if (OperandNode->isSubClassOf("Operand")) { - VT = getValueType(OperandNode->getValueAsDef("Type")); - MadeChange |= Child->UpdateNodeType(ChildResNo, VT, TP); - } else if (OperandNode->isSubClassOf("PointerLikeRegClass")) { - MadeChange |= Child->UpdateNodeType(ChildResNo, MVT::iPTR, TP); - } else if (OperandNode->isSubClassOf("unknown_class")) { - // Nothing to do. - } else - llvm_unreachable("Unknown operand type!"); + // If the operand has sub-operands, they may be provided by distinct + // child patterns, so attempt to match each sub-operand separately. + if (OperandNode->isSubClassOf("Operand")) { + DagInit *MIOpInfo = OperandNode->getValueAsDag("MIOperandInfo"); + if (unsigned NumArgs = MIOpInfo->getNumArgs()) { + // But don't do that if the whole operand is being provided by + // a single ComplexPattern. + const ComplexPattern *AM = Child->getComplexPatternInfo(CDP); + if (!AM || AM->getNumOperands() < NumArgs) { + // Match first sub-operand against the child we already have. + Record *SubRec = cast<DefInit>(MIOpInfo->getArg(0))->getDef(); + MadeChange |= + Child->UpdateNodeTypeFromInst(ChildResNo, SubRec, TP); + + // And the remaining sub-operands against subsequent children. + for (unsigned Arg = 1; Arg < NumArgs; ++Arg) { + if (ChildNo >= getNumChildren()) { + TP.error("Instruction '" + getOperator()->getName() + + "' expects more operands than were provided."); + return false; + } + Child = getChild(ChildNo++); + + SubRec = cast<DefInit>(MIOpInfo->getArg(Arg))->getDef(); + MadeChange |= + Child->UpdateNodeTypeFromInst(ChildResNo, SubRec, TP); + } + continue; + } + } + } - MadeChange |= Child->ApplyTypeConstraints(TP, NotRegisters); + // If we didn't match by pieces above, attempt to match the whole + // operand now. + MadeChange |= Child->UpdateNodeTypeFromInst(ChildResNo, OperandNode, TP); } if (ChildNo != getNumChildren()) { @@ -1668,6 +1715,8 @@ bool TreePatternNode::ApplyTypeConstraints(TreePattern &TP, bool NotRegisters) { return false; } + for (unsigned i = 0, e = getNumChildren(); i != e; ++i) + MadeChange |= getChild(i)->ApplyTypeConstraints(TP, NotRegisters); return MadeChange; } @@ -1817,6 +1866,16 @@ TreePatternNode *TreePattern::ParseTreePattern(Init *TheInit, StringRef OpName){ return Res; } + // ?:$name or just $name. + if (TheInit == UnsetInit::get()) { + if (OpName.empty()) + error("'?' argument requires a name to match with operand list"); + TreePatternNode *Res = new TreePatternNode(TheInit, 1); + Args.push_back(OpName); + Res->setName(OpName); + return Res; + } + if (IntInit *II = dyn_cast<IntInit>(TheInit)) { if (!OpName.empty()) error("Constant int argument should not have a name!"); @@ -2383,6 +2442,7 @@ FindPatternInputsAndOutputs(TreePattern *I, TreePatternNode *Pat, I->error("set destination should be a register!"); if (Val->getDef()->isSubClassOf("RegisterClass") || + Val->getDef()->isSubClassOf("ValueType") || Val->getDef()->isSubClassOf("RegisterOperand") || Val->getDef()->isSubClassOf("PointerLikeRegClass")) { if (Dest->getName().empty()) @@ -2599,6 +2659,25 @@ getInstructionsInTree(TreePatternNode *Tree, SmallVectorImpl<Record*> &Instrs) { getInstructionsInTree(Tree->getChild(i), Instrs); } +/// Check the class of a pattern leaf node against the instruction operand it +/// represents. +static bool checkOperandClass(CGIOperandList::OperandInfo &OI, + Record *Leaf) { + if (OI.Rec == Leaf) + return true; + + // Allow direct value types to be used in instruction set patterns. + // The type will be checked later. + if (Leaf->isSubClassOf("ValueType")) + return true; + + // Patterns can also be ComplexPattern instances. + if (Leaf->isSubClassOf("ComplexPattern")) + return true; + + return false; +} + /// ParseInstructions - Parse all of the instructions, inlining and resolving /// any fragments involved. This populates the Instructions list with fully /// resolved instructions. @@ -2708,7 +2787,7 @@ void CodeGenDAGPatterns::ParseInstructions() { I->error("Operand $" + OpName + " should be a set destination: all " "outputs must occur before inputs in operand list!"); - if (CGI.Operands[i].Rec != R) + if (!checkOperandClass(CGI.Operands[i], R)) I->error("Operand $" + OpName + " class mismatch!"); // Remember the return type. @@ -2747,7 +2826,7 @@ void CodeGenDAGPatterns::ParseInstructions() { if (InVal->isLeaf() && isa<DefInit>(InVal->getLeafValue())) { Record *InRec = static_cast<DefInit*>(InVal->getLeafValue())->getDef(); - if (Op.Rec != InRec && !InRec->isSubClassOf("ComplexPattern")) + if (!checkOperandClass(Op, InRec)) I->error("Operand $" + OpName + "'s register class disagrees" " between the operand and pattern"); } diff --git a/utils/TableGen/CodeGenDAGPatterns.h b/utils/TableGen/CodeGenDAGPatterns.h index 424f02f7ab..7c2fa36741 100644 --- a/utils/TableGen/CodeGenDAGPatterns.h +++ b/utils/TableGen/CodeGenDAGPatterns.h @@ -59,7 +59,7 @@ namespace EEVT { public: TypeSet() {} TypeSet(MVT::SimpleValueType VT, TreePattern &TP); - TypeSet(const std::vector<MVT::SimpleValueType> &VTList); + TypeSet(ArrayRef<MVT::SimpleValueType> VTList); bool isCompletelyUnknown() const { return TypeVec.empty(); } @@ -334,6 +334,7 @@ public: } ~TreePatternNode(); + bool hasName() const { return !Name.empty(); } const std::string &getName() const { return Name; } void setName(StringRef N) { Name.assign(N.begin(), N.end()); } @@ -463,6 +464,11 @@ public: // Higher level manipulation routines. return Types[ResNo].MergeInTypeInfo(EEVT::TypeSet(InTy, TP), TP); } + // Update node type with types inferred from an instruction operand or result + // def from the ins/outs lists. + // Return true if the type changed. + bool UpdateNodeTypeFromInst(unsigned ResNo, Record *Operand, TreePattern &TP); + /// ContainsUnresolvedType - Return true if this tree contains any /// unresolved types. bool ContainsUnresolvedType() const { diff --git a/utils/TableGen/CodeGenRegisters.h b/utils/TableGen/CodeGenRegisters.h index a1921a412a..4f2cc28d49 100644 --- a/utils/TableGen/CodeGenRegisters.h +++ b/utils/TableGen/CodeGenRegisters.h @@ -261,7 +261,7 @@ namespace llvm { public: unsigned EnumValue; std::string Namespace; - std::vector<MVT::SimpleValueType> VTs; + SmallVector<MVT::SimpleValueType, 4> VTs; unsigned SpillSize; unsigned SpillAlignment; int CopyCost; @@ -274,7 +274,7 @@ namespace llvm { const std::string &getName() const { return Name; } std::string getQualifiedName() const; - const std::vector<MVT::SimpleValueType> &getValueTypes() const {return VTs;} + ArrayRef<MVT::SimpleValueType> getValueTypes() const {return VTs;} unsigned getNumValueTypes() const { return VTs.size(); } MVT::SimpleValueType getValueTypeNum(unsigned VTNum) const { diff --git a/utils/TableGen/CodeGenSchedule.cpp b/utils/TableGen/CodeGenSchedule.cpp index 23b79fcddf..112ff65d15 100644 --- a/utils/TableGen/CodeGenSchedule.cpp +++ b/utils/TableGen/CodeGenSchedule.cpp @@ -88,7 +88,7 @@ struct InstRegexOp : public SetTheory::Operator { /// CodeGenModels ctor interprets machine model records and populates maps. CodeGenSchedModels::CodeGenSchedModels(RecordKeeper &RK, const CodeGenTarget &TGT): - Records(RK), Target(TGT), NumItineraryClasses(0) { + Records(RK), Target(TGT) { Sets.addFieldExpander("InstRW", "Instrs"); @@ -217,7 +217,7 @@ void CodeGenSchedModels::collectSchedRW() { for (CodeGenTarget::inst_iterator I = Target.inst_begin(), E = Target.inst_end(); I != E; ++I) { Record *SchedDef = (*I)->TheDef; - if (!SchedDef->isSubClassOf("Sched")) + if (SchedDef->isValueUnset("SchedRW")) continue; RecVec RWs = SchedDef->getValueAsListOfDefs("SchedRW"); for (RecIter RWI = RWs.begin(), RWE = RWs.end(); RWI != RWE; ++RWI) { @@ -502,40 +502,25 @@ void CodeGenSchedModels::collectSchedClasses() { // NoItinerary is always the first class at Idx=0 SchedClasses.resize(1); - SchedClasses.back().Name = "NoItinerary"; + SchedClasses.back().Index = 0; + SchedClasses.back().Name = "NoInstrModel"; + SchedClasses.back().ItinClassDef = Records.getDef("NoItinerary"); SchedClasses.back().ProcIndices.push_back(0); - SchedClassIdxMap[SchedClasses.back().Name] = 0; - // Gather and sort all itinerary classes used by instruction descriptions. - RecVec ItinClassList; + // Create a SchedClass for each unique combination of itinerary class and + // SchedRW list. for (CodeGenTarget::inst_iterator I = Target.inst_begin(), E = Target.inst_end(); I != E; ++I) { Record *ItinDef = (*I)->TheDef->getValueAsDef("Itinerary"); - // Map a new SchedClass with no index. - if (!SchedClassIdxMap.count(ItinDef->getName())) { - SchedClassIdxMap[ItinDef->getName()] = 0; - ItinClassList.push_back(ItinDef); - } - } - // Assign each itinerary class unique number, skipping NoItinerary==0 - NumItineraryClasses = ItinClassList.size(); - std::sort(ItinClassList.begin(), ItinClassList.end(), LessRecord()); - for (unsigned i = 0, N = NumItineraryClasses; i < N; i++) { - Record *ItinDef = ItinClassList[i]; - SchedClassIdxMap[ItinDef->getName()] = SchedClasses.size(); - SchedClasses.push_back(CodeGenSchedClass(ItinDef)); - } - // Infer classes from SchedReadWrite resources listed for each - // instruction definition that inherits from class Sched. - for (CodeGenTarget::inst_iterator I = Target.inst_begin(), - E = Target.inst_end(); I != E; ++I) { - if (!(*I)->TheDef->isSubClassOf("Sched")) - continue; IdxVec Writes, Reads; - findRWs((*I)->TheDef->getValueAsListOfDefs("SchedRW"), Writes, Reads); + if (!(*I)->TheDef->isValueUnset("SchedRW")) + findRWs((*I)->TheDef->getValueAsListOfDefs("SchedRW"), Writes, Reads); + // ProcIdx == 0 indicates the class applies to all processors. IdxVec ProcIndices(1, 0); - addSchedClass(Writes, Reads, ProcIndices); + + unsigned SCIdx = addSchedClass(ItinDef, Writes, Reads, ProcIndices); + InstrClassMap[(*I)->TheDef] = SCIdx; } // Create classes for InstRW defs. RecVec InstRWDefs = Records.getAllDerivedDefinitions("InstRW"); @@ -549,68 +534,70 @@ void CodeGenSchedModels::collectSchedClasses() { DEBUG(EnableDump = true); if (!EnableDump) return; + for (CodeGenTarget::inst_iterator I = Target.inst_begin(), E = Target.inst_end(); I != E; ++I) { - Record *SchedDef = (*I)->TheDef; + std::string InstName = (*I)->TheDef->getName(); - if (SchedDef->isSubClassOf("Sched")) { + unsigned SCIdx = InstrClassMap.lookup((*I)->TheDef); + if (!SCIdx) { + dbgs() << "No machine model for " << (*I)->TheDef->getName() << '\n'; + continue; + } + CodeGenSchedClass &SC = getSchedClass(SCIdx); + if (SC.ProcIndices[0] != 0) + PrintFatalError((*I)->TheDef->getLoc(), "Instruction's sched class " + "must not be subtarget specific."); + + IdxVec ProcIndices; + if (SC.ItinClassDef->getName() != "NoItinerary") { + ProcIndices.push_back(0); + dbgs() << "Itinerary for " << InstName << ": " + << SC.ItinClassDef->getName() << '\n'; + } + if (!SC.Writes.empty()) { + ProcIndices.push_back(0); + dbgs() << "SchedRW machine model for " << InstName; + for (IdxIter WI = SC.Writes.begin(), WE = SC.Writes.end(); WI != WE; ++WI) + dbgs() << " " << SchedWrites[*WI].Name; + for (IdxIter RI = SC.Reads.begin(), RE = SC.Reads.end(); RI != RE; ++RI) + dbgs() << " " << SchedReads[*RI].Name; + dbgs() << '\n'; + } + const RecVec &RWDefs = SchedClasses[SCIdx].InstRWs; + for (RecIter RWI = RWDefs.begin(), RWE = RWDefs.end(); + RWI != RWE; ++RWI) { + const CodeGenProcModel &ProcModel = + getProcModel((*RWI)->getValueAsDef("SchedModel")); + ProcIndices.push_back(ProcModel.Index); + dbgs() << "InstRW on " << ProcModel.ModelName << " for " << InstName; IdxVec Writes; IdxVec Reads; - findRWs((*I)->TheDef->getValueAsListOfDefs("SchedRW"), Writes, Reads); - dbgs() << "SchedRW machine model for " << InstName; + findRWs((*RWI)->getValueAsListOfDefs("OperandReadWrites"), + Writes, Reads); for (IdxIter WI = Writes.begin(), WE = Writes.end(); WI != WE; ++WI) dbgs() << " " << SchedWrites[*WI].Name; for (IdxIter RI = Reads.begin(), RE = Reads.end(); RI != RE; ++RI) dbgs() << " " << SchedReads[*RI].Name; dbgs() << '\n'; } - unsigned SCIdx = InstrClassMap.lookup((*I)->TheDef); - if (SCIdx) { - const RecVec &RWDefs = SchedClasses[SCIdx].InstRWs; - for (RecIter RWI = RWDefs.begin(), RWE = RWDefs.end(); - RWI != RWE; ++RWI) { - const CodeGenProcModel &ProcModel = - getProcModel((*RWI)->getValueAsDef("SchedModel")); - dbgs() << "InstRW on " << ProcModel.ModelName << " for " << InstName; - IdxVec Writes; - IdxVec Reads; - findRWs((*RWI)->getValueAsListOfDefs("OperandReadWrites"), - Writes, Reads); - for (IdxIter WI = Writes.begin(), WE = Writes.end(); WI != WE; ++WI) - dbgs() << " " << SchedWrites[*WI].Name; - for (IdxIter RI = Reads.begin(), RE = Reads.end(); RI != RE; ++RI) - dbgs() << " " << SchedReads[*RI].Name; - dbgs() << '\n'; - } - continue; - } - if (!SchedDef->isSubClassOf("Sched") - && (SchedDef->getValueAsDef("Itinerary")->getName() == "NoItinerary")) { - dbgs() << "No machine model for " << (*I)->TheDef->getName() << '\n'; + for (std::vector<CodeGenProcModel>::iterator PI = ProcModels.begin(), + PE = ProcModels.end(); PI != PE; ++PI) { + if (!std::count(ProcIndices.begin(), ProcIndices.end(), PI->Index)) + dbgs() << "No machine model for " << (*I)->TheDef->getName() + << " on processor " << PI->ModelName << '\n'; } } } -unsigned CodeGenSchedModels::getSchedClassIdx( - const RecVec &RWDefs) const { - - IdxVec Writes, Reads; - findRWs(RWDefs, Writes, Reads); - return findSchedClassIdx(Writes, Reads); -} - /// Find an SchedClass that has been inferred from a per-operand list of /// SchedWrites and SchedReads. -unsigned CodeGenSchedModels::findSchedClassIdx(const IdxVec &Writes, +unsigned CodeGenSchedModels::findSchedClassIdx(Record *ItinClassDef, + const IdxVec &Writes, const IdxVec &Reads) const { for (SchedClassIter I = schedClassBegin(), E = schedClassEnd(); I != E; ++I) { - // Classes with InstRWs may have the same Writes/Reads as a class originally - // produced by a SchedRW definition. We need to be able to recover the - // original class index for processors that don't match any InstRWs. - if (I->ItinClassDef || !I->InstRWs.empty()) - continue; - - if (I->Writes == Writes && I->Reads == Reads) { + if (I->ItinClassDef == ItinClassDef + && I->Writes == Writes && I->Reads == Reads) { return I - schedClassBegin(); } } @@ -621,29 +608,17 @@ unsigned CodeGenSchedModels::findSchedClassIdx(const IdxVec &Writes, unsigned CodeGenSchedModels::getSchedClassIdx( const CodeGenInstruction &Inst) const { - unsigned SCIdx = InstrClassMap.lookup(Inst.TheDef); - if (SCIdx) - return SCIdx; - - // If this opcode isn't mapped by the subtarget fallback to the instruction - // definition's SchedRW or ItinDef values. - if (Inst.TheDef->isSubClassOf("Sched")) { - RecVec RWs = Inst.TheDef->getValueAsListOfDefs("SchedRW"); - return getSchedClassIdx(RWs); - } - Record *ItinDef = Inst.TheDef->getValueAsDef("Itinerary"); - assert(SchedClassIdxMap.count(ItinDef->getName()) && "missing ItinClass"); - unsigned Idx = SchedClassIdxMap.lookup(ItinDef->getName()); - assert(Idx <= NumItineraryClasses && "bad ItinClass index"); - return Idx; + return InstrClassMap.lookup(Inst.TheDef); } std::string CodeGenSchedModels::createSchedClassName( - const IdxVec &OperWrites, const IdxVec &OperReads) { + Record *ItinClassDef, const IdxVec &OperWrites, const IdxVec &OperReads) { std::string Name; + if (ItinClassDef && ItinClassDef->getName() != "NoItinerary") + Name = ItinClassDef->getName(); for (IdxIter WI = OperWrites.begin(), WE = OperWrites.end(); WI != WE; ++WI) { - if (WI != OperWrites.begin()) + if (!Name.empty()) Name += '_'; Name += SchedWrites[*WI].Name; } @@ -665,17 +640,18 @@ std::string CodeGenSchedModels::createSchedClassName(const RecVec &InstDefs) { return Name; } -/// Add an inferred sched class from a per-operand list of SchedWrites and -/// SchedReads. ProcIndices contains the set of IDs of processors that may -/// utilize this class. -unsigned CodeGenSchedModels::addSchedClass(const IdxVec &OperWrites, +/// Add an inferred sched class from an itinerary class and per-operand list of +/// SchedWrites and SchedReads. ProcIndices contains the set of IDs of +/// processors that may utilize this class. +unsigned CodeGenSchedModels::addSchedClass(Record *ItinClassDef, + const IdxVec &OperWrites, const IdxVec &OperReads, const IdxVec &ProcIndices) { assert(!ProcIndices.empty() && "expect at least one ProcIdx"); - unsigned Idx = findSchedClassIdx(OperWrites, OperReads); - if (Idx) { + unsigned Idx = findSchedClassIdx(ItinClassDef, OperWrites, OperReads); + if (Idx || SchedClasses[0].isKeyEqual(ItinClassDef, OperWrites, OperReads)) { IdxVec PI; std::set_union(SchedClasses[Idx].ProcIndices.begin(), SchedClasses[Idx].ProcIndices.end(), @@ -687,7 +663,9 @@ unsigned CodeGenSchedModels::addSchedClass(const IdxVec &OperWrites, Idx = SchedClasses.size(); SchedClasses.resize(Idx+1); CodeGenSchedClass &SC = SchedClasses.back(); - SC.Name = createSchedClassName(OperWrites, OperReads); + SC.Index = Idx; + SC.Name = createSchedClassName(ItinClassDef, OperWrites, OperReads); + SC.ItinClassDef = ItinClassDef; SC.Writes = OperWrites; SC.Reads = OperReads; SC.ProcIndices = ProcIndices; @@ -709,19 +687,10 @@ void CodeGenSchedModels::createInstRWClass(Record *InstRWDef) { PrintFatalError(InstRWDef->getLoc(), "No matching instruction opcodes"); for (RecIter I = InstDefs->begin(), E = InstDefs->end(); I != E; ++I) { - unsigned SCIdx = 0; InstClassMapTy::const_iterator Pos = InstrClassMap.find(*I); - if (Pos != InstrClassMap.end()) - SCIdx = Pos->second; - else { - // This instruction has not been mapped yet. Get the original class. All - // instructions in the same InstrRW class must be from the same original - // class because that is the fall-back class for other processors. - Record *ItinDef = (*I)->getValueAsDef("Itinerary"); - SCIdx = SchedClassIdxMap.lookup(ItinDef->getName()); - if (!SCIdx && (*I)->isSubClassOf("Sched")) - SCIdx = getSchedClassIdx((*I)->getValueAsListOfDefs("SchedRW")); - } + if (Pos == InstrClassMap.end()) + PrintFatalError((*I)->getLoc(), "No sched class for instruction."); + unsigned SCIdx = Pos->second; unsigned CIdx = 0, CEnd = ClassInstrs.size(); for (; CIdx != CEnd; ++CIdx) { if (ClassInstrs[CIdx].first == SCIdx) @@ -741,7 +710,7 @@ void CodeGenSchedModels::createInstRWClass(Record *InstRWDef) { ArrayRef<Record*> InstDefs = ClassInstrs[CIdx].second; // If the all instrs in the current class are accounted for, then leave // them mapped to their old class. - if (SchedClasses[OldSCIdx].InstRWs.size() == InstDefs.size()) { + if (OldSCIdx && SchedClasses[OldSCIdx].InstRWs.size() == InstDefs.size()) { assert(SchedClasses[OldSCIdx].ProcIndices[0] == 0 && "expected a generic SchedClass"); continue; @@ -749,6 +718,7 @@ void CodeGenSchedModels::createInstRWClass(Record *InstRWDef) { unsigned SCIdx = SchedClasses.size(); SchedClasses.resize(SCIdx+1); CodeGenSchedClass &SC = SchedClasses.back(); + SC.Index = SCIdx; SC.Name = createSchedClassName(InstDefs); // Preserve ItinDef and Writes/Reads for processors without an InstRW entry. SC.ItinClassDef = SchedClasses[OldSCIdx].ItinClassDef; @@ -780,32 +750,48 @@ void CodeGenSchedModels::createInstRWClass(Record *InstRWDef) { } } +// True if collectProcItins found anything. +bool CodeGenSchedModels::hasItineraries() const { + for (CodeGenSchedModels::ProcIter PI = procModelBegin(), PE = procModelEnd(); + PI != PE; ++PI) { + if (PI->hasItineraries()) + return true; + } + return false; +} + // Gather the processor itineraries. void CodeGenSchedModels::collectProcItins() { for (std::vector<CodeGenProcModel>::iterator PI = ProcModels.begin(), PE = ProcModels.end(); PI != PE; ++PI) { CodeGenProcModel &ProcModel = *PI; - RecVec ItinRecords = ProcModel.ItinsDef->getValueAsListOfDefs("IID"); - // Skip empty itinerary. - if (ItinRecords.empty()) + if (!ProcModel.hasItineraries()) continue; - ProcModel.ItinDefList.resize(NumItineraryClasses+1); + RecVec ItinRecords = ProcModel.ItinsDef->getValueAsListOfDefs("IID"); + assert(!ItinRecords.empty() && "ProcModel.hasItineraries is incorrect"); + + // Populate ItinDefList with Itinerary records. + ProcModel.ItinDefList.resize(NumInstrSchedClasses); // Insert each itinerary data record in the correct position within // the processor model's ItinDefList. for (unsigned i = 0, N = ItinRecords.size(); i < N; i++) { Record *ItinData = ItinRecords[i]; Record *ItinDef = ItinData->getValueAsDef("TheClass"); - if (!SchedClassIdxMap.count(ItinDef->getName())) { + bool FoundClass = false; + for (SchedClassIter SCI = schedClassBegin(), SCE = schedClassEnd(); + SCI != SCE; ++SCI) { + // Multiple SchedClasses may share an itinerary. Update all of them. + if (SCI->ItinClassDef == ItinDef) { + ProcModel.ItinDefList[SCI->Index] = ItinData; + FoundClass = true; + } + } + if (!FoundClass) { DEBUG(dbgs() << ProcModel.ItinsDef->getName() - << " has unused itinerary class " << ItinDef->getName() << '\n'); - continue; + << " missing class for itinerary " << ItinDef->getName() << '\n'); } - assert(SchedClassIdxMap.count(ItinDef->getName()) && "missing ItinClass"); - unsigned Idx = SchedClassIdxMap.lookup(ItinDef->getName()); - assert(Idx <= NumItineraryClasses && "bad ItinClass index"); - ProcModel.ItinDefList[Idx] = ItinData; } // Check for missing itinerary entries. assert(!ProcModel.ItinDefList[0] && "NoItinerary class can't have rec"); @@ -839,13 +825,17 @@ void CodeGenSchedModels::collectProcItinRW() { /// Infer new classes from existing classes. In the process, this may create new /// SchedWrites from sequences of existing SchedWrites. void CodeGenSchedModels::inferSchedClasses() { + DEBUG(dbgs() << NumInstrSchedClasses << " instr sched classes.\n"); + // Visit all existing classes and newly created classes. for (unsigned Idx = 0; Idx != SchedClasses.size(); ++Idx) { + assert(SchedClasses[Idx].Index == Idx && "bad SCIdx"); + if (SchedClasses[Idx].ItinClassDef) inferFromItinClass(SchedClasses[Idx].ItinClassDef, Idx); - else if (!SchedClasses[Idx].InstRWs.empty()) + if (!SchedClasses[Idx].InstRWs.empty()) inferFromInstRWs(Idx); - else { + if (!SchedClasses[Idx].Writes.empty()) { inferFromRW(SchedClasses[Idx].Writes, SchedClasses[Idx].Reads, Idx, SchedClasses[Idx].ProcIndices); } @@ -1042,11 +1032,13 @@ static bool hasVariant(ArrayRef<PredTransition> Transitions, // Populate IntersectingVariants with any variants or aliased sequences of the // given SchedRW whose processor indices and predicates are not mutually -// exclusive with the given transition, +// exclusive with the given transition. void PredTransitions::getIntersectingVariants( const CodeGenSchedRW &SchedRW, unsigned TransIdx, std::vector<TransVariant> &IntersectingVariants) { + bool GenericRW = false; + std::vector<TransVariant> Variants; if (SchedRW.HasVariants) { unsigned VarProcIdx = 0; @@ -1058,6 +1050,8 @@ void PredTransitions::getIntersectingVariants( const RecVec VarDefs = SchedRW.TheDef->getValueAsListOfDefs("Variants"); for (RecIter RI = VarDefs.begin(), RE = VarDefs.end(); RI != RE; ++RI) Variants.push_back(TransVariant(*RI, SchedRW.Index, VarProcIdx, 0)); + if (VarProcIdx == 0) + GenericRW = true; } for (RecIter AI = SchedRW.Aliases.begin(), AE = SchedRW.Aliases.end(); AI != AE; ++AI) { @@ -1081,6 +1075,8 @@ void PredTransitions::getIntersectingVariants( Variants.push_back( TransVariant(AliasRW.TheDef, SchedRW.Index, AliasProcIdx, 0)); } + if (AliasProcIdx == 0) + GenericRW = true; } for (unsigned VIdx = 0, VEnd = Variants.size(); VIdx != VEnd; ++VIdx) { TransVariant &Variant = Variants[VIdx]; @@ -1118,6 +1114,10 @@ void PredTransitions::getIntersectingVariants( TransVec.push_back(TransVec[TransIdx]); } } + if (GenericRW && IntersectingVariants.empty()) { + PrintFatalError(SchedRW.TheDef->getLoc(), "No variant of this type has " + "a matching predicate on any processor"); + } } // Push the Reads/Writes selected by this variant onto the PredTransition @@ -1215,10 +1215,6 @@ void PredTransitions::substituteVariantOperand( // This will push a copies of TransVec[TransIdx] on the back of TransVec. std::vector<TransVariant> IntersectingVariants; getIntersectingVariants(SchedRW, TransIdx, IntersectingVariants); - if (IntersectingVariants.empty()) - PrintFatalError(SchedRW.TheDef->getLoc(), - "No variant of this type has " - "a matching predicate on any processor"); // Now expand each variant on top of its copy of the transition. for (std::vector<TransVariant>::const_iterator IVI = IntersectingVariants.begin(), @@ -1295,8 +1291,8 @@ static void inferFromTransitions(ArrayRef<PredTransition> LastTransitions, IdxVec ProcIndices(I->ProcIndices.begin(), I->ProcIndices.end()); CodeGenSchedTransition SCTrans; SCTrans.ToClassIdx = - SchedModels.addSchedClass(OperWritesVariant, OperReadsVariant, - ProcIndices); + SchedModels.addSchedClass(/*ItinClassDef=*/0, OperWritesVariant, + OperReadsVariant, ProcIndices); SCTrans.ProcIndices = ProcIndices; // The final PredTerm is unique set of predicates guarding the transition. RecVec Preds; @@ -1318,7 +1314,7 @@ void CodeGenSchedModels::inferFromRW(const IdxVec &OperWrites, const IdxVec &OperReads, unsigned FromClassIdx, const IdxVec &ProcIndices) { - DEBUG(dbgs() << "INFER RW: "); + DEBUG(dbgs() << "INFER RW proc("; dumpIdxVec(ProcIndices); dbgs() << ") "); // Create a seed transition with an empty PredTerm and the expanded sequences // of SchedWrites for the current SchedClass. @@ -1371,6 +1367,56 @@ void CodeGenSchedModels::inferFromRW(const IdxVec &OperWrites, inferFromTransitions(LastTransitions, FromClassIdx, *this); } +// Check if any processor resource group contains all resource records in +// SubUnits. +bool CodeGenSchedModels::hasSuperGroup(RecVec &SubUnits, CodeGenProcModel &PM) { + for (unsigned i = 0, e = PM.ProcResourceDefs.size(); i < e; ++i) { + if (!PM.ProcResourceDefs[i]->isSubClassOf("ProcResGroup")) + continue; + RecVec SuperUnits = + PM.ProcResourceDefs[i]->getValueAsListOfDefs("Resources"); + RecIter RI = SubUnits.begin(), RE = SubUnits.end(); + for ( ; RI != RE; ++RI) { + if (std::find(SuperUnits.begin(), SuperUnits.end(), *RI) + == SuperUnits.end()) { + break; + } + } + if (RI == RE) + return true; + } + return false; +} + +// Verify that overlapping groups have a common supergroup. +void CodeGenSchedModels::verifyProcResourceGroups(CodeGenProcModel &PM) { + for (unsigned i = 0, e = PM.ProcResourceDefs.size(); i < e; ++i) { + if (!PM.ProcResourceDefs[i]->isSubClassOf("ProcResGroup")) + continue; + RecVec CheckUnits = + PM.ProcResourceDefs[i]->getValueAsListOfDefs("Resources"); + for (unsigned j = i+1; j < e; ++j) { + if (!PM.ProcResourceDefs[j]->isSubClassOf("ProcResGroup")) + continue; + RecVec OtherUnits = + PM.ProcResourceDefs[j]->getValueAsListOfDefs("Resources"); + if (std::find_first_of(CheckUnits.begin(), CheckUnits.end(), + OtherUnits.begin(), OtherUnits.end()) + != CheckUnits.end()) { + // CheckUnits and OtherUnits overlap + OtherUnits.insert(OtherUnits.end(), CheckUnits.begin(), + CheckUnits.end()); + if (!hasSuperGroup(OtherUnits, PM)) { + PrintFatalError((PM.ProcResourceDefs[i])->getLoc(), + "proc resource group overlaps with " + + PM.ProcResourceDefs[j]->getName() + + " but no supergroup contains both."); + } + } + } + } +} + // Collect and sort WriteRes, ReadAdvance, and ProcResources. void CodeGenSchedModels::collectProcResources() { // Add any subtarget-specific SchedReadWrites that are directly associated @@ -1441,6 +1487,7 @@ void CodeGenSchedModels::collectProcResources() { dbgs() << (*RI)->getName() << " "; } dbgs() << '\n'); + verifyProcResourceGroups(PM); } } @@ -1542,6 +1589,20 @@ Record *CodeGenSchedModels::findProcResUnits(Record *ProcResKind, ProcUnitDef = *RI; } } + RecVec ProcResGroups = Records.getAllDerivedDefinitions("ProcResGroup"); + for (RecIter RI = ProcResGroups.begin(), RE = ProcResGroups.end(); + RI != RE; ++RI) { + + if (*RI == ProcResKind + && (*RI)->getValueAsDef("SchedModel") == PM.ModelDef) { + if (ProcUnitDef) { + PrintFatalError((*RI)->getLoc(), + "Multiple ProcessorResourceUnits associated with " + + ProcResKind->getName()); + } + ProcUnitDef = *RI; + } + } if (!ProcUnitDef) { PrintFatalError(ProcResKind->getLoc(), "No ProcessorResources associated with " @@ -1563,6 +1624,9 @@ void CodeGenSchedModels::addProcResource(Record *ProcResKind, return; PM.ProcResourceDefs.push_back(ProcResUnits); + if (ProcResUnits->isSubClassOf("ProcResGroup")) + return; + if (!ProcResUnits->getValueInit("Super")->isComplete()) return; @@ -1625,7 +1689,7 @@ void CodeGenSchedRW::dump() const { } void CodeGenSchedClass::dump(const CodeGenSchedModels* SchedModels) const { - dbgs() << "SCHEDCLASS " << Name << '\n' + dbgs() << "SCHEDCLASS " << Index << ":" << Name << '\n' << " Writes: "; for (unsigned i = 0, N = Writes.size(); i < N; ++i) { SchedModels->getSchedWrite(Writes[i]).dump(); @@ -1643,6 +1707,13 @@ void CodeGenSchedClass::dump(const CodeGenSchedModels* SchedModels) const { } } dbgs() << "\n ProcIdx: "; dumpIdxVec(ProcIndices); dbgs() << '\n'; + if (!Transitions.empty()) { + dbgs() << "\n Transitions for Proc "; + for (std::vector<CodeGenSchedTransition>::const_iterator + TI = Transitions.begin(), TE = Transitions.end(); TI != TE; ++TI) { + dumpIdxVec(TI->ProcIndices); + } + } } void PredTransitions::dump() const { diff --git a/utils/TableGen/CodeGenSchedule.h b/utils/TableGen/CodeGenSchedule.h index 78f115ecd8..2e0a149104 100644 --- a/utils/TableGen/CodeGenSchedule.h +++ b/utils/TableGen/CodeGenSchedule.h @@ -125,6 +125,7 @@ struct CodeGenSchedTransition { /// itinerary class. Each inherits the processor index from the ItinRW record /// that mapped the itinerary class to the variant Writes or Reads. struct CodeGenSchedClass { + unsigned Index; std::string Name; Record *ItinClassDef; @@ -141,12 +142,16 @@ struct CodeGenSchedClass { // off to join another inferred class. RecVec InstRWs; - CodeGenSchedClass(): ItinClassDef(0) {} - CodeGenSchedClass(Record *rec): ItinClassDef(rec) { - Name = rec->getName(); - ProcIndices.push_back(0); + CodeGenSchedClass(): Index(0), ItinClassDef(0) {} + + bool isKeyEqual(Record *IC, const IdxVec &W, const IdxVec &R) { + return ItinClassDef == IC && Writes == W && Reads == R; } + // Is this class generated from a variants if existing classes? Instructions + // are never mapped directly to inferred scheduling classes. + bool isInferred() const { return !ItinClassDef; } + #ifndef NDEBUG void dump(const CodeGenSchedModels *SchedModels) const; #endif @@ -189,11 +194,16 @@ struct CodeGenProcModel { // Per-operand machine model resources associated with this processor. RecVec ProcResourceDefs; + RecVec ProcResGroupDefs; CodeGenProcModel(unsigned Idx, const std::string &Name, Record *MDef, Record *IDef) : Index(Idx), ModelName(Name), ModelDef(MDef), ItinsDef(IDef) {} + bool hasItineraries() const { + return !ItinsDef->getValueAsListOfDefs("IID").empty(); + } + bool hasInstrSchedModel() const { return !WriteResDefs.empty() || !ItinRWDefs.empty(); } @@ -227,24 +237,11 @@ class CodeGenSchedModels { // List of unique SchedClasses. std::vector<CodeGenSchedClass> SchedClasses; - // Map SchedClass name to itinerary index. - // These are either explicit itinerary classes or classes implied by - // instruction definitions with SchedReadWrite lists. - StringMap<unsigned> SchedClassIdxMap; - - // SchedClass indices 1 up to and including NumItineraryClasses identify - // itinerary classes that are explicitly used for this target's instruction - // definitions. NoItinerary always has index 0 regardless of whether it is - // explicitly referenced. - // - // Any implied SchedClass has an index greater than NumItineraryClasses. - unsigned NumItineraryClasses; - // Any inferred SchedClass has an index greater than NumInstrSchedClassses. unsigned NumInstrSchedClasses; - // Map Instruction to SchedClass index. Only for Instructions mentioned in - // InstRW records. + // Map each instruction to its unique SchedClass index considering the + // combination of it's itinerary class, SchedRW list, and InstRW records. typedef DenseMap<Record*, unsigned> InstClassMapTy; InstClassMapTy InstrClassMap; @@ -280,6 +277,9 @@ public: ProcIter procModelBegin() const { return ProcModels.begin(); } ProcIter procModelEnd() const { return ProcModels.end(); } + // Return true if any processors have itineraries. + bool hasItineraries() const; + // Get a SchedWrite from its index. const CodeGenSchedRW &getSchedWrite(unsigned Idx) const { assert(Idx < SchedWrites.size() && "bad SchedWrite index"); @@ -311,16 +311,6 @@ public: // Return true if the given write record is referenced by a ReadAdvance. bool hasReadOfWrite(Record *WriteDef) const; - // Check if any instructions are assigned to an explicit itinerary class other - // than NoItinerary. - bool hasItineraryClasses() const { return NumItineraryClasses > 0; } - - // Return the number of itinerary classes in use by this target's instruction - // descriptions, not including "NoItinerary". - unsigned numItineraryClasses() const { - return NumItineraryClasses; - } - // Get a SchedClass from its index. CodeGenSchedClass &getSchedClass(unsigned Idx) { assert(Idx < SchedClasses.size() && "bad SchedClass index"); @@ -336,28 +326,26 @@ public: // for NoItinerary. unsigned getSchedClassIdx(const CodeGenInstruction &Inst) const; - unsigned getSchedClassIdx(const RecVec &RWDefs) const; - - unsigned getSchedClassIdxForItin(const Record *ItinDef) { - return SchedClassIdxMap[ItinDef->getName()]; - } - typedef std::vector<CodeGenSchedClass>::const_iterator SchedClassIter; SchedClassIter schedClassBegin() const { return SchedClasses.begin(); } SchedClassIter schedClassEnd() const { return SchedClasses.end(); } + unsigned numInstrSchedClasses() const { return NumInstrSchedClasses; } + void findRWs(const RecVec &RWDefs, IdxVec &Writes, IdxVec &Reads) const; void findRWs(const RecVec &RWDefs, IdxVec &RWs, bool IsRead) const; void expandRWSequence(unsigned RWIdx, IdxVec &RWSeq, bool IsRead) const; void expandRWSeqForProc(unsigned RWIdx, IdxVec &RWSeq, bool IsRead, const CodeGenProcModel &ProcModel) const; - unsigned addSchedClass(const IdxVec &OperWrites, const IdxVec &OperReads, - const IdxVec &ProcIndices); + unsigned addSchedClass(Record *ItinDef, const IdxVec &OperWrites, + const IdxVec &OperReads, const IdxVec &ProcIndices); unsigned findOrInsertRW(ArrayRef<unsigned> Seq, bool IsRead); - unsigned findSchedClassIdx(const IdxVec &Writes, const IdxVec &Reads) const; + unsigned findSchedClassIdx(Record *ItinClassDef, + const IdxVec &Writes, + const IdxVec &Reads) const; Record *findProcResUnits(Record *ProcResKind, const CodeGenProcModel &PM) const; @@ -375,7 +363,8 @@ private: void collectSchedClasses(); - std::string createSchedClassName(const IdxVec &OperWrites, + std::string createSchedClassName(Record *ItinClassDef, + const IdxVec &OperWrites, const IdxVec &OperReads); std::string createSchedClassName(const RecVec &InstDefs); void createInstRWClass(Record *InstRWDef); @@ -391,6 +380,9 @@ private: void inferFromItinClass(Record *ItinClassDef, unsigned FromClassIdx); void inferFromInstRWs(unsigned SCIdx); + bool hasSuperGroup(RecVec &SubUnits, CodeGenProcModel &PM); + void verifyProcResourceGroups(CodeGenProcModel &PM); + void collectProcResources(); void collectItinProcResources(Record *ItinClassDef); diff --git a/utils/TableGen/CodeGenTarget.cpp b/utils/TableGen/CodeGenTarget.cpp index 5d2f96b851..ab2b002b19 100644 --- a/utils/TableGen/CodeGenTarget.cpp +++ b/utils/TableGen/CodeGenTarget.cpp @@ -229,7 +229,7 @@ getRegisterVTs(Record *R) const { for (unsigned i = 0, e = RCs.size(); i != e; ++i) { const CodeGenRegisterClass &RC = *RCs[i]; if (RC.contains(Reg)) { - const std::vector<MVT::SimpleValueType> &InVTs = RC.getValueTypes(); + ArrayRef<MVT::SimpleValueType> InVTs = RC.getValueTypes(); Result.insert(Result.end(), InVTs.begin(), InVTs.end()); } } diff --git a/utils/TableGen/CodeGenTarget.h b/utils/TableGen/CodeGenTarget.h index 4a1c6d8fcb..6271443029 100644 --- a/utils/TableGen/CodeGenTarget.h +++ b/utils/TableGen/CodeGenTarget.h @@ -68,7 +68,7 @@ class CodeGenTarget { mutable DenseMap<const Record*, CodeGenInstruction*> Instructions; mutable CodeGenRegBank *RegBank; mutable std::vector<Record*> RegAltNameIndices; - mutable std::vector<MVT::SimpleValueType> LegalValueTypes; + mutable SmallVector<MVT::SimpleValueType, 8> LegalValueTypes; void ReadRegAltNameIndices() const; void ReadInstructions() const; void ReadLegalValueTypes() const; @@ -129,7 +129,7 @@ public: /// specified physical register. std::vector<MVT::SimpleValueType> getRegisterVTs(Record *R) const; - const std::vector<MVT::SimpleValueType> &getLegalValueTypes() const { + ArrayRef<MVT::SimpleValueType> getLegalValueTypes() const { if (LegalValueTypes.empty()) ReadLegalValueTypes(); return LegalValueTypes; } @@ -137,7 +137,7 @@ public: /// isLegalValueType - Return true if the specified value type is natively /// supported by the target (i.e. there are registers that directly hold it). bool isLegalValueType(MVT::SimpleValueType VT) const { - const std::vector<MVT::SimpleValueType> &LegalVTs = getLegalValueTypes(); + ArrayRef<MVT::SimpleValueType> LegalVTs = getLegalValueTypes(); for (unsigned i = 0, e = LegalVTs.size(); i != e; ++i) if (LegalVTs[i] == VT) return true; return false; diff --git a/utils/TableGen/DAGISelMatcherGen.cpp b/utils/TableGen/DAGISelMatcherGen.cpp index 38ffa30ec8..ed41631456 100644 --- a/utils/TableGen/DAGISelMatcherGen.cpp +++ b/utils/TableGen/DAGISelMatcherGen.cpp @@ -211,6 +211,12 @@ void MatcherGen::EmitLeafMatchCode(const TreePatternNode *N) { return AddMatcher(new CheckIntegerMatcher(II->getValue())); } + // An UnsetInit represents a named node without any constraints. + if (N->getLeafValue() == UnsetInit::get()) { + assert(N->hasName() && "Unnamed ? leaf"); + return; + } + DefInit *DI = dyn_cast<DefInit>(N->getLeafValue()); if (DI == 0) { errs() << "Unknown leaf kind: " << *N << "\n"; @@ -218,6 +224,17 @@ void MatcherGen::EmitLeafMatchCode(const TreePatternNode *N) { } Record *LeafRec = DI->getDef(); + + // A ValueType leaf node can represent a register when named, or itself when + // unnamed. + if (LeafRec->isSubClassOf("ValueType")) { + // A named ValueType leaf always matches: (add i32:$a, i32:$b). + if (N->hasName()) + return; + // An unnamed ValueType as in (sext_inreg GPR:$foo, i8). + return AddMatcher(new CheckValueTypeMatcher(LeafRec->getName())); + } + if (// Handle register references. Nothing to do here, they always match. LeafRec->isSubClassOf("RegisterClass") || LeafRec->isSubClassOf("RegisterOperand") || @@ -236,9 +253,6 @@ void MatcherGen::EmitLeafMatchCode(const TreePatternNode *N) { return; } - if (LeafRec->isSubClassOf("ValueType")) - return AddMatcher(new CheckValueTypeMatcher(LeafRec->getName())); - if (LeafRec->isSubClassOf("CondCode")) return AddMatcher(new CheckCondCodeMatcher(LeafRec->getName())); @@ -734,20 +748,33 @@ EmitResultInstructionAsOperand(const TreePatternNode *N, continue; } - const TreePatternNode *Child = N->getChild(ChildNo); - // Otherwise this is a normal operand or a predicate operand without // 'execute always'; emit it. - unsigned BeforeAddingNumOps = InstOps.size(); - EmitResultOperand(Child, InstOps); - assert(InstOps.size() > BeforeAddingNumOps && "Didn't add any operands"); - // If the operand is an instruction and it produced multiple results, just - // take the first one. - if (!Child->isLeaf() && Child->getOperator()->isSubClassOf("Instruction")) - InstOps.resize(BeforeAddingNumOps+1); + // For operands with multiple sub-operands we may need to emit + // multiple child patterns to cover them all. However, ComplexPattern + // children may themselves emit multiple MI operands. + unsigned NumSubOps = 1; + if (OperandNode->isSubClassOf("Operand")) { + DagInit *MIOpInfo = OperandNode->getValueAsDag("MIOperandInfo"); + if (unsigned NumArgs = MIOpInfo->getNumArgs()) + NumSubOps = NumArgs; + } - ++ChildNo; + unsigned FinalNumOps = InstOps.size() + NumSubOps; + while (InstOps.size() < FinalNumOps) { + const TreePatternNode *Child = N->getChild(ChildNo); + unsigned BeforeAddingNumOps = InstOps.size(); + EmitResultOperand(Child, InstOps); + assert(InstOps.size() > BeforeAddingNumOps && "Didn't add any operands"); + + // If the operand is an instruction and it produced multiple results, just + // take the first one. + if (!Child->isLeaf() && Child->getOperator()->isSubClassOf("Instruction")) + InstOps.resize(BeforeAddingNumOps+1); + + ++ChildNo; + } } // If this node has input glue or explicitly specified input physregs, we diff --git a/utils/TableGen/RegisterInfoEmitter.cpp b/utils/TableGen/RegisterInfoEmitter.cpp index cf225117e1..1b5d90b8bd 100644 --- a/utils/TableGen/RegisterInfoEmitter.cpp +++ b/utils/TableGen/RegisterInfoEmitter.cpp @@ -1031,7 +1031,7 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target, } // Build a shared array of value types. - SequenceToOffsetTable<std::vector<MVT::SimpleValueType> > VTSeqs; + SequenceToOffsetTable<SmallVector<MVT::SimpleValueType, 4> > VTSeqs; for (unsigned rc = 0, e = RegisterClasses.size(); rc != e; ++rc) VTSeqs.add(RegisterClasses[rc]->VTs); VTSeqs.layout(); diff --git a/utils/TableGen/SubtargetEmitter.cpp b/utils/TableGen/SubtargetEmitter.cpp index fc8d00dd83..4918b1b143 100644 --- a/utils/TableGen/SubtargetEmitter.cpp +++ b/utils/TableGen/SubtargetEmitter.cpp @@ -87,6 +87,8 @@ class SubtargetEmitter { const CodeGenProcModel &ProcModel); Record *FindReadAdvance(const CodeGenSchedRW &SchedRead, const CodeGenProcModel &ProcModel); + void ExpandProcResources(RecVec &PRVec, std::vector<int64_t> &Cycles, + const CodeGenProcModel &ProcModel); void GenSchedClassTables(const CodeGenProcModel &ProcModel, SchedClassTables &SchedTables); void EmitSchedClassTables(SchedClassTables &SchedTables, raw_ostream &OS); @@ -445,17 +447,15 @@ EmitStageAndOperandCycleData(raw_ostream &OS, // If this processor defines no itineraries, then leave the itinerary list // empty. std::vector<InstrItinerary> &ItinList = ProcItinLists.back(); - if (ProcModel.ItinDefList.empty()) + if (!ProcModel.hasItineraries()) continue; - // Reserve index==0 for NoItinerary. - ItinList.resize(SchedModels.numItineraryClasses()+1); - const std::string &Name = ProcModel.ItinsDef->getName(); - // For each itinerary data - for (unsigned SchedClassIdx = 0, - SchedClassEnd = ProcModel.ItinDefList.size(); + ItinList.resize(SchedModels.numInstrSchedClasses()); + assert(ProcModel.ItinDefList.size() == ItinList.size() && "bad Itins"); + + for (unsigned SchedClassIdx = 0, SchedClassEnd = ItinList.size(); SchedClassIdx < SchedClassEnd; ++SchedClassIdx) { // Next itinerary data @@ -631,13 +631,31 @@ void SubtargetEmitter::EmitProcessorResources(const CodeGenProcModel &ProcModel, for (unsigned i = 0, e = ProcModel.ProcResourceDefs.size(); i < e; ++i) { Record *PRDef = ProcModel.ProcResourceDefs[i]; - // Find the SuperIdx - unsigned SuperIdx = 0; Record *SuperDef = 0; - if (PRDef->getValueInit("Super")->isComplete()) { - SuperDef = - SchedModels.findProcResUnits(PRDef->getValueAsDef("Super"), ProcModel); - SuperIdx = ProcModel.getProcResourceIdx(SuperDef); + unsigned SuperIdx = 0; + unsigned NumUnits = 0; + bool IsBuffered = true; + if (PRDef->isSubClassOf("ProcResGroup")) { + RecVec ResUnits = PRDef->getValueAsListOfDefs("Resources"); + for (RecIter RUI = ResUnits.begin(), RUE = ResUnits.end(); + RUI != RUE; ++RUI) { + if (!NumUnits) + IsBuffered = (*RUI)->getValueAsBit("Buffered"); + else if(IsBuffered != (*RUI)->getValueAsBit("Buffered")) + PrintFatalError(PRDef->getLoc(), + "Mixing buffered and unbuffered resources."); + NumUnits += (*RUI)->getValueAsInt("NumUnits"); + } + } + else { + // Find the SuperIdx + if (PRDef->getValueInit("Super")->isComplete()) { + SuperDef = SchedModels.findProcResUnits( + PRDef->getValueAsDef("Super"), ProcModel); + SuperIdx = ProcModel.getProcResourceIdx(SuperDef); + } + NumUnits = PRDef->getValueAsInt("NumUnits"); + IsBuffered = PRDef->getValueAsBit("Buffered"); } // Emit the ProcResourceDesc if (i+1 == e) @@ -645,8 +663,8 @@ void SubtargetEmitter::EmitProcessorResources(const CodeGenProcModel &ProcModel, OS << " {DBGFIELD(\"" << PRDef->getName() << "\") "; if (PRDef->getName().size() < 15) OS.indent(15 - PRDef->getName().size()); - OS << PRDef->getValueAsInt("NumUnits") << ", " << SuperIdx << ", " - << PRDef->getValueAsBit("Buffered") << "}" << Sep << " // #" << i+1; + OS << NumUnits << ", " << SuperIdx << ", " + << IsBuffered << "}" << Sep << " // #" << i+1; if (SuperDef) OS << ", Super=" << SuperDef->getName(); OS << "\n"; @@ -763,6 +781,56 @@ Record *SubtargetEmitter::FindReadAdvance(const CodeGenSchedRW &SchedRead, return ResDef; } +// Expand an explicit list of processor resources into a full list of implied +// resource groups and super resources that cover them. +void SubtargetEmitter::ExpandProcResources(RecVec &PRVec, + std::vector<int64_t> &Cycles, + const CodeGenProcModel &PM) { + // Default to 1 resource cycle. + Cycles.resize(PRVec.size(), 1); + for (unsigned i = 0, e = PRVec.size(); i != e; ++i) { + Record *PRDef = PRVec[i]; + RecVec SubResources; + if (PRDef->isSubClassOf("ProcResGroup")) + SubResources = PRDef->getValueAsListOfDefs("Resources"); + else { + SubResources.push_back(PRDef); + PRDef = SchedModels.findProcResUnits(PRVec[i], PM); + for (Record *SubDef = PRDef; + SubDef->getValueInit("Super")->isComplete();) { + if (SubDef->isSubClassOf("ProcResGroup")) { + // Disallow this for simplicitly. + PrintFatalError(SubDef->getLoc(), "Processor resource group " + " cannot be a super resources."); + } + Record *SuperDef = + SchedModels.findProcResUnits(SubDef->getValueAsDef("Super"), PM); + PRVec.push_back(SuperDef); + Cycles.push_back(Cycles[i]); + SubDef = SuperDef; + } + } + for (RecIter PRI = PM.ProcResourceDefs.begin(), + PRE = PM.ProcResourceDefs.end(); + PRI != PRE; ++PRI) { + if (*PRI == PRDef || !(*PRI)->isSubClassOf("ProcResGroup")) + continue; + RecVec SuperResources = (*PRI)->getValueAsListOfDefs("Resources"); + RecIter SubI = SubResources.begin(), SubE = SubResources.end(); + for( ; SubI != SubE; ++SubI) { + if (std::find(SuperResources.begin(), SuperResources.end(), *SubI) + == SuperResources.end()) { + break; + } + } + if (SubI == SubE) { + PRVec.push_back(*PRI); + Cycles.push_back(Cycles[i]); + } + } + } +} + // Generate the SchedClass table for this processor and update global // tables. Must be called for each processor in order. void SubtargetEmitter::GenSchedClassTables(const CodeGenProcModel &ProcModel, @@ -787,7 +855,22 @@ void SubtargetEmitter::GenSchedClassTables(const CodeGenProcModel &ProcModel, SCDesc.ReadAdvanceIdx = 0; // A Variant SchedClass has no resources of its own. - if (!SCI->Transitions.empty()) { + bool HasVariants = false; + for (std::vector<CodeGenSchedTransition>::const_iterator + TI = SCI->Transitions.begin(), TE = SCI->Transitions.end(); + TI != TE; ++TI) { + if (TI->ProcIndices[0] == 0) { + HasVariants = true; + break; + } + IdxIter PIPos = std::find(TI->ProcIndices.begin(), + TI->ProcIndices.end(), ProcModel.Index); + if (PIPos != TI->ProcIndices.end()) { + HasVariants = true; + break; + } + } + if (HasVariants) { SCDesc.NumMicroOps = MCSchedClassDesc::VariantNumMicroOps; continue; } @@ -804,27 +887,8 @@ void SubtargetEmitter::GenSchedClassTables(const CodeGenProcModel &ProcModel, } IdxVec Writes = SCI->Writes; IdxVec Reads = SCI->Reads; - if (SCI->ItinClassDef) { - assert(SCI->InstRWs.empty() && "ItinClass should not have InstRWs"); - // Check this processor's itinerary class resources. - for (RecIter II = ProcModel.ItinRWDefs.begin(), - IE = ProcModel.ItinRWDefs.end(); II != IE; ++II) { - RecVec Matched = (*II)->getValueAsListOfDefs("MatchedItinClasses"); - if (std::find(Matched.begin(), Matched.end(), SCI->ItinClassDef) - != Matched.end()) { - SchedModels.findRWs((*II)->getValueAsListOfDefs("OperandReadWrites"), - Writes, Reads); - break; - } - } - if (Writes.empty()) { - DEBUG(dbgs() << ProcModel.ItinsDef->getName() - << " does not have resources for itinerary class " - << SCI->ItinClassDef->getName() << '\n'); - } - } - else if (!SCI->InstRWs.empty()) { - // This class may have a default ReadWrite list which can be overriden by + if (!SCI->InstRWs.empty()) { + // This class has a default ReadWrite list which can be overriden by // InstRW definitions. Record *RWDef = 0; for (RecIter RWI = SCI->InstRWs.begin(), RWE = SCI->InstRWs.end(); @@ -842,6 +906,23 @@ void SubtargetEmitter::GenSchedClassTables(const CodeGenProcModel &ProcModel, Writes, Reads); } } + if (Writes.empty()) { + // Check this processor's itinerary class resources. + for (RecIter II = ProcModel.ItinRWDefs.begin(), + IE = ProcModel.ItinRWDefs.end(); II != IE; ++II) { + RecVec Matched = (*II)->getValueAsListOfDefs("MatchedItinClasses"); + if (std::find(Matched.begin(), Matched.end(), SCI->ItinClassDef) + != Matched.end()) { + SchedModels.findRWs((*II)->getValueAsListOfDefs("OperandReadWrites"), + Writes, Reads); + break; + } + } + if (Writes.empty()) { + DEBUG(dbgs() << ProcModel.ModelName + << " does not have resources for class " << SCI->Name << '\n'); + } + } // Sum resources across all operand writes. std::vector<MCWriteProcResEntry> WriteProcResources; std::vector<MCWriteLatencyEntry> WriteLatencies; @@ -859,7 +940,8 @@ void SubtargetEmitter::GenSchedClassTables(const CodeGenProcModel &ProcModel, WriterNames.push_back(SchedModels.getSchedWrite(WriteID).Name); // If this Write is not referenced by a ReadAdvance, don't distinguish it // from other WriteLatency entries. - if (!SchedModels.hasReadOfWrite(SchedModels.getSchedWrite(WriteID).TheDef)) { + if (!SchedModels.hasReadOfWrite( + SchedModels.getSchedWrite(WriteID).TheDef)) { WriteID = 0; } WLEntry.WriteResourceID = WriteID; @@ -884,16 +966,29 @@ void SubtargetEmitter::GenSchedClassTables(const CodeGenProcModel &ProcModel, RecVec PRVec = WriteRes->getValueAsListOfDefs("ProcResources"); std::vector<int64_t> Cycles = WriteRes->getValueAsListOfInts("ResourceCycles"); + + ExpandProcResources(PRVec, Cycles, ProcModel); + for (unsigned PRIdx = 0, PREnd = PRVec.size(); PRIdx != PREnd; ++PRIdx) { MCWriteProcResEntry WPREntry; WPREntry.ProcResourceIdx = ProcModel.getProcResourceIdx(PRVec[PRIdx]); assert(WPREntry.ProcResourceIdx && "Bad ProcResourceIdx"); - if (Cycles.size() > PRIdx) - WPREntry.Cycles = Cycles[PRIdx]; - else - WPREntry.Cycles = 1; - WriteProcResources.push_back(WPREntry); + WPREntry.Cycles = Cycles[PRIdx]; + // If this resource is already used in this sequence, add the current + // entry's cycles so that the same resource appears to be used + // serially, rather than multiple parallel uses. This is important for + // in-order machine where the resource consumption is a hazard. + unsigned WPRIdx = 0, WPREnd = WriteProcResources.size(); + for( ; WPRIdx != WPREnd; ++WPRIdx) { + if (WriteProcResources[WPRIdx].ProcResourceIdx + == WPREntry.ProcResourceIdx) { + WriteProcResources[WPRIdx].Cycles += WPREntry.Cycles; + break; + } + } + if (WPRIdx == WPREnd) + WriteProcResources.push_back(WPREntry); } } WriteLatencies.push_back(WLEntry); @@ -1062,7 +1157,7 @@ void SubtargetEmitter::EmitSchedClassTables(SchedClassTables &SchedTables, // The first class is always invalid. We no way to distinguish it except by // name and position. - assert(SchedModels.getSchedClass(0).Name == "NoItinerary" + assert(SchedModels.getSchedClass(0).Name == "NoInstrModel" && "invalid class not first"); OS << " {DBGFIELD(\"InvalidSchedClass\") " << MCSchedClassDesc::InvalidNumMicroOps @@ -1119,7 +1214,7 @@ void SubtargetEmitter::EmitProcessorModels(raw_ostream &OS) { - SchedModels.schedClassBegin()) << ",\n"; else OS << " 0, 0, 0, 0, // No instruction-level machine model.\n"; - if (SchedModels.hasItineraryClasses()) + if (SchedModels.hasItineraries()) OS << " " << PI->ItinsDef->getName() << ");\n"; else OS << " 0); // No Itinerary\n"; @@ -1176,7 +1271,7 @@ void SubtargetEmitter::EmitSchedModel(raw_ostream &OS) { << "#define DBGFIELD(x)\n" << "#endif\n"; - if (SchedModels.hasItineraryClasses()) { + if (SchedModels.hasItineraries()) { std::vector<std::vector<InstrItinerary> > ProcItinLists; // Emit the stage data EmitStageAndOperandCycleData(OS, ProcItinLists); @@ -1217,7 +1312,7 @@ void SubtargetEmitter::EmitSchedModelHelpers(std::string ClassName, SCE = SchedModels.schedClassEnd(); SCI != SCE; ++SCI) { if (SCI->Transitions.empty()) continue; - VariantClasses.push_back(SCI - SchedModels.schedClassBegin()); + VariantClasses.push_back(SCI->Index); } if (!VariantClasses.empty()) { OS << " switch (SchedClass) {\n"; @@ -1264,13 +1359,8 @@ void SubtargetEmitter::EmitSchedModelHelpers(std::string ClassName, if (*PI == 0) break; } - unsigned SCIdx = 0; - if (SC.ItinClassDef) - SCIdx = SchedModels.getSchedClassIdxForItin(SC.ItinClassDef); - else - SCIdx = SchedModels.findSchedClassIdx(SC.Writes, SC.Reads); - if (SCIdx != *VCI) - OS << " return " << SCIdx << ";\n"; + if (SC.isInferred()) + OS << " return " << SC.Index << ";\n"; OS << " break;\n"; } OS << " };\n"; @@ -1376,7 +1466,7 @@ void SubtargetEmitter::run(raw_ostream &OS) { << Target << "WriteProcResTable, " << Target << "WriteLatencyTable, " << Target << "ReadAdvanceTable, "; - if (SchedModels.hasItineraryClasses()) { + if (SchedModels.hasItineraries()) { OS << '\n'; OS.indent(22); OS << Target << "Stages, " << Target << "OperandCycles, " @@ -1433,7 +1523,7 @@ void SubtargetEmitter::run(raw_ostream &OS) { OS << "extern const llvm::MCReadAdvanceEntry " << Target << "ReadAdvanceTable[];\n"; - if (SchedModels.hasItineraryClasses()) { + if (SchedModels.hasItineraries()) { OS << "extern const llvm::InstrStage " << Target << "Stages[];\n"; OS << "extern const unsigned " << Target << "OperandCycles[];\n"; OS << "extern const unsigned " << Target << "ForwardingPaths[];\n"; @@ -1457,7 +1547,7 @@ void SubtargetEmitter::run(raw_ostream &OS) { << Target << "WriteLatencyTable, " << Target << "ReadAdvanceTable, "; OS << '\n'; OS.indent(22); - if (SchedModels.hasItineraryClasses()) { + if (SchedModels.hasItineraries()) { OS << Target << "Stages, " << Target << "OperandCycles, " << Target << "ForwardingPaths, "; diff --git a/utils/TableGen/TableGen.cpp b/utils/TableGen/TableGen.cpp index 510f2548cd..b5c3ca760d 100644 --- a/utils/TableGen/TableGen.cpp +++ b/utils/TableGen/TableGen.cpp @@ -40,7 +40,8 @@ enum ActionType { GenTgtIntrinsic, PrintEnums, PrintSets, - GenOptParserDefs + GenOptParserDefs, + GenCTags }; namespace { @@ -82,6 +83,8 @@ namespace { "Print expanded sets for testing DAG exprs"), clEnumValN(GenOptParserDefs, "gen-opt-parser-defs", "Generate option definitions"), + clEnumValN(GenCTags, "gen-ctags", + "Generate ctags-compatible index"), clEnumValEnd)); cl::opt<std::string> @@ -161,6 +164,9 @@ bool LLVMTableGenMain(raw_ostream &OS, RecordKeeper &Records) { } break; } + case GenCTags: + EmitCTags(Records, OS); + break; } return false; diff --git a/utils/TableGen/TableGenBackends.h b/utils/TableGen/TableGenBackends.h index cc99092658..28b626e17e 100644 --- a/utils/TableGen/TableGenBackends.h +++ b/utils/TableGen/TableGenBackends.h @@ -75,5 +75,6 @@ void EmitRegisterInfo(RecordKeeper &RK, raw_ostream &OS); void EmitSubtarget(RecordKeeper &RK, raw_ostream &OS); void EmitMapTable(RecordKeeper &RK, raw_ostream &OS); void EmitOptParser(RecordKeeper &RK, raw_ostream &OS); +void EmitCTags(RecordKeeper &RK, raw_ostream &OS); } // End llvm namespace diff --git a/utils/TableGen/X86RecognizableInstr.cpp b/utils/TableGen/X86RecognizableInstr.cpp index b00f7ea2ba..46f2052b01 100644 --- a/utils/TableGen/X86RecognizableInstr.cpp +++ b/utils/TableGen/X86RecognizableInstr.cpp @@ -29,22 +29,25 @@ using namespace llvm; MAP(C4, 36) \ MAP(C8, 37) \ MAP(C9, 38) \ - MAP(E8, 39) \ - MAP(F0, 40) \ - MAP(F8, 41) \ - MAP(F9, 42) \ - MAP(D0, 45) \ - MAP(D1, 46) \ - MAP(D4, 47) \ - MAP(D5, 48) \ - MAP(D8, 49) \ - MAP(D9, 50) \ - MAP(DA, 51) \ - MAP(DB, 52) \ - MAP(DC, 53) \ - MAP(DD, 54) \ - MAP(DE, 55) \ - MAP(DF, 56) + MAP(CA, 39) \ + MAP(CB, 40) \ + MAP(E8, 41) \ + MAP(F0, 42) \ + MAP(F8, 45) \ + MAP(F9, 46) \ + MAP(D0, 47) \ + MAP(D1, 48) \ + MAP(D4, 49) \ + MAP(D5, 50) \ + MAP(D6, 51) \ + MAP(D8, 52) \ + MAP(D9, 53) \ + MAP(DA, 54) \ + MAP(DB, 55) \ + MAP(DC, 56) \ + MAP(DD, 57) \ + MAP(DE, 58) \ + MAP(DF, 59) // A clone of X86 since we can't depend on something that is generated. namespace X86Local { @@ -764,6 +767,17 @@ void RecognizableInstr::emitInstructionSpecifier(DisassemblerTables &tables) { HANDLE_OPERAND(immediate) HANDLE_OPERAND(immediate) break; + case X86Local::MRM_F8: + if (Opcode == 0xc6) { + assert(numPhysicalOperands == 1 && + "Unexpected number of operands for X86Local::MRM_F8"); + HANDLE_OPERAND(immediate) + } else if (Opcode == 0xc7) { + assert(numPhysicalOperands == 1 && + "Unexpected number of operands for X86Local::MRM_F8"); + HANDLE_OPERAND(relocation) + } + break; case X86Local::MRMInitReg: // Ignored. break; diff --git a/utils/TableGen/tdtags b/utils/TableGen/tdtags new file mode 100644 index 0000000000..5214485f4e --- /dev/null +++ b/utils/TableGen/tdtags @@ -0,0 +1,453 @@ +#!/bin/sh +#===-- tdtags - TableGen tags wrapper ---------------------------*- sh -*-===# +# vim:set sts=2 sw=2 et: +#===----------------------------------------------------------------------===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===----------------------------------------------------------------------===# +# +# This is a wrapper script to simplify generating ctags(1)-compatible index +# files for target .td files. Run tdtags -H for more documentation. +# +# For portability, this script is intended to conform to IEEE Std 1003.1-2008. +# +#===----------------------------------------------------------------------===# + +SELF=${0##*/} + +usage() { +cat <<END +Usage: $SELF [ <options> ] tdfile + or: $SELF [ <options> ] -x recipe [arg ...] +OPTIONS + -H Display further help. + -a Append the tags to an existing tags file. + -f <file> Write tags to the specified file (defaults to 'tags'). + -I <dir> Add the directory to the search path for tblgen include files. + -x <recipe> Generate tags file(s) for a common use case: + -q Suppress $TBLGEN error messages. + -v Be verbose; report progress. +END + usage_recipes +} + +usage_recipes() { +cat <<END + all - Generate an index in each directory that contains .td files + in the LLVM source tree. + here - Generate an index for all .td files in the current directory. + recurse - Generate an index in each directory that contains .td files + in and under the current directory. + target [<target> ...] + - Generate a tags file for each specified LLVM code generator + target, or if none are specified, all targets. +END +} + +help() { +cat <<END +NAME + $SELF - generate ctags(1)-compatible index files for tblgen .td source + +SYNOPSIS + $SELF [ options ] -x recipe [arg ...] + $SELF [ options ] [file ...] + +DESCRIPTION + With the '-x' option, $SELF produces one or more tags files for a + particular common use case. See the RECIPES section below for details. + + Without the '-x' option, $SELF provides a ctags(1)-like interface to + $TBLGEN. + +OPTIONS + -a Append newly generated tags to those already in an existing + tags file. Without ths option, any and all existing tags are + replaced. NOTE: When building a mixed tags file, using ${SELF} + for tblgen tags and ctags(1) for other languages, it is best + to run ${SELF} first without '-a', and ctags(1) second with '-a', + because ctags(1) handling is more capable. + -f <file> Use the name <file> for the tags file, rather than the default + "tags". If the <file> is "-", then the tag index is written to + standard output. + -H Display this document. + -I <dir> Add the directory <dir> to the search path for 'include' + statements in tblgen source. + -x Run a canned recipe, rather than operate on specified files. + When '-x' is present, the first non-option argument is the + name of a recipe, and any further arguments are arguments to + that recipe. With no arguments, lists the available recipes. + -q Suppress $TBLGEN error messages. Not all .td files are well- + formed outside a specific context, so recipes will sometimes + produce error messages for certain .td files. These errors + do not affect the indices produced for valid files. + -v Be verbose; report progress. + +RECIPES + $SELF -x all + Produce a tags file in every directory in the LLVM source tree + that contains any .td files. + $SELF -x here + Produce a tags file from .td files in the current directory. + $SELF -x recurse + Produce a tags file in every directory that contains any .td + files, in and under the current directory. + $SELF -x target [<target> ...] + Produce a tags file for each named code generator target, or + if none are named, for all code generator targets. +END +} + +# Temporary file management. +# +# Since SUS sh(1) has no arrays, this script makes extensive use of +# temporary files. The follow are 'global' and used to carry information +# across functions: +# $TMP:D Include directories. +# $TMP:I Included files. +# $TMP:T Top-level files, that are not included by another. +# $TMP:W Directories in which to generate tags (Worklist). +# For portability to OS X, names must not differ only in case. +# +TMP=${TMPDIR:-/tmp}/$SELF:$$ +trap "rm -f $TMP*" 0 +trap exit 1 2 13 15 +>$TMP:D + +td_dump() +{ + if [ $OPT_VERBOSE -gt 1 ] + then + printf '===== %s =====\n' "$1" + cat <"$1" + fi +} + +# Escape the arguments, taken as a whole. +e() { + printf '%s' "$*" | + sed -e "s/'/'\\\\''/g" -e "1s/^/'/" -e "\$s/\$/'/" +} + +# Determine whether the given directory contains at least one .td file. +dir_has_td() { + for i in $1/*.td + do + [ -f "$i" ] && return 0 + done + return 1 +} + +# Partition the supplied list of files, plus any files included from them, +# into two groups: +# $TMP:T Top-level files, that are not included by another. +# $TMP:I Included files. +# Add standard directories to the include paths in $TMP:D if this would +# benefit the any of the included files. +td_prep() { + >$TMP:E + >$TMP:J + for i in *.td + do + [ "x$i" = 'x*.td' ] && return 1 + if [ -f "$i" ] + then + printf '%s\n' "$i" >>$TMP:E + sed -n -e 's/include[[:space:]]"\(.*\)".*/\1/p' <"$i" >>$TMP:J + else + printf >&2 '%s: "%s" not found.\n' "$SELF" "$i" + exit 7 + fi + done + sort -u <$TMP:E >$TMP:X + sort -u <$TMP:J >$TMP:I + # A file that exists but is not included is toplevel. + comm -23 $TMP:X $TMP:I >$TMP:T + td_dump $TMP:T + td_dump $TMP:I + # Check include files. + while read i + do + [ -f "$i" ] && continue + while read d + do + [ -f "$d/$i" ] && break + done <$TMP:D + if [ -z "$d" ] + then + # See whether this include file can be found in a common location. + for d in $LLVM_SRC_ROOT/include \ + $LLVM_SRC_ROOT/tools/clang/include + do + if [ -f "$d/$i" ] + then + printf '%s\n' "$d" >>$TMP:D + break + fi + done + fi + done <$TMP:I + td_dump $TMP:D +} + +# Generate tags for the list of files in $TMP:T. +td_tag() { + # Collect include directories. + inc= + while read d + do + inc="${inc}${inc:+ }$(e "-I=$d")" + done <$TMP:D + + if [ $OPT_VERBOSE -ne 0 ] + then + printf >&2 'In "%s",\n' "$PWD" + fi + + # Generate tags for each file. + n=0 + while read i + do + if [ $OPT_VERBOSE -ne 0 ] + then + printf >&2 ' generating tags from "%s"\n' "$i" + fi + n=$((n + 1)) + t=$(printf '%s:A:%05u' "$TMP" $n) + eval $TBLGEN --gen-ctags $inc "$i" >$t 2>$TMP:F + [ $OPT_NOTBLGENERR -eq 1 ] || cat $TMP:F + done <$TMP:T + + # Add existing tags if requested. + if [ $OPT_APPEND -eq 1 -a -f "$OPT_TAGSFILE" ] + then + if [ $OPT_VERBOSE -ne 0 ] + then + printf >&2 ' and existing tags from "%s"\n' "$OPT_TAGSFILE" + fi + n=$((n + 1)) + t=$(printf '%s:A:%05u' "$TMP" $n) + sed -e '/^!_TAG_/d' <"$OPT_TAGSFILE" | sort -u >$t + fi + + # Merge tags. + if [ $n = 1 ] + then + mv -f "$t" $TMP:M + else + sort -m -u $TMP:A:* >$TMP:M + fi + + # Emit tags. + if [ x${OPT_TAGSFILE}x = x-x ] + then + cat $TMP:M + else + if [ $OPT_VERBOSE -ne 0 ] + then + printf >&2 ' into "%s".\n' "$OPT_TAGSFILE" + fi + mv -f $TMP:M "$OPT_TAGSFILE" + fi +} + +# Generate tags for the current directory. +td_here() { + td_prep + [ -s $TMP:T ] || return 1 + td_tag +} + +# Generate tags for the current directory, and report an error if there are +# no .td files present. +do_here() +{ + if ! td_here + then + printf >&2 '%s: Nothing to do here.\n' "$SELF" + exit 1 + fi +} + +# Generate tags for all .td files under the current directory. +do_recurse() +{ + td_find "$PWD" + td_dirs +} + +# Generate tags for all .td files in LLVM. +do_all() +{ + td_find "$LLVM_SRC_ROOT" + td_dirs +} + +# Generate tags for each directory in the worklist $TMP:W. +td_dirs() +{ + while read d + do + (cd "$d" && td_here) + done <$TMP:W +} + +# Find directories containing .td files within the specified directory, +# and record them in the worklist $TMP:W. +td_find() +{ + find -L "$1" -type f -name '*.td' | + sed -e 's:/[^/]*$::' | + sort -u >$TMP:W + td_dump $TMP:W +} + +# Generate tags for the specified code generator targets, or +# if there are no arguments, all targets. +do_targets() { + cd $LLVM_SRC_ROOT/lib/Target + if [ -z "$*" ] + then + td_find "$PWD" + else + # Check that every specified argument is a target directory; + # if not, list all target directories. + for d + do + if [ -d "$d" ] && dir_has_td "$d" + then + printf '%s/%s\n' "$PWD" "$d" + else + printf >&2 '%s: "%s" is not a target. Targets are:\n' "$SELF" "$d" + for d in * + do + [ -d "$d" ] || continue + dir_has_td "$d" && printf >&2 ' %s\n' "$d" + done + exit 2 + fi + done >$TMP:W + fi + td_dirs +} + +# Change to the directory at the top of the enclosing LLVM source tree, +# if possible. +llvm_src_root() { + while [ "$PWD" != / ] + do + # Use this directory if multiple notable subdirectories are present. + [ -d include/llvm -a -d lib/Target ] && return 0 + cd .. + done + return 1 +} + +# Ensure sort(1) behaves consistently. +LC_ALL=C +export LC_ALL + +# Globals. +TBLGEN=llvm-tblgen +LLVM_SRC_ROOT= + +# Command options. +OPT_TAGSFILE=tags +OPT_RECIPES=0 +OPT_APPEND=0 +OPT_VERBOSE=0 +OPT_NOTBLGENERR=0 + +while getopts 'af:hxqvHI:' opt +do + case $opt in + a) + OPT_APPEND=1 + ;; + f) + OPT_TAGSFILE="$OPTARG" + ;; + x) + OPT_RECIPES=1 + ;; + q) + OPT_NOTBLGENERR=1 + ;; + v) + OPT_VERBOSE=$((OPT_VERBOSE + 1)) + ;; + I) + printf '%s\n' "$OPTARG" >>$TMP:D + ;; + [hH]) + help + exit 0 + ;; + *) + usage >&2 + exit 4 + ;; + esac +done +shift $((OPTIND - 1)) + +# Handle the case where tdtags is a simple ctags(1)-like wrapper for tblgen. +if [ $OPT_RECIPES -eq 0 ] +then + if [ -z "$*" ] + then + help >&2 + exit 5 + fi + for i + do + printf '%s\n' "$i" + done >$TMP:T + td_tag + exit $? +fi + +# Find the directory at the top of the enclosing LLVM source tree. +if ! LLVM_SRC_ROOT=$(llvm_src_root && pwd) +then + printf >&2 '%s: Run from within the LLVM source tree.\n' "$SELF" + exit 3 +fi + +# Select canned actions. +RECIPE="$1" +case "$RECIPE" in +all) + shift + do_all + ;; +.|cwd|here) + shift + do_here + ;; +recurse) + shift + do_recurse + ;; +target) + shift + do_targets "$@" + ;; +*) + if [ -n "$RECIPE" ] + then + shift + printf >&2 '%s: Unknown recipe "-x %s". ' "$SELF" "$RECIPE" + fi + printf >&2 'Recipes:\n' + usage_recipes >&2 + printf >&2 'Run "%s -H" for help.\n' "$SELF" + exit 6 + ;; +esac + +exit $? diff --git a/utils/git-svn/git-svnrevert b/utils/git-svn/git-svnrevert new file mode 100755 index 0000000000..06a9c44091 --- /dev/null +++ b/utils/git-svn/git-svnrevert @@ -0,0 +1,52 @@ +#!/bin/bash + +if [ $# -ne 1 ]; then + echo "Invalid arguments!" + echo "$0 <commit to revert>" + exit 1 +fi + +if [ -n "$(git status -uno -s --porcelain)" ]; then + echo "You have unstashed changes. Please stash and then revert." + git status -uno + exit 1 +fi + +COMMIT=$1 + +SVN_REVISION=$(git svn find-rev "$COMMIT") +if [ $? -ne 0 ]; then + echo "Error! Could not find an svn revision for commit $COMMIT!" + exit 1 +fi + +# Grab the one line message for our revert commit message. +ONE_LINE_MSG=$(git log --oneline $COMMIT -1 | cut -f2- -d " ") + +# Revert the commit. +git revert --no-commit $COMMIT 2>/dev/null +if [ $? -ne 0 ]; then + echo "Error! Failed to revert commit $COMMIT. Resetting to head." + git reset --hard HEAD + exit 1 +fi + +# Create a template in our .git directory. +TEMPLATE="`git rev-parse --git-dir`/git-svn-revert-template" +cat > $TEMPLATE <<EOF +Revert "$ONE_LINE_MSG" + +This reverts commit r$SVN_REVISION. +EOF + +# Begin the commit but give our user an opportunity to edit it. +git commit --file="$TEMPLATE" --edit +if [ $? -ne 0 ]; then + echo "Error! Failed to commit reverting commit for commit $COMMIT. Reverting to head." + git reset --hard HEAD + rm -rf $TEMPLATE + exit 1 +fi + +rm -rf $TEMPLATE + diff --git a/utils/git-svn/git-svnup b/utils/git-svn/git-svnup new file mode 100755 index 0000000000..3321f6ba08 --- /dev/null +++ b/utils/git-svn/git-svnup @@ -0,0 +1,15 @@ +#!/bin/bash + +if [ -n "`git status -uno -s --porcelain`" ]; then + echo "You have unstashed changes. Can not update repository..." + git status -uno + exit 1 +fi + +git fetch +OLD_BRANCH=$(git rev-parse --abbrev-ref HEAD) +git checkout master 2> /dev/null +git svn rebase -l +git checkout $OLD_BRANCH 2> /dev/null + +exit 0 diff --git a/utils/lit/lit/TestFormats.py b/utils/lit/lit/TestFormats.py index a272976ca0..26541f183b 100644 --- a/utils/lit/lit/TestFormats.py +++ b/utils/lit/lit/TestFormats.py @@ -97,6 +97,9 @@ class GoogleTest(object): if litConfig.useValgrind: cmd = litConfig.valgrindArgs + cmd + if litConfig.noExecute: + return Test.PASS, '' + out, err, exitCode = TestRunner.executeCommand( cmd, env=test.config.environment) diff --git a/utils/lit/lit/TestRunner.py b/utils/lit/lit/TestRunner.py index 07fb43f840..84176996a8 100644 --- a/utils/lit/lit/TestRunner.py +++ b/utils/lit/lit/TestRunner.py @@ -277,7 +277,10 @@ def executeScript(test, litConfig, tmpBase, commands, cwd): script += '.bat' # Write script file - f = open(script,'w') + mode = 'w' + if litConfig.isWindows and not isWin32CMDEXE: + mode += 'b' # Avoid CRLFs when writing bash scripts. + f = open(script, mode) if isWin32CMDEXE: f.write('\nif %ERRORLEVEL% NEQ 0 EXIT\n'.join(commands)) else: diff --git a/utils/lit/lit/discovery.py b/utils/lit/lit/discovery.py index c869a671ef..64a9510f95 100644 --- a/utils/lit/lit/discovery.py +++ b/utils/lit/lit/discovery.py @@ -137,7 +137,7 @@ def getTestsInSuite(ts, path_in_suite, litConfig, # Search subdirectories. for filename in os.listdir(source_path): # FIXME: This doesn't belong here? - if filename in ('Output', '.svn') or filename in lc.excludes: + if filename in ('Output', '.svn', '.git') or filename in lc.excludes: continue # Ignore non-directories. @@ -147,20 +147,31 @@ def getTestsInSuite(ts, path_in_suite, litConfig, # Check for nested test suites, first in the execpath in case there is a # site configuration and then in the source path. - file_execpath = ts.getExecPath(path_in_suite + (filename,)) + subpath = path_in_suite + (filename,) + file_execpath = ts.getExecPath(subpath) if dirContainsTestSuite(file_execpath, litConfig): - sub_ts, subiter = getTests(file_execpath, litConfig, - testSuiteCache, localConfigCache) + sub_ts, subpath_in_suite = getTestSuite(file_execpath, litConfig, + testSuiteCache) elif dirContainsTestSuite(file_sourcepath, litConfig): - sub_ts, subiter = getTests(file_sourcepath, litConfig, - testSuiteCache, localConfigCache) + sub_ts, subpath_in_suite = getTestSuite(file_sourcepath, litConfig, + testSuiteCache) else: - # Otherwise, continue loading from inside this test suite. - subiter = getTestsInSuite(ts, path_in_suite + (filename,), - litConfig, testSuiteCache, - localConfigCache) sub_ts = None + # If the this directory recursively maps back to the current test suite, + # disregard it (this can happen if the exec root is located inside the + # current test suite, for example). + if sub_ts is ts: + continue + + # Otherwise, load from the nested test suite, if present. + if sub_ts is not None: + subiter = getTestsInSuite(sub_ts, subpath_in_suite, litConfig, + testSuiteCache, localConfigCache) + else: + subiter = getTestsInSuite(ts, subpath, litConfig, testSuiteCache, + localConfigCache) + N = 0 for res in subiter: N += 1 diff --git a/utils/lit/lit/main.py b/utils/lit/lit/main.py index da961eeedc..de97a8e1aa 100755 --- a/utils/lit/lit/main.py +++ b/utils/lit/lit/main.py @@ -219,7 +219,7 @@ def main(builtinParameters = {}): group.add_option("", "--shuffle", dest="shuffle", help="Run tests in random order", action="store_true", default=False) - group.add_option("", "--filter", dest="filter", metavar="EXPRESSION", + group.add_option("", "--filter", dest="filter", metavar="REGEX", help=("Only run tests with paths matching the given " "regular expression"), action="store", default=None) diff --git a/utils/lit/tests/Inputs/discovery/lit.cfg b/utils/lit/tests/Inputs/discovery/lit.cfg index 3513bfffd1..4049ab16f9 100644 --- a/utils/lit/tests/Inputs/discovery/lit.cfg +++ b/utils/lit/tests/Inputs/discovery/lit.cfg @@ -1,5 +1,10 @@ config.name = 'top-level-suite' config.suffixes = ['.txt'] config.test_format = lit.formats.ShTest() -config.test_source_root = None -config.test_exec_root = None + +# We intentionally don't set the source root or exec root directories here, +# because this suite gets reused for testing the exec root behavior (in +# ../exec-discovery). +# +#config.test_source_root = None +#config.test_exec_root = None diff --git a/utils/lit/tests/Inputs/exec-discovery-in-tree/lit.cfg b/utils/lit/tests/Inputs/exec-discovery-in-tree/lit.cfg new file mode 100644 index 0000000000..342b2fdd3c --- /dev/null +++ b/utils/lit/tests/Inputs/exec-discovery-in-tree/lit.cfg @@ -0,0 +1,7 @@ +# Verify that the site configuration was loaded. +if config.test_source_root is None or config.test_exec_root is None: + lit.fatal("No site specific configuration") + +config.name = 'exec-discovery-in-tree-suite' +config.suffixes = ['.txt'] +config.test_format = lit.formats.ShTest() diff --git a/utils/lit/tests/Inputs/exec-discovery-in-tree/obj/lit.site.cfg b/utils/lit/tests/Inputs/exec-discovery-in-tree/obj/lit.site.cfg new file mode 100644 index 0000000000..de9a3d0c6d --- /dev/null +++ b/utils/lit/tests/Inputs/exec-discovery-in-tree/obj/lit.site.cfg @@ -0,0 +1,4 @@ +import os +config.test_exec_root = os.path.dirname(__file__) +config.test_source_root = os.path.dirname(config.test_exec_root) +lit.load_config(config, os.path.join(config.test_source_root, "lit.cfg"))
\ No newline at end of file diff --git a/utils/lit/tests/Inputs/exec-discovery-in-tree/test-one.txt b/utils/lit/tests/Inputs/exec-discovery-in-tree/test-one.txt new file mode 100644 index 0000000000..b80b60b7a2 --- /dev/null +++ b/utils/lit/tests/Inputs/exec-discovery-in-tree/test-one.txt @@ -0,0 +1 @@ +# RUN: true diff --git a/utils/lit/tests/Inputs/exec-discovery/lit.site.cfg b/utils/lit/tests/Inputs/exec-discovery/lit.site.cfg new file mode 100644 index 0000000000..796569a301 --- /dev/null +++ b/utils/lit/tests/Inputs/exec-discovery/lit.site.cfg @@ -0,0 +1,5 @@ +# Load the discovery suite, but with a separate exec root. +import os +config.test_exec_root = os.path.dirname(__file__) +config.test_source_root = os.path.join(os.path.dirname(config.test_exec_root), "discovery") +lit.load_config(config, os.path.join(config.test_source_root, "lit.cfg")) diff --git a/utils/lit/tests/Inputs/progress-bar/lit.cfg b/utils/lit/tests/Inputs/progress-bar/lit.cfg new file mode 100644 index 0000000000..4878b65609 --- /dev/null +++ b/utils/lit/tests/Inputs/progress-bar/lit.cfg @@ -0,0 +1,5 @@ +config.name = 'shtest-shell' +config.suffixes = ['.txt'] +config.test_format = lit.formats.ShTest() +config.test_source_root = None +config.test_exec_root = None diff --git a/utils/lit/tests/Inputs/progress-bar/test-1.txt b/utils/lit/tests/Inputs/progress-bar/test-1.txt new file mode 100644 index 0000000000..49932c3006 --- /dev/null +++ b/utils/lit/tests/Inputs/progress-bar/test-1.txt @@ -0,0 +1 @@ +# RUN: false diff --git a/utils/lit/tests/Inputs/progress-bar/test-2.txt b/utils/lit/tests/Inputs/progress-bar/test-2.txt new file mode 100644 index 0000000000..49932c3006 --- /dev/null +++ b/utils/lit/tests/Inputs/progress-bar/test-2.txt @@ -0,0 +1 @@ +# RUN: false diff --git a/utils/lit/tests/Inputs/progress-bar/test-3.txt b/utils/lit/tests/Inputs/progress-bar/test-3.txt new file mode 100644 index 0000000000..49932c3006 --- /dev/null +++ b/utils/lit/tests/Inputs/progress-bar/test-3.txt @@ -0,0 +1 @@ +# RUN: false diff --git a/utils/lit/tests/Inputs/progress-bar/test-4.txt b/utils/lit/tests/Inputs/progress-bar/test-4.txt new file mode 100644 index 0000000000..49932c3006 --- /dev/null +++ b/utils/lit/tests/Inputs/progress-bar/test-4.txt @@ -0,0 +1 @@ +# RUN: false diff --git a/utils/lit/tests/discovery.py b/utils/lit/tests/discovery.py index 54b99d3945..56d9dd07e8 100644 --- a/utils/lit/tests/discovery.py +++ b/utils/lit/tests/discovery.py @@ -5,17 +5,17 @@ # RUN: FileCheck --check-prefix=CHECK-BASIC-OUT < %t.out %s # RUN: FileCheck --check-prefix=CHECK-BASIC-ERR < %t.err %s # -# CHECK-BASIC-ERR: loading suite config '{{.*}}/tests/Inputs/discovery/lit.cfg' -# CHECK-BASIC-ERR: loading local config '{{.*}}/tests/Inputs/discovery/subdir/lit.local.cfg' -# CHECK-BASIC-ERR: loading suite config '{{.*}}/tests/Inputs/discovery/subsuite/lit.cfg' +# CHECK-BASIC-ERR: loading suite config '{{.*}}/discovery/lit.cfg' +# CHECK-BASIC-ERR: loading local config '{{.*}}/discovery/subdir/lit.local.cfg' +# CHECK-BASIC-ERR: loading suite config '{{.*}}/discovery/subsuite/lit.cfg' # # CHECK-BASIC-OUT: -- Test Suites -- # CHECK-BASIC-OUT: sub-suite - 2 tests -# CHECK-BASIC-OUT: Source Root: -# CHECK-BASIC-OUT: Exec Root : +# CHECK-BASIC-OUT: Source Root: {{.*/discovery/subsuite$}} +# CHECK-BASIC-OUT: Exec Root : {{.*/discovery/subsuite$}} # CHECK-BASIC-OUT: top-level-suite - 3 tests -# CHECK-BASIC-OUT: Source Root: -# CHECK-BASIC-OUT: Exec Root : +# CHECK-BASIC-OUT: Source Root: {{.*/discovery$}} +# CHECK-BASIC-OUT: Exec Root : {{.*/discovery$}} # # CHECK-BASIC-OUT: -- Testing: 5 tests, 1 threads -- # CHECK-BASIC-OUT: PASS: sub-suite :: test-one @@ -23,3 +23,74 @@ # CHECK-BASIC-OUT: PASS: top-level-suite :: subdir/test-three # CHECK-BASIC-OUT: PASS: top-level-suite :: test-one # CHECK-BASIC-OUT: PASS: top-level-suite :: test-two + + +# Check discovery when exact test names are given. +# +# RUN: %{lit} \ +# RUN: %{inputs}/discovery/subdir/test-three.py \ +# RUN: %{inputs}/discovery/subsuite/test-one.txt \ +# RUN: -j 1 --no-execute --show-suites -v > %t.out +# RUN: FileCheck --check-prefix=CHECK-EXACT-TEST < %t.out %s +# +# CHECK-EXACT-TEST: -- Testing: 2 tests, 1 threads -- +# CHECK-EXACT-TEST: PASS: sub-suite :: test-one +# CHECK-EXACT-TEST: PASS: top-level-suite :: subdir/test-three + + +# Check discovery when using an exec path. +# +# RUN: %{lit} %{inputs}/exec-discovery \ +# RUN: -j 1 --debug --no-execute --show-suites -v > %t.out 2> %t.err +# RUN: FileCheck --check-prefix=CHECK-ASEXEC-OUT < %t.out %s +# RUN: FileCheck --check-prefix=CHECK-ASEXEC-ERR < %t.err %s +# +# CHECK-ASEXEC-ERR: loading suite config '{{.*}}/exec-discovery/lit.site.cfg' +# CHECK-ASEXEC-ERR: load_config from '{{.*}}/discovery/lit.cfg' +# CHECK-ASEXEC-ERR: loaded config '{{.*}}/discovery/lit.cfg' +# CHECK-ASEXEC-ERR: loaded config '{{.*}}/exec-discovery/lit.site.cfg' +# CHECK-ASEXEC-ERR: loading local config '{{.*}}/discovery/subdir/lit.local.cfg' +# CHECK-ASEXEC-ERR: loading suite config '{{.*}}/discovery/subsuite/lit.cfg' +# +# CHECK-ASEXEC-OUT: -- Test Suites -- +# CHECK-ASEXEC-OUT: sub-suite - 2 tests +# CHECK-ASEXEC-OUT: Source Root: {{.*/discovery/subsuite$}} +# CHECK-ASEXEC-OUT: Exec Root : {{.*/discovery/subsuite$}} +# CHECK-ASEXEC-OUT: top-level-suite - 3 tests +# CHECK-ASEXEC-OUT: Source Root: {{.*/discovery$}} +# CHECK-ASEXEC-OUT: Exec Root : {{.*/exec-discovery$}} +# +# CHECK-ASEXEC-OUT: -- Testing: 5 tests, 1 threads -- +# CHECK-ASEXEC-OUT: PASS: sub-suite :: test-one +# CHECK-ASEXEC-OUT: PASS: sub-suite :: test-two +# CHECK-ASEXEC-OUT: PASS: top-level-suite :: subdir/test-three +# CHECK-ASEXEC-OUT: PASS: top-level-suite :: test-one +# CHECK-ASEXEC-OUT: PASS: top-level-suite :: test-two + + +# Check discovery when exact test names are given. +# +# FIXME: Note that using a path into a subsuite doesn't work correctly here. +# +# RUN: %{lit} \ +# RUN: %{inputs}/exec-discovery/subdir/test-three.py \ +# RUN: -j 1 --no-execute --show-suites -v > %t.out +# RUN: FileCheck --check-prefix=CHECK-ASEXEC-EXACT-TEST < %t.out %s +# +# CHECK-ASEXEC-EXACT-TEST: -- Testing: 1 tests, 1 threads -- +# CHECK-ASEXEC-EXACT-TEST: PASS: top-level-suite :: subdir/test-three + + +# Check that we don't recurse infinitely when loading an site specific test +# suite located inside the test source root. +# +# RUN: %{lit} \ +# RUN: %{inputs}/exec-discovery-in-tree/obj/ \ +# RUN: -j 1 --no-execute --show-suites -v > %t.out +# RUN: FileCheck --check-prefix=CHECK-ASEXEC-INTREE < %t.out %s +# +# CHECK-ASEXEC-INTREE: exec-discovery-in-tree-suite - 1 tests +# CHECK-ASEXEC-INTREE-NEXT: Source Root: {{.*/exec-discovery-in-tree$}} +# CHECK-ASEXEC-INTREE-NEXT: Exec Root : {{.*/exec-discovery-in-tree/obj$}} +# CHECK-ASEXEC-INTREE-NEXT: -- Testing: 1 tests, 1 threads -- +# CHECK-ASEXEC-INTREE-NEXT: PASS: exec-discovery-in-tree-suite :: test-one diff --git a/utils/lit/tests/progress-bar.py b/utils/lit/tests/progress-bar.py new file mode 100644 index 0000000000..d046748508 --- /dev/null +++ b/utils/lit/tests/progress-bar.py @@ -0,0 +1,13 @@ +# Check the simple progress bar. +# +# RUN: not %{lit} -j 1 -s %{inputs}/progress-bar > %t.out +# RUN: FileCheck < %t.out %s +# +# CHECK: Testing: 0 .. 10.. 20 +# CHECK: FAIL: shtest-shell :: test-1.txt (1 of 4) +# CHECK: Testing: 0 .. 10.. 20.. 30.. 40.. +# CHECK: FAIL: shtest-shell :: test-2.txt (2 of 4) +# CHECK: Testing: 0 .. 10.. 20.. 30.. 40.. 50.. 60.. 70 +# CHECK: FAIL: shtest-shell :: test-3.txt (3 of 4) +# CHECK: Testing: 0 .. 10.. 20.. 30.. 40.. 50.. 60.. 70.. 80.. 90.. +# CHECK: FAIL: shtest-shell :: test-4.txt (4 of 4) diff --git a/utils/obj2yaml/CMakeLists.txt b/utils/obj2yaml/CMakeLists.txt deleted file mode 100644 index d64bf1bad8..0000000000 --- a/utils/obj2yaml/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -set(LLVM_LINK_COMPONENTS archive object) - -add_llvm_utility(obj2yaml - obj2yaml.cpp coff2yaml.cpp - ) - -target_link_libraries(obj2yaml LLVMSupport) diff --git a/utils/obj2yaml/Makefile b/utils/obj2yaml/Makefile deleted file mode 100644 index 5b96bdd5b9..0000000000 --- a/utils/obj2yaml/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -##===- utils/obj2yaml/Makefile ----------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../.. -TOOLNAME = obj2yaml -USEDLIBS = LLVMObject.a LLVMSupport.a - -# This tool has no plugins, optimize startup time. -TOOL_NO_EXPORTS = 1 - -# Don't install this utility -NO_INSTALL = 1 - -include $(LEVEL)/Makefile.common diff --git a/utils/obj2yaml/coff2yaml.cpp b/utils/obj2yaml/coff2yaml.cpp deleted file mode 100644 index f0241d931e..0000000000 --- a/utils/obj2yaml/coff2yaml.cpp +++ /dev/null @@ -1,361 +0,0 @@ -//===------ utils/obj2yaml.cpp - obj2yaml conversion tool -------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "obj2yaml.h" -#include "llvm/Object/COFF.h" - - -template <typename One, typename Two> -struct pod_pair { // I'd much rather use std::pair, but it's not a POD - One first; - Two second; -}; - -#define STRING_PAIR(x) {llvm::COFF::x, #x} -static const pod_pair<llvm::COFF::MachineTypes, const char *> -MachineTypePairs [] = { - STRING_PAIR(IMAGE_FILE_MACHINE_UNKNOWN), - STRING_PAIR(IMAGE_FILE_MACHINE_AM33), - STRING_PAIR(IMAGE_FILE_MACHINE_AMD64), - STRING_PAIR(IMAGE_FILE_MACHINE_ARM), - STRING_PAIR(IMAGE_FILE_MACHINE_ARMV7), - STRING_PAIR(IMAGE_FILE_MACHINE_EBC), - STRING_PAIR(IMAGE_FILE_MACHINE_I386), - STRING_PAIR(IMAGE_FILE_MACHINE_IA64), - STRING_PAIR(IMAGE_FILE_MACHINE_M32R), - STRING_PAIR(IMAGE_FILE_MACHINE_MIPS16), - STRING_PAIR(IMAGE_FILE_MACHINE_MIPSFPU), - STRING_PAIR(IMAGE_FILE_MACHINE_MIPSFPU16), - STRING_PAIR(IMAGE_FILE_MACHINE_POWERPC), - STRING_PAIR(IMAGE_FILE_MACHINE_POWERPCFP), - STRING_PAIR(IMAGE_FILE_MACHINE_R4000), - STRING_PAIR(IMAGE_FILE_MACHINE_SH3), - STRING_PAIR(IMAGE_FILE_MACHINE_SH3DSP), - STRING_PAIR(IMAGE_FILE_MACHINE_SH4), - STRING_PAIR(IMAGE_FILE_MACHINE_SH5), - STRING_PAIR(IMAGE_FILE_MACHINE_THUMB), - STRING_PAIR(IMAGE_FILE_MACHINE_WCEMIPSV2) -}; - -static const pod_pair<llvm::COFF::SectionCharacteristics, const char *> -SectionCharacteristicsPairs1 [] = { - STRING_PAIR(IMAGE_SCN_TYPE_NO_PAD), - STRING_PAIR(IMAGE_SCN_CNT_CODE), - STRING_PAIR(IMAGE_SCN_CNT_INITIALIZED_DATA), - STRING_PAIR(IMAGE_SCN_CNT_UNINITIALIZED_DATA), - STRING_PAIR(IMAGE_SCN_LNK_OTHER), - STRING_PAIR(IMAGE_SCN_LNK_INFO), - STRING_PAIR(IMAGE_SCN_LNK_REMOVE), - STRING_PAIR(IMAGE_SCN_LNK_COMDAT), - STRING_PAIR(IMAGE_SCN_GPREL), - STRING_PAIR(IMAGE_SCN_MEM_PURGEABLE), - STRING_PAIR(IMAGE_SCN_MEM_16BIT), - STRING_PAIR(IMAGE_SCN_MEM_LOCKED), - STRING_PAIR(IMAGE_SCN_MEM_PRELOAD) -}; - -static const pod_pair<llvm::COFF::SectionCharacteristics, const char *> -SectionCharacteristicsPairsAlignment [] = { - STRING_PAIR(IMAGE_SCN_ALIGN_1BYTES), - STRING_PAIR(IMAGE_SCN_ALIGN_2BYTES), - STRING_PAIR(IMAGE_SCN_ALIGN_4BYTES), - STRING_PAIR(IMAGE_SCN_ALIGN_8BYTES), - STRING_PAIR(IMAGE_SCN_ALIGN_16BYTES), - STRING_PAIR(IMAGE_SCN_ALIGN_32BYTES), - STRING_PAIR(IMAGE_SCN_ALIGN_64BYTES), - STRING_PAIR(IMAGE_SCN_ALIGN_128BYTES), - STRING_PAIR(IMAGE_SCN_ALIGN_256BYTES), - STRING_PAIR(IMAGE_SCN_ALIGN_512BYTES), - STRING_PAIR(IMAGE_SCN_ALIGN_1024BYTES), - STRING_PAIR(IMAGE_SCN_ALIGN_2048BYTES), - STRING_PAIR(IMAGE_SCN_ALIGN_4096BYTES), - STRING_PAIR(IMAGE_SCN_ALIGN_8192BYTES) -}; - -static const pod_pair<llvm::COFF::SectionCharacteristics, const char *> -SectionCharacteristicsPairs2 [] = { - STRING_PAIR(IMAGE_SCN_LNK_NRELOC_OVFL), - STRING_PAIR(IMAGE_SCN_MEM_DISCARDABLE), - STRING_PAIR(IMAGE_SCN_MEM_NOT_CACHED), - STRING_PAIR(IMAGE_SCN_MEM_NOT_PAGED), - STRING_PAIR(IMAGE_SCN_MEM_SHARED), - STRING_PAIR(IMAGE_SCN_MEM_EXECUTE), - STRING_PAIR(IMAGE_SCN_MEM_READ), - STRING_PAIR(IMAGE_SCN_MEM_WRITE) -}; - -static const pod_pair<llvm::COFF::SymbolBaseType, const char *> -SymbolBaseTypePairs [] = { - STRING_PAIR(IMAGE_SYM_TYPE_NULL), - STRING_PAIR(IMAGE_SYM_TYPE_VOID), - STRING_PAIR(IMAGE_SYM_TYPE_CHAR), - STRING_PAIR(IMAGE_SYM_TYPE_SHORT), - STRING_PAIR(IMAGE_SYM_TYPE_INT), - STRING_PAIR(IMAGE_SYM_TYPE_LONG), - STRING_PAIR(IMAGE_SYM_TYPE_FLOAT), - STRING_PAIR(IMAGE_SYM_TYPE_DOUBLE), - STRING_PAIR(IMAGE_SYM_TYPE_STRUCT), - STRING_PAIR(IMAGE_SYM_TYPE_UNION), - STRING_PAIR(IMAGE_SYM_TYPE_ENUM), - STRING_PAIR(IMAGE_SYM_TYPE_MOE), - STRING_PAIR(IMAGE_SYM_TYPE_BYTE), - STRING_PAIR(IMAGE_SYM_TYPE_WORD), - STRING_PAIR(IMAGE_SYM_TYPE_UINT), - STRING_PAIR(IMAGE_SYM_TYPE_DWORD) -}; - -static const pod_pair<llvm::COFF::SymbolComplexType, const char *> -SymbolComplexTypePairs [] = { - STRING_PAIR(IMAGE_SYM_DTYPE_NULL), - STRING_PAIR(IMAGE_SYM_DTYPE_POINTER), - STRING_PAIR(IMAGE_SYM_DTYPE_FUNCTION), - STRING_PAIR(IMAGE_SYM_DTYPE_ARRAY), -}; - -static const pod_pair<llvm::COFF::SymbolStorageClass, const char *> -SymbolStorageClassPairs [] = { - STRING_PAIR(IMAGE_SYM_CLASS_END_OF_FUNCTION), - STRING_PAIR(IMAGE_SYM_CLASS_NULL), - STRING_PAIR(IMAGE_SYM_CLASS_AUTOMATIC), - STRING_PAIR(IMAGE_SYM_CLASS_EXTERNAL), - STRING_PAIR(IMAGE_SYM_CLASS_STATIC), - STRING_PAIR(IMAGE_SYM_CLASS_REGISTER), - STRING_PAIR(IMAGE_SYM_CLASS_EXTERNAL_DEF), - STRING_PAIR(IMAGE_SYM_CLASS_LABEL), - STRING_PAIR(IMAGE_SYM_CLASS_UNDEFINED_LABEL), - STRING_PAIR(IMAGE_SYM_CLASS_MEMBER_OF_STRUCT), - STRING_PAIR(IMAGE_SYM_CLASS_ARGUMENT), - STRING_PAIR(IMAGE_SYM_CLASS_STRUCT_TAG), - STRING_PAIR(IMAGE_SYM_CLASS_MEMBER_OF_UNION), - STRING_PAIR(IMAGE_SYM_CLASS_UNION_TAG), - STRING_PAIR(IMAGE_SYM_CLASS_TYPE_DEFINITION), - STRING_PAIR(IMAGE_SYM_CLASS_UNDEFINED_STATIC), - STRING_PAIR(IMAGE_SYM_CLASS_ENUM_TAG), - STRING_PAIR(IMAGE_SYM_CLASS_MEMBER_OF_ENUM), - STRING_PAIR(IMAGE_SYM_CLASS_REGISTER_PARAM), - STRING_PAIR(IMAGE_SYM_CLASS_BIT_FIELD), - STRING_PAIR(IMAGE_SYM_CLASS_BLOCK), - STRING_PAIR(IMAGE_SYM_CLASS_FUNCTION), - STRING_PAIR(IMAGE_SYM_CLASS_END_OF_STRUCT), - STRING_PAIR(IMAGE_SYM_CLASS_FILE), - STRING_PAIR(IMAGE_SYM_CLASS_SECTION), - STRING_PAIR(IMAGE_SYM_CLASS_WEAK_EXTERNAL), - STRING_PAIR(IMAGE_SYM_CLASS_CLR_TOKEN), -}; - -static const pod_pair<llvm::COFF::RelocationTypeX86, const char *> -RelocationTypeX86Pairs [] = { - STRING_PAIR(IMAGE_REL_I386_ABSOLUTE), - STRING_PAIR(IMAGE_REL_I386_DIR16), - STRING_PAIR(IMAGE_REL_I386_REL16), - STRING_PAIR(IMAGE_REL_I386_DIR32), - STRING_PAIR(IMAGE_REL_I386_DIR32NB), - STRING_PAIR(IMAGE_REL_I386_SEG12), - STRING_PAIR(IMAGE_REL_I386_SECTION), - STRING_PAIR(IMAGE_REL_I386_SECREL), - STRING_PAIR(IMAGE_REL_I386_TOKEN), - STRING_PAIR(IMAGE_REL_I386_SECREL7), - STRING_PAIR(IMAGE_REL_I386_REL32), - STRING_PAIR(IMAGE_REL_AMD64_ABSOLUTE), - STRING_PAIR(IMAGE_REL_AMD64_ADDR64), - STRING_PAIR(IMAGE_REL_AMD64_ADDR32), - STRING_PAIR(IMAGE_REL_AMD64_ADDR32NB), - STRING_PAIR(IMAGE_REL_AMD64_REL32), - STRING_PAIR(IMAGE_REL_AMD64_REL32_1), - STRING_PAIR(IMAGE_REL_AMD64_REL32_2), - STRING_PAIR(IMAGE_REL_AMD64_REL32_3), - STRING_PAIR(IMAGE_REL_AMD64_REL32_4), - STRING_PAIR(IMAGE_REL_AMD64_REL32_5), - STRING_PAIR(IMAGE_REL_AMD64_SECTION), - STRING_PAIR(IMAGE_REL_AMD64_SECREL), - STRING_PAIR(IMAGE_REL_AMD64_SECREL7), - STRING_PAIR(IMAGE_REL_AMD64_TOKEN), - STRING_PAIR(IMAGE_REL_AMD64_SREL32), - STRING_PAIR(IMAGE_REL_AMD64_PAIR), - STRING_PAIR(IMAGE_REL_AMD64_SSPAN32) -}; - -static const pod_pair<llvm::COFF::RelocationTypesARM, const char *> -RelocationTypesARMPairs [] = { - STRING_PAIR(IMAGE_REL_ARM_ABSOLUTE), - STRING_PAIR(IMAGE_REL_ARM_ADDR32), - STRING_PAIR(IMAGE_REL_ARM_ADDR32NB), - STRING_PAIR(IMAGE_REL_ARM_BRANCH24), - STRING_PAIR(IMAGE_REL_ARM_BRANCH11), - STRING_PAIR(IMAGE_REL_ARM_TOKEN), - STRING_PAIR(IMAGE_REL_ARM_BLX24), - STRING_PAIR(IMAGE_REL_ARM_BLX11), - STRING_PAIR(IMAGE_REL_ARM_SECTION), - STRING_PAIR(IMAGE_REL_ARM_SECREL), - STRING_PAIR(IMAGE_REL_ARM_MOV32A), - STRING_PAIR(IMAGE_REL_ARM_MOV32T), - STRING_PAIR(IMAGE_REL_ARM_BRANCH20T), - STRING_PAIR(IMAGE_REL_ARM_BRANCH24T), - STRING_PAIR(IMAGE_REL_ARM_BLX23T) -}; -#undef STRING_PAIR - - -static const char endl = '\n'; - -namespace yaml { // COFF-specific yaml-writing specific routines - -static llvm::raw_ostream &writeName(llvm::raw_ostream &Out, - const char *Name, std::size_t NameSize) { - for (std::size_t i = 0; i < NameSize; ++i) { - if (!Name[i]) break; - Out << Name[i]; - } - return Out; -} - -// Given an array of pod_pair<enum, const char *>, write all enums that match -template <typename T, std::size_t N> -static llvm::raw_ostream &writeBitMask(llvm::raw_ostream &Out, - const pod_pair<T, const char *> (&Arr)[N], unsigned long Val) { - for (std::size_t i = 0; i < N; ++i) - if (Val & Arr[i].first) - Out << Arr[i].second << ", "; - return Out; -} - -} // end of yaml namespace - -// Given an array of pod_pair<enum, const char *>, look up a value -template <typename T, std::size_t N> -const char *nameLookup(const pod_pair<T, const char *> (&Arr)[N], - unsigned long Val, const char *NotFound = NULL) { - T n = static_cast<T>(Val); - for (std::size_t i = 0; i < N; ++i) - if (n == Arr[i].first) - return Arr[i].second; - return NotFound; -} - - -static llvm::raw_ostream &yamlCOFFHeader( - const llvm::object::coff_file_header *Header,llvm::raw_ostream &Out) { - - Out << "header: !Header" << endl; - Out << " Machine: "; - Out << nameLookup(MachineTypePairs, Header->Machine, "# Unknown_MachineTypes") - << " # ("; - return yaml::writeHexNumber(Out, Header->Machine) << ")" << endl << endl; -} - - -static llvm::raw_ostream &yamlCOFFSections(llvm::object::COFFObjectFile &Obj, - std::size_t NumSections, llvm::raw_ostream &Out) { - llvm::error_code ec; - Out << "sections:" << endl; - for (llvm::object::section_iterator iter = Obj.begin_sections(); - iter != Obj.end_sections(); iter.increment(ec)) { - const llvm::object::coff_section *sect = Obj.getCOFFSection(iter); - - Out << " - !Section" << endl; - Out << " Name: "; - yaml::writeName(Out, sect->Name, sizeof(sect->Name)) << endl; - - Out << " Characteristics: ["; - yaml::writeBitMask(Out, SectionCharacteristicsPairs1, sect->Characteristics); - Out << nameLookup(SectionCharacteristicsPairsAlignment, - sect->Characteristics & 0x00F00000, "# Unrecognized_IMAGE_SCN_ALIGN") - << ", "; - yaml::writeBitMask(Out, SectionCharacteristicsPairs2, sect->Characteristics); - Out << "] # "; - yaml::writeHexNumber(Out, sect->Characteristics) << endl; - - llvm::ArrayRef<uint8_t> sectionData; - Obj.getSectionContents(sect, sectionData); - Out << " SectionData: "; - yaml::writeHexStream(Out, sectionData) << endl; - if (iter->begin_relocations() != iter->end_relocations()) - Out << " Relocations:\n"; - for (llvm::object::relocation_iterator rIter = iter->begin_relocations(); - rIter != iter->end_relocations(); rIter.increment(ec)) { - const llvm::object::coff_relocation *reloc = Obj.getCOFFRelocation(rIter); - - Out << " - !Relocation" << endl; - Out << " VirtualAddress: " ; - yaml::writeHexNumber(Out, reloc->VirtualAddress) << endl; - Out << " SymbolTableIndex: " << reloc->SymbolTableIndex << endl; - Out << " Type: " - << nameLookup(RelocationTypeX86Pairs, reloc->Type) << endl; - // TODO: Use the correct reloc type for the machine. - Out << endl; - } - - } - return Out; -} - -static llvm::raw_ostream& yamlCOFFSymbols(llvm::object::COFFObjectFile &Obj, - std::size_t NumSymbols, llvm::raw_ostream &Out) { - llvm::error_code ec; - Out << "symbols:" << endl; - for (llvm::object::symbol_iterator iter = Obj.begin_symbols(); - iter != Obj.end_symbols(); iter.increment(ec)) { - // Gather all the info that we need - llvm::StringRef str; - const llvm::object::coff_symbol *symbol = Obj.getCOFFSymbol(iter); - Obj.getSymbolName(symbol, str); - std::size_t simpleType = symbol->getBaseType(); - std::size_t complexType = symbol->getComplexType(); - std::size_t storageClass = symbol->StorageClass; - - Out << " - !Symbol" << endl; - Out << " Name: " << str << endl; - - Out << " Value: " << symbol->Value << endl; - Out << " SectionNumber: " << symbol->SectionNumber << endl; - - Out << " SimpleType: " - << nameLookup(SymbolBaseTypePairs, simpleType, - "# Unknown_SymbolBaseType") - << " # (" << simpleType << ")" << endl; - - Out << " ComplexType: " - << nameLookup(SymbolComplexTypePairs, complexType, - "# Unknown_SymbolComplexType") - << " # (" << complexType << ")" << endl; - - Out << " StorageClass: " - << nameLookup(SymbolStorageClassPairs, storageClass, - "# Unknown_StorageClass") - << " # (" << (int) storageClass << ")" << endl; - - if (symbol->NumberOfAuxSymbols > 0) { - llvm::ArrayRef<uint8_t> aux = Obj.getSymbolAuxData(symbol); - Out << " NumberOfAuxSymbols: " - << (int) symbol->NumberOfAuxSymbols << endl; - Out << " AuxillaryData: "; - yaml::writeHexStream(Out, aux); - } - - Out << endl; - } - - return Out; -} - - -llvm::error_code coff2yaml(llvm::raw_ostream &Out, llvm::MemoryBuffer *TheObj) { - llvm::error_code ec; - llvm::object::COFFObjectFile obj(TheObj, ec); - if (!ec) { - const llvm::object::coff_file_header *hd; - ec = obj.getHeader(hd); - if (!ec) { - yamlCOFFHeader(hd, Out); - yamlCOFFSections(obj, hd->NumberOfSections, Out); - yamlCOFFSymbols(obj, hd->NumberOfSymbols, Out); - } - } - return ec; -} diff --git a/utils/obj2yaml/obj2yaml.cpp b/utils/obj2yaml/obj2yaml.cpp deleted file mode 100644 index bdc461a947..0000000000 --- a/utils/obj2yaml/obj2yaml.cpp +++ /dev/null @@ -1,86 +0,0 @@ -//===------ utils/obj2yaml.cpp - obj2yaml conversion tool -------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "obj2yaml.h" -#include "llvm/ADT/OwningPtr.h" -#include "llvm/Object/Archive.h" -#include "llvm/Object/COFF.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/ManagedStatic.h" -#include "llvm/Support/PrettyStackTrace.h" -#include "llvm/Support/Signals.h" - -const char endl = '\n'; - -namespace yaml { // generic yaml-writing specific routines - -unsigned char printable(unsigned char Ch) { - return Ch >= ' ' && Ch <= '~' ? Ch : '.'; -} - -llvm::raw_ostream &writeHexStream(llvm::raw_ostream &Out, - const llvm::ArrayRef<uint8_t> arr) { - const char *hex = "0123456789ABCDEF"; - Out << " !hex \""; - - typedef llvm::ArrayRef<uint8_t>::const_iterator iter_t; - const iter_t end = arr.end(); - for (iter_t iter = arr.begin(); iter != end; ++iter) - Out << hex[(*iter >> 4) & 0x0F] << hex[(*iter & 0x0F)]; - - Out << "\" # |"; - for (iter_t iter = arr.begin(); iter != end; ++iter) - Out << printable(*iter); - Out << "|" << endl; - - return Out; - } - -llvm::raw_ostream &writeHexNumber(llvm::raw_ostream &Out, unsigned long long N) { - if (N >= 10) - Out << "0x"; - Out.write_hex(N); - return Out; -} - -} - - -using namespace llvm; -enum ObjectFileType { coff }; - -cl::opt<ObjectFileType> InputFormat( - cl::desc("Choose input format"), - cl::values( - clEnumVal(coff, "process COFF object files"), - clEnumValEnd)); - -cl::opt<std::string> InputFilename(cl::Positional, cl::desc("<input file>"), cl::init("-")); - -int main(int argc, char * argv[]) { - cl::ParseCommandLineOptions(argc, argv); - sys::PrintStackTraceOnErrorSignal(); - PrettyStackTraceProgram X(argc, argv); - llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. - -// Process the input file - OwningPtr<MemoryBuffer> buf; - -// TODO: If this is an archive, then burst it and dump each entry - if (error_code ec = MemoryBuffer::getFileOrSTDIN(InputFilename, buf)) - llvm::errs() << "Error: '" << ec.message() << "' opening file '" - << InputFilename << "'" << endl; - else { - ec = coff2yaml(llvm::outs(), buf.take()); - if (ec) - llvm::errs() << "Error: " << ec.message() << " dumping COFF file" << endl; - } - - return 0; -} diff --git a/utils/obj2yaml/obj2yaml.h b/utils/obj2yaml/obj2yaml.h deleted file mode 100644 index 0bc376a6db..0000000000 --- a/utils/obj2yaml/obj2yaml.h +++ /dev/null @@ -1,34 +0,0 @@ -//===------ utils/obj2yaml.hpp - obj2yaml conversion tool -------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -// This file declares some helper routines, and also the format-specific -// writers. To add a new format, add the declaration here, and, in a separate -// source file, implement it. -//===----------------------------------------------------------------------===// - -#ifndef LLVM_UTILS_OBJ2YAML_H -#define LLVM_UTILS_OBJ2YAML_H - -#include "llvm/ADT/ArrayRef.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Support/system_error.h" - -namespace yaml { // routines for writing YAML -// Write a hex stream: -// <Prefix> !hex: "<hex digits>" #|<ASCII chars>\n - llvm::raw_ostream &writeHexStream - (llvm::raw_ostream &Out, const llvm::ArrayRef<uint8_t> arr); - -// Writes a number in hex; prefix it by 0x if it is >= 10 - llvm::raw_ostream &writeHexNumber - (llvm::raw_ostream &Out, unsigned long long N); -} - -llvm::error_code coff2yaml(llvm::raw_ostream &Out, llvm::MemoryBuffer *TheObj); - -#endif diff --git a/utils/release/tag.sh b/utils/release/tag.sh index 399d5c5a7f..c327174f81 100755 --- a/utils/release/tag.sh +++ b/utils/release/tag.sh @@ -32,7 +32,7 @@ function usage() { function tag_version() { set -x - for proj in llvm cfe dragonegg test-suite compiler-rt ; do + for proj in llvm cfe dragonegg test-suite compiler-rt libcxx clang-tools-extra ; do if svn ls $base_url/$proj/branches/release_$release > /dev/null 2>&1 ; then if [ $rebranch = "no" ]; then continue @@ -49,7 +49,7 @@ function tag_version() { function tag_release_candidate() { set -x - for proj in llvm cfe dragonegg test-suite compiler-rt ; do + for proj in llvm cfe dragonegg test-suite compiler-rt libcxx clang-tools-extra ; do if ! svn ls $base_url/$proj/tags/RELEASE_$release > /dev/null 2>&1 ; then svn mkdir -m "Creating release directory for release_$release." $base_url/$proj/tags/RELEASE_$release fi diff --git a/utils/release/test-release.sh b/utils/release/test-release.sh index a62e82974a..86200fdd84 100755 --- a/utils/release/test-release.sh +++ b/utils/release/test-release.sh @@ -35,7 +35,7 @@ do_objc="yes" do_64bit="yes" do_debug="no" do_asserts="no" -do_compare="yes" +do_compare="no" BuildDir="`pwd`" function usage() { diff --git a/utils/unittest/UnitTestMain/TestMain.cpp b/utils/unittest/UnitTestMain/TestMain.cpp index ce32b7380f..5387512e6f 100644 --- a/utils/unittest/UnitTestMain/TestMain.cpp +++ b/utils/unittest/UnitTestMain/TestMain.cpp @@ -20,11 +20,16 @@ # endif #endif +const char *TestMainArgv0; + int main(int argc, char **argv) { llvm::sys::PrintStackTraceOnErrorSignal(); testing::InitGoogleTest(&argc, argv); llvm::cl::ParseCommandLineOptions(argc, argv); + // Make it easy for a test to re-execute itself by saving argv[0]. + TestMainArgv0 = argv[0]; + # if defined(LLVM_ON_WIN32) // Disable all of the possible ways Windows conspires to make automated // testing impossible. diff --git a/utils/yaml2obj/CMakeLists.txt b/utils/yaml2obj/CMakeLists.txt deleted file mode 100644 index f8b1197524..0000000000 --- a/utils/yaml2obj/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -add_llvm_utility(yaml2obj - yaml2obj.cpp - ) - -target_link_libraries(yaml2obj LLVMSupport) diff --git a/utils/yaml2obj/Makefile b/utils/yaml2obj/Makefile deleted file mode 100644 index e746d85190..0000000000 --- a/utils/yaml2obj/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -##===- utils/yaml2obj/Makefile ----------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../.. -TOOLNAME = yaml2obj -USEDLIBS = LLVMSupport.a - -# This tool has no plugins, optimize startup time. -TOOL_NO_EXPORTS = 1 - -# Don't install this utility -NO_INSTALL = 1 - -include $(LEVEL)/Makefile.common diff --git a/utils/yaml2obj/yaml2obj.cpp b/utils/yaml2obj/yaml2obj.cpp deleted file mode 100644 index 5a8ec1d096..0000000000 --- a/utils/yaml2obj/yaml2obj.cpp +++ /dev/null @@ -1,879 +0,0 @@ -//===- yaml2obj - Convert YAML to a binary object file --------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This program takes a YAML description of an object file and outputs the -// binary equivalent. -// -// This is used for writing tests that require binary files. -// -//===----------------------------------------------------------------------===// - -#include "llvm/ADT/SmallString.h" -#include "llvm/ADT/StringExtras.h" -#include "llvm/ADT/StringMap.h" -#include "llvm/ADT/StringSwitch.h" -#include "llvm/Support/COFF.h" -#include "llvm/Support/Casting.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/Endian.h" -#include "llvm/Support/ManagedStatic.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/PrettyStackTrace.h" -#include "llvm/Support/Signals.h" -#include "llvm/Support/SourceMgr.h" -#include "llvm/Support/YAMLParser.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Support/system_error.h" -#include <vector> - -using namespace llvm; - -static cl::opt<std::string> - Input(cl::Positional, cl::desc("<input>"), cl::init("-")); - -template<class T> -typename llvm::enable_if_c<std::numeric_limits<T>::is_integer, bool>::type -getAs(const llvm::yaml::ScalarNode *SN, T &Result) { - SmallString<4> Storage; - StringRef Value = SN->getValue(Storage); - if (Value.getAsInteger(0, Result)) - return false; - return true; -} - -// Given a container with begin and end with ::value_type of a character type. -// Iterate through pairs of characters in the the set of [a-fA-F0-9] ignoring -// all other characters. -struct hex_pair_iterator { - StringRef::const_iterator Current, End; - typedef SmallVector<char, 2> value_type; - value_type Pair; - bool IsDone; - - hex_pair_iterator(StringRef C) - : Current(C.begin()), End(C.end()), IsDone(false) { - // Initalize Pair. - ++*this; - } - - // End iterator. - hex_pair_iterator() : Current(), End(), IsDone(true) {} - - value_type operator *() const { - return Pair; - } - - hex_pair_iterator operator ++() { - // We're at the end of the input. - if (Current == End) { - IsDone = true; - return *this; - } - Pair = value_type(); - for (; Current != End && Pair.size() != 2; ++Current) { - // Is a valid hex digit. - if ((*Current >= '0' && *Current <= '9') || - (*Current >= 'a' && *Current <= 'f') || - (*Current >= 'A' && *Current <= 'F')) - Pair.push_back(*Current); - } - // Hit the end without getting 2 hex digits. Pair is invalid. - if (Pair.size() != 2) - IsDone = true; - return *this; - } - - bool operator ==(const hex_pair_iterator Other) { - return (IsDone == Other.IsDone) || - (Current == Other.Current && End == Other.End); - } - - bool operator !=(const hex_pair_iterator Other) { - return !(*this == Other); - } -}; - -template <class ContainerOut> -static bool hexStringToByteArray(StringRef Str, ContainerOut &Out) { - for (hex_pair_iterator I(Str), E; I != E; ++I) { - typename hex_pair_iterator::value_type Pair = *I; - typename ContainerOut::value_type Byte; - if (StringRef(Pair.data(), 2).getAsInteger(16, Byte)) - return false; - Out.push_back(Byte); - } - return true; -} - -/// This parses a yaml stream that represents a COFF object file. -/// See docs/yaml2obj for the yaml scheema. -struct COFFParser { - COFFParser(yaml::Stream &Input) : YS(Input) { - std::memset(&Header, 0, sizeof(Header)); - // A COFF string table always starts with a 4 byte size field. Offsets into - // it include this size, so allocate it now. - StringTable.append(4, 0); - } - - bool parseHeader(yaml::Node *HeaderN) { - yaml::MappingNode *MN = dyn_cast<yaml::MappingNode>(HeaderN); - if (!MN) { - YS.printError(HeaderN, "header's value must be a mapping node"); - return false; - } - for (yaml::MappingNode::iterator i = MN->begin(), e = MN->end(); - i != e; ++i) { - yaml::ScalarNode *Key = dyn_cast<yaml::ScalarNode>(i->getKey()); - if (!Key) { - YS.printError(i->getKey(), "Keys must be scalar values"); - return false; - } - SmallString<32> Storage; - StringRef KeyValue = Key->getValue(Storage); - if (KeyValue == "Characteristics") { - if (!parseHeaderCharacteristics(i->getValue())) - return false; - } else { - yaml::ScalarNode *Value = dyn_cast<yaml::ScalarNode>(i->getValue()); - if (!Value) { - YS.printError(Value, - Twine(KeyValue) + " must be a scalar value"); - return false; - } - if (KeyValue == "Machine") { - uint16_t Machine = COFF::MT_Invalid; - if (!getAs(Value, Machine)) { - // It's not a raw number, try matching the string. - StringRef ValueValue = Value->getValue(Storage); - Machine = StringSwitch<COFF::MachineTypes>(ValueValue) - .Case( "IMAGE_FILE_MACHINE_UNKNOWN" - , COFF::IMAGE_FILE_MACHINE_UNKNOWN) - .Case( "IMAGE_FILE_MACHINE_AM33" - , COFF::IMAGE_FILE_MACHINE_AM33) - .Case( "IMAGE_FILE_MACHINE_AMD64" - , COFF::IMAGE_FILE_MACHINE_AMD64) - .Case( "IMAGE_FILE_MACHINE_ARM" - , COFF::IMAGE_FILE_MACHINE_ARM) - .Case( "IMAGE_FILE_MACHINE_ARMV7" - , COFF::IMAGE_FILE_MACHINE_ARMV7) - .Case( "IMAGE_FILE_MACHINE_EBC" - , COFF::IMAGE_FILE_MACHINE_EBC) - .Case( "IMAGE_FILE_MACHINE_I386" - , COFF::IMAGE_FILE_MACHINE_I386) - .Case( "IMAGE_FILE_MACHINE_IA64" - , COFF::IMAGE_FILE_MACHINE_IA64) - .Case( "IMAGE_FILE_MACHINE_M32R" - , COFF::IMAGE_FILE_MACHINE_M32R) - .Case( "IMAGE_FILE_MACHINE_MIPS16" - , COFF::IMAGE_FILE_MACHINE_MIPS16) - .Case( "IMAGE_FILE_MACHINE_MIPSFPU" - , COFF::IMAGE_FILE_MACHINE_MIPSFPU) - .Case( "IMAGE_FILE_MACHINE_MIPSFPU16" - , COFF::IMAGE_FILE_MACHINE_MIPSFPU16) - .Case( "IMAGE_FILE_MACHINE_POWERPC" - , COFF::IMAGE_FILE_MACHINE_POWERPC) - .Case( "IMAGE_FILE_MACHINE_POWERPCFP" - , COFF::IMAGE_FILE_MACHINE_POWERPCFP) - .Case( "IMAGE_FILE_MACHINE_R4000" - , COFF::IMAGE_FILE_MACHINE_R4000) - .Case( "IMAGE_FILE_MACHINE_SH3" - , COFF::IMAGE_FILE_MACHINE_SH3) - .Case( "IMAGE_FILE_MACHINE_SH3DSP" - , COFF::IMAGE_FILE_MACHINE_SH3DSP) - .Case( "IMAGE_FILE_MACHINE_SH4" - , COFF::IMAGE_FILE_MACHINE_SH4) - .Case( "IMAGE_FILE_MACHINE_SH5" - , COFF::IMAGE_FILE_MACHINE_SH5) - .Case( "IMAGE_FILE_MACHINE_THUMB" - , COFF::IMAGE_FILE_MACHINE_THUMB) - .Case( "IMAGE_FILE_MACHINE_WCEMIPSV2" - , COFF::IMAGE_FILE_MACHINE_WCEMIPSV2) - .Default(COFF::MT_Invalid); - if (Machine == COFF::MT_Invalid) { - YS.printError(Value, "Invalid value for Machine"); - return false; - } - } - Header.Machine = Machine; - } else if (KeyValue == "NumberOfSections") { - if (!getAs(Value, Header.NumberOfSections)) { - YS.printError(Value, "Invalid value for NumberOfSections"); - return false; - } - } else if (KeyValue == "TimeDateStamp") { - if (!getAs(Value, Header.TimeDateStamp)) { - YS.printError(Value, "Invalid value for TimeDateStamp"); - return false; - } - } else if (KeyValue == "PointerToSymbolTable") { - if (!getAs(Value, Header.PointerToSymbolTable)) { - YS.printError(Value, "Invalid value for PointerToSymbolTable"); - return false; - } - } else if (KeyValue == "NumberOfSymbols") { - if (!getAs(Value, Header.NumberOfSymbols)) { - YS.printError(Value, "Invalid value for NumberOfSymbols"); - return false; - } - } else if (KeyValue == "SizeOfOptionalHeader") { - if (!getAs(Value, Header.SizeOfOptionalHeader)) { - YS.printError(Value, "Invalid value for SizeOfOptionalHeader"); - return false; - } - } else { - YS.printError(Key, "Unrecognized key in header"); - return false; - } - } - } - return true; - } - - bool parseHeaderCharacteristics(yaml::Node *Characteristics) { - yaml::ScalarNode *Value = dyn_cast<yaml::ScalarNode>(Characteristics); - yaml::SequenceNode *SeqValue - = dyn_cast<yaml::SequenceNode>(Characteristics); - if (!Value && !SeqValue) { - YS.printError(Characteristics, - "Characteristics must either be a number or sequence"); - return false; - } - if (Value) { - if (!getAs(Value, Header.Characteristics)) { - YS.printError(Value, "Invalid value for Characteristics"); - return false; - } - } else { - for (yaml::SequenceNode::iterator ci = SeqValue->begin(), - ce = SeqValue->end(); - ci != ce; ++ci) { - yaml::ScalarNode *CharValue = dyn_cast<yaml::ScalarNode>(&*ci); - if (!CharValue) { - YS.printError(CharValue, - "Characteristics must be scalar values"); - return false; - } - SmallString<32> Storage; - StringRef Char = CharValue->getValue(Storage); - uint16_t Characteristic = StringSwitch<COFF::Characteristics>(Char) - .Case( "IMAGE_FILE_RELOCS_STRIPPED" - , COFF::IMAGE_FILE_RELOCS_STRIPPED) - .Case( "IMAGE_FILE_EXECUTABLE_IMAGE" - , COFF::IMAGE_FILE_EXECUTABLE_IMAGE) - .Case( "IMAGE_FILE_LINE_NUMS_STRIPPED" - , COFF::IMAGE_FILE_LINE_NUMS_STRIPPED) - .Case( "IMAGE_FILE_LOCAL_SYMS_STRIPPED" - , COFF::IMAGE_FILE_LOCAL_SYMS_STRIPPED) - .Case( "IMAGE_FILE_AGGRESSIVE_WS_TRIM" - , COFF::IMAGE_FILE_AGGRESSIVE_WS_TRIM) - .Case( "IMAGE_FILE_LARGE_ADDRESS_AWARE" - , COFF::IMAGE_FILE_LARGE_ADDRESS_AWARE) - .Case( "IMAGE_FILE_BYTES_REVERSED_LO" - , COFF::IMAGE_FILE_BYTES_REVERSED_LO) - .Case( "IMAGE_FILE_32BIT_MACHINE" - , COFF::IMAGE_FILE_32BIT_MACHINE) - .Case( "IMAGE_FILE_DEBUG_STRIPPED" - , COFF::IMAGE_FILE_DEBUG_STRIPPED) - .Case( "IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP" - , COFF::IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP) - .Case( "IMAGE_FILE_SYSTEM" - , COFF::IMAGE_FILE_SYSTEM) - .Case( "IMAGE_FILE_DLL" - , COFF::IMAGE_FILE_DLL) - .Case( "IMAGE_FILE_UP_SYSTEM_ONLY" - , COFF::IMAGE_FILE_UP_SYSTEM_ONLY) - .Default(COFF::C_Invalid); - if (Characteristic == COFF::C_Invalid) { - // TODO: Typo-correct. - YS.printError(CharValue, - "Invalid value for Characteristic"); - return false; - } - Header.Characteristics |= Characteristic; - } - } - return true; - } - - bool parseSections(yaml::Node *SectionsN) { - yaml::SequenceNode *SN = dyn_cast<yaml::SequenceNode>(SectionsN); - if (!SN) { - YS.printError(SectionsN, "Sections must be a sequence"); - return false; - } - for (yaml::SequenceNode::iterator i = SN->begin(), e = SN->end(); - i != e; ++i) { - Section Sec; - std::memset(&Sec.Header, 0, sizeof(Sec.Header)); - yaml::MappingNode *SecMap = dyn_cast<yaml::MappingNode>(&*i); - if (!SecMap) { - YS.printError(&*i, "Section entry must be a map"); - return false; - } - for (yaml::MappingNode::iterator si = SecMap->begin(), se = SecMap->end(); - si != se; ++si) { - yaml::ScalarNode *Key = dyn_cast<yaml::ScalarNode>(si->getKey()); - if (!Key) { - YS.printError(si->getKey(), "Keys must be scalar values"); - return false; - } - SmallString<32> Storage; - StringRef KeyValue = Key->getValue(Storage); - - yaml::ScalarNode *Value = dyn_cast<yaml::ScalarNode>(si->getValue()); - if (KeyValue == "Name") { - // If the name is less than 8 bytes, store it in place, otherwise - // store it in the string table. - StringRef Name = Value->getValue(Storage); - std::fill_n(Sec.Header.Name, unsigned(COFF::NameSize), 0); - if (Name.size() <= COFF::NameSize) { - std::copy(Name.begin(), Name.end(), Sec.Header.Name); - } else { - // Add string to the string table and format the index for output. - unsigned Index = getStringIndex(Name); - std::string str = utostr(Index); - if (str.size() > 7) { - YS.printError(Value, "String table got too large"); - return false; - } - Sec.Header.Name[0] = '/'; - std::copy(str.begin(), str.end(), Sec.Header.Name + 1); - } - } else if (KeyValue == "VirtualSize") { - if (!getAs(Value, Sec.Header.VirtualSize)) { - YS.printError(Value, "Invalid value for VirtualSize"); - return false; - } - } else if (KeyValue == "VirtualAddress") { - if (!getAs(Value, Sec.Header.VirtualAddress)) { - YS.printError(Value, "Invalid value for VirtualAddress"); - return false; - } - } else if (KeyValue == "SizeOfRawData") { - if (!getAs(Value, Sec.Header.SizeOfRawData)) { - YS.printError(Value, "Invalid value for SizeOfRawData"); - return false; - } - } else if (KeyValue == "PointerToRawData") { - if (!getAs(Value, Sec.Header.PointerToRawData)) { - YS.printError(Value, "Invalid value for PointerToRawData"); - return false; - } - } else if (KeyValue == "PointerToRelocations") { - if (!getAs(Value, Sec.Header.PointerToRelocations)) { - YS.printError(Value, "Invalid value for PointerToRelocations"); - return false; - } - } else if (KeyValue == "PointerToLineNumbers") { - if (!getAs(Value, Sec.Header.PointerToLineNumbers)) { - YS.printError(Value, "Invalid value for PointerToLineNumbers"); - return false; - } - } else if (KeyValue == "NumberOfRelocations") { - if (!getAs(Value, Sec.Header.NumberOfRelocations)) { - YS.printError(Value, "Invalid value for NumberOfRelocations"); - return false; - } - } else if (KeyValue == "NumberOfLineNumbers") { - if (!getAs(Value, Sec.Header.NumberOfLineNumbers)) { - YS.printError(Value, "Invalid value for NumberOfLineNumbers"); - return false; - } - } else if (KeyValue == "Characteristics") { - yaml::SequenceNode *SeqValue - = dyn_cast<yaml::SequenceNode>(si->getValue()); - if (!Value && !SeqValue) { - YS.printError(si->getValue(), - "Characteristics must either be a number or sequence"); - return false; - } - if (Value) { - if (!getAs(Value, Sec.Header.Characteristics)) { - YS.printError(Value, "Invalid value for Characteristics"); - return false; - } - } else { - for (yaml::SequenceNode::iterator ci = SeqValue->begin(), - ce = SeqValue->end(); - ci != ce; ++ci) { - yaml::ScalarNode *CharValue = dyn_cast<yaml::ScalarNode>(&*ci); - if (!CharValue) { - YS.printError(CharValue, "Invalid value for Characteristics"); - return false; - } - StringRef Char = CharValue->getValue(Storage); - uint32_t Characteristic = - StringSwitch<COFF::SectionCharacteristics>(Char) - .Case( "IMAGE_SCN_TYPE_NO_PAD" - , COFF::IMAGE_SCN_TYPE_NO_PAD) - .Case( "IMAGE_SCN_CNT_CODE" - , COFF::IMAGE_SCN_CNT_CODE) - .Case( "IMAGE_SCN_CNT_INITIALIZED_DATA" - , COFF::IMAGE_SCN_CNT_INITIALIZED_DATA) - .Case( "IMAGE_SCN_CNT_UNINITIALIZED_DATA" - , COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA) - .Case( "IMAGE_SCN_LNK_OTHER" - , COFF::IMAGE_SCN_LNK_OTHER) - .Case( "IMAGE_SCN_LNK_INFO" - , COFF::IMAGE_SCN_LNK_INFO) - .Case( "IMAGE_SCN_LNK_REMOVE" - , COFF::IMAGE_SCN_LNK_REMOVE) - .Case( "IMAGE_SCN_LNK_COMDAT" - , COFF::IMAGE_SCN_LNK_COMDAT) - .Case( "IMAGE_SCN_GPREL" - , COFF::IMAGE_SCN_GPREL) - .Case( "IMAGE_SCN_MEM_PURGEABLE" - , COFF::IMAGE_SCN_MEM_PURGEABLE) - .Case( "IMAGE_SCN_MEM_16BIT" - , COFF::IMAGE_SCN_MEM_16BIT) - .Case( "IMAGE_SCN_MEM_LOCKED" - , COFF::IMAGE_SCN_MEM_LOCKED) - .Case( "IMAGE_SCN_MEM_PRELOAD" - , COFF::IMAGE_SCN_MEM_PRELOAD) - .Case( "IMAGE_SCN_ALIGN_1BYTES" - , COFF::IMAGE_SCN_ALIGN_1BYTES) - .Case( "IMAGE_SCN_ALIGN_2BYTES" - , COFF::IMAGE_SCN_ALIGN_2BYTES) - .Case( "IMAGE_SCN_ALIGN_4BYTES" - , COFF::IMAGE_SCN_ALIGN_4BYTES) - .Case( "IMAGE_SCN_ALIGN_8BYTES" - , COFF::IMAGE_SCN_ALIGN_8BYTES) - .Case( "IMAGE_SCN_ALIGN_16BYTES" - , COFF::IMAGE_SCN_ALIGN_16BYTES) - .Case( "IMAGE_SCN_ALIGN_32BYTES" - , COFF::IMAGE_SCN_ALIGN_32BYTES) - .Case( "IMAGE_SCN_ALIGN_64BYTES" - , COFF::IMAGE_SCN_ALIGN_64BYTES) - .Case( "IMAGE_SCN_ALIGN_128BYTES" - , COFF::IMAGE_SCN_ALIGN_128BYTES) - .Case( "IMAGE_SCN_ALIGN_256BYTES" - , COFF::IMAGE_SCN_ALIGN_256BYTES) - .Case( "IMAGE_SCN_ALIGN_512BYTES" - , COFF::IMAGE_SCN_ALIGN_512BYTES) - .Case( "IMAGE_SCN_ALIGN_1024BYTES" - , COFF::IMAGE_SCN_ALIGN_1024BYTES) - .Case( "IMAGE_SCN_ALIGN_2048BYTES" - , COFF::IMAGE_SCN_ALIGN_2048BYTES) - .Case( "IMAGE_SCN_ALIGN_4096BYTES" - , COFF::IMAGE_SCN_ALIGN_4096BYTES) - .Case( "IMAGE_SCN_ALIGN_8192BYTES" - , COFF::IMAGE_SCN_ALIGN_8192BYTES) - .Case( "IMAGE_SCN_LNK_NRELOC_OVFL" - , COFF::IMAGE_SCN_LNK_NRELOC_OVFL) - .Case( "IMAGE_SCN_MEM_DISCARDABLE" - , COFF::IMAGE_SCN_MEM_DISCARDABLE) - .Case( "IMAGE_SCN_MEM_NOT_CACHED" - , COFF::IMAGE_SCN_MEM_NOT_CACHED) - .Case( "IMAGE_SCN_MEM_NOT_PAGED" - , COFF::IMAGE_SCN_MEM_NOT_PAGED) - .Case( "IMAGE_SCN_MEM_SHARED" - , COFF::IMAGE_SCN_MEM_SHARED) - .Case( "IMAGE_SCN_MEM_EXECUTE" - , COFF::IMAGE_SCN_MEM_EXECUTE) - .Case( "IMAGE_SCN_MEM_READ" - , COFF::IMAGE_SCN_MEM_READ) - .Case( "IMAGE_SCN_MEM_WRITE" - , COFF::IMAGE_SCN_MEM_WRITE) - .Default(COFF::SC_Invalid); - if (Characteristic == COFF::SC_Invalid) { - YS.printError(CharValue, "Invalid value for Characteristic"); - return false; - } - Sec.Header.Characteristics |= Characteristic; - } - } - } else if (KeyValue == "SectionData") { - yaml::ScalarNode *Value = dyn_cast<yaml::ScalarNode>(si->getValue()); - SmallString<32> Storage; - StringRef Data = Value->getValue(Storage); - if (!hexStringToByteArray(Data, Sec.Data)) { - YS.printError(Value, "SectionData must be a collection of pairs of" - "hex bytes"); - return false; - } - } else - si->skip(); - } - Sections.push_back(Sec); - } - return true; - } - - bool parseSymbols(yaml::Node *SymbolsN) { - yaml::SequenceNode *SN = dyn_cast<yaml::SequenceNode>(SymbolsN); - if (!SN) { - YS.printError(SymbolsN, "Symbols must be a sequence"); - return false; - } - for (yaml::SequenceNode::iterator i = SN->begin(), e = SN->end(); - i != e; ++i) { - Symbol Sym; - std::memset(&Sym.Header, 0, sizeof(Sym.Header)); - yaml::MappingNode *SymMap = dyn_cast<yaml::MappingNode>(&*i); - if (!SymMap) { - YS.printError(&*i, "Symbol must be a map"); - return false; - } - for (yaml::MappingNode::iterator si = SymMap->begin(), se = SymMap->end(); - si != se; ++si) { - yaml::ScalarNode *Key = dyn_cast<yaml::ScalarNode>(si->getKey()); - if (!Key) { - YS.printError(si->getKey(), "Keys must be scalar values"); - return false; - } - SmallString<32> Storage; - StringRef KeyValue = Key->getValue(Storage); - - yaml::ScalarNode *Value = dyn_cast<yaml::ScalarNode>(si->getValue()); - if (!Value) { - YS.printError(si->getValue(), "Must be a scalar value"); - return false; - } - if (KeyValue == "Name") { - // If the name is less than 8 bytes, store it in place, otherwise - // store it in the string table. - StringRef Name = Value->getValue(Storage); - std::fill_n(Sym.Header.Name, unsigned(COFF::NameSize), 0); - if (Name.size() <= COFF::NameSize) { - std::copy(Name.begin(), Name.end(), Sym.Header.Name); - } else { - // Add string to the string table and format the index for output. - unsigned Index = getStringIndex(Name); - *reinterpret_cast<support::aligned_ulittle32_t*>( - Sym.Header.Name + 4) = Index; - } - } else if (KeyValue == "Value") { - if (!getAs(Value, Sym.Header.Value)) { - YS.printError(Value, "Invalid value for Value"); - return false; - } - } else if (KeyValue == "SimpleType") { - Sym.Header.Type |= StringSwitch<COFF::SymbolBaseType>( - Value->getValue(Storage)) - .Case("IMAGE_SYM_TYPE_NULL", COFF::IMAGE_SYM_TYPE_NULL) - .Case("IMAGE_SYM_TYPE_VOID", COFF::IMAGE_SYM_TYPE_VOID) - .Case("IMAGE_SYM_TYPE_CHAR", COFF::IMAGE_SYM_TYPE_CHAR) - .Case("IMAGE_SYM_TYPE_SHORT", COFF::IMAGE_SYM_TYPE_SHORT) - .Case("IMAGE_SYM_TYPE_INT", COFF::IMAGE_SYM_TYPE_INT) - .Case("IMAGE_SYM_TYPE_LONG", COFF::IMAGE_SYM_TYPE_LONG) - .Case("IMAGE_SYM_TYPE_FLOAT", COFF::IMAGE_SYM_TYPE_FLOAT) - .Case("IMAGE_SYM_TYPE_DOUBLE", COFF::IMAGE_SYM_TYPE_DOUBLE) - .Case("IMAGE_SYM_TYPE_STRUCT", COFF::IMAGE_SYM_TYPE_STRUCT) - .Case("IMAGE_SYM_TYPE_UNION", COFF::IMAGE_SYM_TYPE_UNION) - .Case("IMAGE_SYM_TYPE_ENUM", COFF::IMAGE_SYM_TYPE_ENUM) - .Case("IMAGE_SYM_TYPE_MOE", COFF::IMAGE_SYM_TYPE_MOE) - .Case("IMAGE_SYM_TYPE_BYTE", COFF::IMAGE_SYM_TYPE_BYTE) - .Case("IMAGE_SYM_TYPE_WORD", COFF::IMAGE_SYM_TYPE_WORD) - .Case("IMAGE_SYM_TYPE_UINT", COFF::IMAGE_SYM_TYPE_UINT) - .Case("IMAGE_SYM_TYPE_DWORD", COFF::IMAGE_SYM_TYPE_DWORD) - .Default(COFF::IMAGE_SYM_TYPE_NULL); - } else if (KeyValue == "ComplexType") { - Sym.Header.Type |= StringSwitch<COFF::SymbolComplexType>( - Value->getValue(Storage)) - .Case("IMAGE_SYM_DTYPE_NULL", COFF::IMAGE_SYM_DTYPE_NULL) - .Case("IMAGE_SYM_DTYPE_POINTER", COFF::IMAGE_SYM_DTYPE_POINTER) - .Case("IMAGE_SYM_DTYPE_FUNCTION", COFF::IMAGE_SYM_DTYPE_FUNCTION) - .Case("IMAGE_SYM_DTYPE_ARRAY", COFF::IMAGE_SYM_DTYPE_ARRAY) - .Default(COFF::IMAGE_SYM_DTYPE_NULL) - << COFF::SCT_COMPLEX_TYPE_SHIFT; - } else if (KeyValue == "StorageClass") { - Sym.Header.StorageClass = StringSwitch<COFF::SymbolStorageClass>( - Value->getValue(Storage)) - .Case( "IMAGE_SYM_CLASS_END_OF_FUNCTION" - , COFF::IMAGE_SYM_CLASS_END_OF_FUNCTION) - .Case( "IMAGE_SYM_CLASS_NULL" - , COFF::IMAGE_SYM_CLASS_NULL) - .Case( "IMAGE_SYM_CLASS_AUTOMATIC" - , COFF::IMAGE_SYM_CLASS_AUTOMATIC) - .Case( "IMAGE_SYM_CLASS_EXTERNAL" - , COFF::IMAGE_SYM_CLASS_EXTERNAL) - .Case( "IMAGE_SYM_CLASS_STATIC" - , COFF::IMAGE_SYM_CLASS_STATIC) - .Case( "IMAGE_SYM_CLASS_REGISTER" - , COFF::IMAGE_SYM_CLASS_REGISTER) - .Case( "IMAGE_SYM_CLASS_EXTERNAL_DEF" - , COFF::IMAGE_SYM_CLASS_EXTERNAL_DEF) - .Case( "IMAGE_SYM_CLASS_LABEL" - , COFF::IMAGE_SYM_CLASS_LABEL) - .Case( "IMAGE_SYM_CLASS_UNDEFINED_LABEL" - , COFF::IMAGE_SYM_CLASS_UNDEFINED_LABEL) - .Case( "IMAGE_SYM_CLASS_MEMBER_OF_STRUCT" - , COFF::IMAGE_SYM_CLASS_MEMBER_OF_STRUCT) - .Case( "IMAGE_SYM_CLASS_ARGUMENT" - , COFF::IMAGE_SYM_CLASS_ARGUMENT) - .Case( "IMAGE_SYM_CLASS_STRUCT_TAG" - , COFF::IMAGE_SYM_CLASS_STRUCT_TAG) - .Case( "IMAGE_SYM_CLASS_MEMBER_OF_UNION" - , COFF::IMAGE_SYM_CLASS_MEMBER_OF_UNION) - .Case( "IMAGE_SYM_CLASS_UNION_TAG" - , COFF::IMAGE_SYM_CLASS_UNION_TAG) - .Case( "IMAGE_SYM_CLASS_TYPE_DEFINITION" - , COFF::IMAGE_SYM_CLASS_TYPE_DEFINITION) - .Case( "IMAGE_SYM_CLASS_UNDEFINED_STATIC" - , COFF::IMAGE_SYM_CLASS_UNDEFINED_STATIC) - .Case( "IMAGE_SYM_CLASS_ENUM_TAG" - , COFF::IMAGE_SYM_CLASS_ENUM_TAG) - .Case( "IMAGE_SYM_CLASS_MEMBER_OF_ENUM" - , COFF::IMAGE_SYM_CLASS_MEMBER_OF_ENUM) - .Case( "IMAGE_SYM_CLASS_REGISTER_PARAM" - , COFF::IMAGE_SYM_CLASS_REGISTER_PARAM) - .Case( "IMAGE_SYM_CLASS_BIT_FIELD" - , COFF::IMAGE_SYM_CLASS_BIT_FIELD) - .Case( "IMAGE_SYM_CLASS_BLOCK" - , COFF::IMAGE_SYM_CLASS_BLOCK) - .Case( "IMAGE_SYM_CLASS_FUNCTION" - , COFF::IMAGE_SYM_CLASS_FUNCTION) - .Case( "IMAGE_SYM_CLASS_END_OF_STRUCT" - , COFF::IMAGE_SYM_CLASS_END_OF_STRUCT) - .Case( "IMAGE_SYM_CLASS_FILE" - , COFF::IMAGE_SYM_CLASS_FILE) - .Case( "IMAGE_SYM_CLASS_SECTION" - , COFF::IMAGE_SYM_CLASS_SECTION) - .Case( "IMAGE_SYM_CLASS_WEAK_EXTERNAL" - , COFF::IMAGE_SYM_CLASS_WEAK_EXTERNAL) - .Case( "IMAGE_SYM_CLASS_CLR_TOKEN" - , COFF::IMAGE_SYM_CLASS_CLR_TOKEN) - .Default(COFF::SSC_Invalid); - if (Sym.Header.StorageClass == COFF::SSC_Invalid) { - YS.printError(Value, "Invalid value for StorageClass"); - return false; - } - } else if (KeyValue == "SectionNumber") { - if (!getAs(Value, Sym.Header.SectionNumber)) { - YS.printError(Value, "Invalid value for SectionNumber"); - return false; - } - } else if (KeyValue == "AuxillaryData") { - StringRef Data = Value->getValue(Storage); - if (!hexStringToByteArray(Data, Sym.AuxSymbols)) { - YS.printError(Value, "AuxillaryData must be a collection of pairs" - "of hex bytes"); - return false; - } - } else - si->skip(); - } - Symbols.push_back(Sym); - } - return true; - } - - bool parse() { - yaml::Document &D = *YS.begin(); - yaml::MappingNode *Root = dyn_cast<yaml::MappingNode>(D.getRoot()); - if (!Root) { - YS.printError(D.getRoot(), "Root node must be a map"); - return false; - } - for (yaml::MappingNode::iterator i = Root->begin(), e = Root->end(); - i != e; ++i) { - yaml::ScalarNode *Key = dyn_cast<yaml::ScalarNode>(i->getKey()); - if (!Key) { - YS.printError(i->getKey(), "Keys must be scalar values"); - return false; - } - SmallString<32> Storage; - StringRef KeyValue = Key->getValue(Storage); - if (KeyValue == "header") { - if (!parseHeader(i->getValue())) - return false; - } else if (KeyValue == "sections") { - if (!parseSections(i->getValue())) - return false; - } else if (KeyValue == "symbols") { - if (!parseSymbols(i->getValue())) - return false; - } - } - return !YS.failed(); - } - - unsigned getStringIndex(StringRef Str) { - StringMap<unsigned>::iterator i = StringTableMap.find(Str); - if (i == StringTableMap.end()) { - unsigned Index = StringTable.size(); - StringTable.append(Str.begin(), Str.end()); - StringTable.push_back(0); - StringTableMap[Str] = Index; - return Index; - } - return i->second; - } - - yaml::Stream &YS; - COFF::header Header; - - struct Section { - COFF::section Header; - std::vector<uint8_t> Data; - std::vector<COFF::relocation> Relocations; - }; - - struct Symbol { - COFF::symbol Header; - std::vector<uint8_t> AuxSymbols; - }; - - std::vector<Section> Sections; - std::vector<Symbol> Symbols; - StringMap<unsigned> StringTableMap; - std::string StringTable; -}; - -// Take a CP and assign addresses and sizes to everything. Returns false if the -// layout is not valid to do. -static bool layoutCOFF(COFFParser &CP) { - uint32_t SectionTableStart = 0; - uint32_t SectionTableSize = 0; - - // The section table starts immediately after the header, including the - // optional header. - SectionTableStart = sizeof(COFF::header) + CP.Header.SizeOfOptionalHeader; - SectionTableSize = sizeof(COFF::section) * CP.Sections.size(); - - uint32_t CurrentSectionDataOffset = SectionTableStart + SectionTableSize; - - // Assign each section data address consecutively. - for (std::vector<COFFParser::Section>::iterator i = CP.Sections.begin(), - e = CP.Sections.end(); - i != e; ++i) { - if (!i->Data.empty()) { - i->Header.SizeOfRawData = i->Data.size(); - i->Header.PointerToRawData = CurrentSectionDataOffset; - CurrentSectionDataOffset += i->Header.SizeOfRawData; - // TODO: Handle alignment. - } else { - i->Header.SizeOfRawData = 0; - i->Header.PointerToRawData = 0; - } - } - - uint32_t SymbolTableStart = CurrentSectionDataOffset; - - // Calculate number of symbols. - uint32_t NumberOfSymbols = 0; - for (std::vector<COFFParser::Symbol>::iterator i = CP.Symbols.begin(), - e = CP.Symbols.end(); - i != e; ++i) { - if (i->AuxSymbols.size() % COFF::SymbolSize != 0) { - errs() << "AuxillaryData size not a multiple of symbol size!\n"; - return false; - } - i->Header.NumberOfAuxSymbols = i->AuxSymbols.size() / COFF::SymbolSize; - NumberOfSymbols += 1 + i->Header.NumberOfAuxSymbols; - } - - // Store all the allocated start addresses in the header. - CP.Header.NumberOfSections = CP.Sections.size(); - CP.Header.NumberOfSymbols = NumberOfSymbols; - CP.Header.PointerToSymbolTable = SymbolTableStart; - - *reinterpret_cast<support::ulittle32_t *>(&CP.StringTable[0]) - = CP.StringTable.size(); - - return true; -} - -template <typename value_type> -struct binary_le_impl { - value_type Value; - binary_le_impl(value_type V) : Value(V) {} -}; - -template <typename value_type> -raw_ostream &operator <<( raw_ostream &OS - , const binary_le_impl<value_type> &BLE) { - char Buffer[sizeof(BLE.Value)]; - support::endian::write<value_type, support::little, support::unaligned>( - Buffer, BLE.Value); - OS.write(Buffer, sizeof(BLE.Value)); - return OS; -} - -template <typename value_type> -binary_le_impl<value_type> binary_le(value_type V) { - return binary_le_impl<value_type>(V); -} - -void writeCOFF(COFFParser &CP, raw_ostream &OS) { - OS << binary_le(CP.Header.Machine) - << binary_le(CP.Header.NumberOfSections) - << binary_le(CP.Header.TimeDateStamp) - << binary_le(CP.Header.PointerToSymbolTable) - << binary_le(CP.Header.NumberOfSymbols) - << binary_le(CP.Header.SizeOfOptionalHeader) - << binary_le(CP.Header.Characteristics); - - // Output section table. - for (std::vector<COFFParser::Section>::const_iterator i = CP.Sections.begin(), - e = CP.Sections.end(); - i != e; ++i) { - OS.write(i->Header.Name, COFF::NameSize); - OS << binary_le(i->Header.VirtualSize) - << binary_le(i->Header.VirtualAddress) - << binary_le(i->Header.SizeOfRawData) - << binary_le(i->Header.PointerToRawData) - << binary_le(i->Header.PointerToRelocations) - << binary_le(i->Header.PointerToLineNumbers) - << binary_le(i->Header.NumberOfRelocations) - << binary_le(i->Header.NumberOfLineNumbers) - << binary_le(i->Header.Characteristics); - } - - // Output section data. - for (std::vector<COFFParser::Section>::const_iterator i = CP.Sections.begin(), - e = CP.Sections.end(); - i != e; ++i) { - if (!i->Data.empty()) - OS.write(reinterpret_cast<const char*>(&i->Data[0]), i->Data.size()); - } - - // Output symbol table. - - for (std::vector<COFFParser::Symbol>::const_iterator i = CP.Symbols.begin(), - e = CP.Symbols.end(); - i != e; ++i) { - OS.write(i->Header.Name, COFF::NameSize); - OS << binary_le(i->Header.Value) - << binary_le(i->Header.SectionNumber) - << binary_le(i->Header.Type) - << binary_le(i->Header.StorageClass) - << binary_le(i->Header.NumberOfAuxSymbols); - if (!i->AuxSymbols.empty()) - OS.write( reinterpret_cast<const char*>(&i->AuxSymbols[0]) - , i->AuxSymbols.size()); - } - - // Output string table. - OS.write(&CP.StringTable[0], CP.StringTable.size()); -} - -int main(int argc, char **argv) { - cl::ParseCommandLineOptions(argc, argv); - sys::PrintStackTraceOnErrorSignal(); - PrettyStackTraceProgram X(argc, argv); - llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. - - OwningPtr<MemoryBuffer> Buf; - if (MemoryBuffer::getFileOrSTDIN(Input, Buf)) - return 1; - - SourceMgr SM; - yaml::Stream S(Buf->getBuffer(), SM); - COFFParser CP(S); - if (!CP.parse()) { - errs() << "yaml2obj: Failed to parse YAML file!\n"; - return 1; - } - if (!layoutCOFF(CP)) { - errs() << "yaml2obj: Failed to layout COFF file!\n"; - return 1; - } - writeCOFF(CP, outs()); -} |