diff options
Diffstat (limited to 'utils')
-rw-r--r-- | utils/LLVMBuild.txt | 4 | ||||
-rw-r--r-- | utils/TableGen/AsmMatcherEmitter.cpp | 85 | ||||
-rw-r--r-- | utils/TableGen/CMakeLists.txt | 1 | ||||
-rw-r--r-- | utils/TableGen/CodeGenRegisters.cpp | 146 | ||||
-rw-r--r-- | utils/TableGen/CodeGenRegisters.h | 7 | ||||
-rw-r--r-- | utils/TableGen/CodeGenTarget.cpp | 1 | ||||
-rw-r--r-- | utils/TableGen/DFAPacketizerEmitter.cpp | 512 | ||||
-rw-r--r-- | utils/TableGen/DFAPacketizerEmitter.h | 54 | ||||
-rw-r--r-- | utils/TableGen/EDEmitter.cpp | 12 | ||||
-rw-r--r-- | utils/TableGen/LLVMBuild.txt | 1 | ||||
-rw-r--r-- | utils/TableGen/SubtargetEmitter.cpp | 4 | ||||
-rw-r--r-- | utils/TableGen/TableGen.cpp | 7 | ||||
-rw-r--r-- | utils/buildit/GNUmakefile | 2 | ||||
-rw-r--r-- | utils/lit/lit/TestRunner.py | 50 | ||||
-rw-r--r-- | utils/llvm-build/llvmbuild/componentinfo.py | 37 | ||||
-rw-r--r-- | utils/llvm-build/llvmbuild/main.py | 171 | ||||
-rwxr-xr-x | utils/release/tag.sh | 10 | ||||
-rwxr-xr-x | utils/release/test-release.sh | 50 | ||||
-rw-r--r-- | utils/unittest/CMakeLists.txt | 8 | ||||
-rw-r--r-- | utils/unittest/UnitTestMain/Makefile | 2 | ||||
-rw-r--r-- | utils/unittest/googletest/Makefile | 2 |
21 files changed, 1003 insertions, 163 deletions
diff --git a/utils/LLVMBuild.txt b/utils/LLVMBuild.txt index fc16720..382bfd3 100644 --- a/utils/LLVMBuild.txt +++ b/utils/LLVMBuild.txt @@ -15,6 +15,9 @@ ; ;===------------------------------------------------------------------------===; +[common] +subdirectories = TableGen unittest + [component_0] type = Group name = BuildTools @@ -24,4 +27,3 @@ parent = $ROOT type = Group name = UtilityTools parent = $ROOT - diff --git a/utils/TableGen/AsmMatcherEmitter.cpp b/utils/TableGen/AsmMatcherEmitter.cpp index 01754c3..5ef0db9 100644 --- a/utils/TableGen/AsmMatcherEmitter.cpp +++ b/utils/TableGen/AsmMatcherEmitter.cpp @@ -252,11 +252,6 @@ public: switch (Kind) { case Invalid: assert(0 && "Invalid kind!"); - case Token: - // Tokens are comparable by value. - // - // FIXME: Compare by enum value. - return ValueName < RHS.ValueName; default: // This class precedes the RHS if it is a proper subset of the RHS. @@ -737,7 +732,9 @@ void MatchableInfo::TokenizeAsmString(const AsmMatcherInfo &Info) { // The first token of the instruction is the mnemonic, which must be a // simple string, not a $foo variable or a singleton register. - assert(!AsmOperands.empty() && "Instruction has no tokens?"); + if (AsmOperands.empty()) + throw TGError(TheDef->getLoc(), + "Instruction '" + TheDef->getName() + "' has no tokens"); Mnemonic = AsmOperands[0].Token; if (Mnemonic[0] == '$' || getSingletonRegisterForAsmOperand(0, Info)) throw TGError(TheDef->getLoc(), @@ -1302,6 +1299,17 @@ void AsmMatcherInfo::BuildInfo() { II->BuildAliasResultOperands(); } + // Process token alias definitions and set up the associated superclass + // information. + std::vector<Record*> AllTokenAliases = + Records.getAllDerivedDefinitions("TokenAlias"); + for (unsigned i = 0, e = AllTokenAliases.size(); i != e; ++i) { + Record *Rec = AllTokenAliases[i]; + ClassInfo *FromClass = getTokenClass(Rec->getValueAsString("FromToken")); + ClassInfo *ToClass = getTokenClass(Rec->getValueAsString("ToToken")); + FromClass->SuperClasses.push_back(ToClass); + } + // Reorder classes so that classes precede super classes. std::sort(Classes.begin(), Classes.end(), less_ptr<ClassInfo>()); } @@ -1663,7 +1671,7 @@ static void EmitMatchClassEnumeration(CodeGenTarget &Target, /// EmitValidateOperandClass - Emit the function to validate an operand class. static void EmitValidateOperandClass(AsmMatcherInfo &Info, raw_ostream &OS) { - OS << "static bool ValidateOperandClass(MCParsedAsmOperand *GOp, " + OS << "static bool validateOperandClass(MCParsedAsmOperand *GOp, " << "MatchClassKind Kind) {\n"; OS << " " << Info.Target.getName() << "Operand &Operand = *(" << Info.Target.getName() << "Operand*)GOp;\n"; @@ -1674,7 +1682,8 @@ static void EmitValidateOperandClass(AsmMatcherInfo &Info, // Check for Token operands first. OS << " if (Operand.isToken())\n"; - OS << " return MatchTokenString(Operand.getToken()) == Kind;\n\n"; + OS << " return isSubclass(matchTokenString(Operand.getToken()), Kind);" + << "\n\n"; // Check for register operands, including sub-classes. OS << " if (Operand.isReg()) {\n"; @@ -1688,7 +1697,7 @@ static void EmitValidateOperandClass(AsmMatcherInfo &Info, << it->first->getName() << ": OpKind = " << it->second->Name << "; break;\n"; OS << " }\n"; - OS << " return IsSubclass(OpKind, Kind);\n"; + OS << " return isSubclass(OpKind, Kind);\n"; OS << " }\n\n"; // Check the user classes. We don't care what order since we're only @@ -1715,8 +1724,8 @@ static void EmitValidateOperandClass(AsmMatcherInfo &Info, static void EmitIsSubclass(CodeGenTarget &Target, std::vector<ClassInfo*> &Infos, raw_ostream &OS) { - OS << "/// IsSubclass - Compute whether \\arg A is a subclass of \\arg B.\n"; - OS << "static bool IsSubclass(MatchClassKind A, MatchClassKind B) {\n"; + OS << "/// isSubclass - Compute whether \\arg A is a subclass of \\arg B.\n"; + OS << "static bool isSubclass(MatchClassKind A, MatchClassKind B) {\n"; OS << " if (A == B)\n"; OS << " return true;\n\n"; @@ -1727,32 +1736,30 @@ static void EmitIsSubclass(CodeGenTarget &Target, ie = Infos.end(); it != ie; ++it) { ClassInfo &A = **it; - if (A.Kind != ClassInfo::Token) { - std::vector<StringRef> SuperClasses; - for (std::vector<ClassInfo*>::iterator it = Infos.begin(), - ie = Infos.end(); it != ie; ++it) { - ClassInfo &B = **it; - - if (&A != &B && A.isSubsetOf(B)) - SuperClasses.push_back(B.Name); - } + std::vector<StringRef> SuperClasses; + for (std::vector<ClassInfo*>::iterator it = Infos.begin(), + ie = Infos.end(); it != ie; ++it) { + ClassInfo &B = **it; - if (SuperClasses.empty()) - continue; + if (&A != &B && A.isSubsetOf(B)) + SuperClasses.push_back(B.Name); + } - OS << "\n case " << A.Name << ":\n"; + if (SuperClasses.empty()) + continue; - if (SuperClasses.size() == 1) { - OS << " return B == " << SuperClasses.back() << ";\n"; - continue; - } + OS << "\n case " << A.Name << ":\n"; - OS << " switch (B) {\n"; - OS << " default: return false;\n"; - for (unsigned i = 0, e = SuperClasses.size(); i != e; ++i) - OS << " case " << SuperClasses[i] << ": return true;\n"; - OS << " }\n"; + if (SuperClasses.size() == 1) { + OS << " return B == " << SuperClasses.back() << ";\n"; + continue; } + + OS << " switch (B) {\n"; + OS << " default: return false;\n"; + for (unsigned i = 0, e = SuperClasses.size(); i != e; ++i) + OS << " case " << SuperClasses[i] << ": return true;\n"; + OS << " }\n"; } OS << " }\n"; OS << "}\n\n"; @@ -1774,7 +1781,7 @@ static void EmitMatchTokenString(CodeGenTarget &Target, "return " + CI.Name + ";")); } - OS << "static MatchClassKind MatchTokenString(StringRef Name) {\n"; + OS << "static MatchClassKind matchTokenString(StringRef Name) {\n"; StringMatcher("Name", Matches, OS).Emit(); @@ -1912,7 +1919,7 @@ static bool EmitMnemonicAliases(raw_ostream &OS, const AsmMatcherInfo &Info) { Info.getRecords().getAllDerivedDefinitions("MnemonicAlias"); if (Aliases.empty()) return false; - OS << "static void ApplyMnemonicAliases(StringRef &Mnemonic, " + OS << "static void applyMnemonicAliases(StringRef &Mnemonic, " "unsigned Features) {\n"; // Keep track of all the aliases from a mnemonic. Use an std::map so that the @@ -2061,7 +2068,7 @@ static void EmitCustomOperandParsing(raw_ostream &OS, CodeGenTarget &Target, // the found operand class. OS << Target.getName() << ClassName << "::OperandMatchResultTy " << Target.getName() << ClassName << "::\n" - << "TryCustomParseOperand(SmallVectorImpl<MCParsedAsmOperand*>" + << "tryCustomParseOperand(SmallVectorImpl<MCParsedAsmOperand*>" << " &Operands,\n unsigned MCK) {\n\n" << " switch(MCK) {\n"; @@ -2127,7 +2134,7 @@ static void EmitCustomOperandParsing(raw_ostream &OS, CodeGenTarget &Target, // Emit call to the custom parser method OS << " // call custom parse method to handle the operand\n"; OS << " OperandMatchResultTy Result = "; - OS << "TryCustomParseOperand(Operands, it->Class);\n"; + OS << "tryCustomParseOperand(Operands, it->Class);\n"; OS << " if (Result != MatchOperand_NoMatch)\n"; OS << " return Result;\n"; OS << " }\n\n"; @@ -2214,7 +2221,7 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { OS << " SmallVectorImpl<MCParsedAsmOperand*> &Operands,\n"; OS << " StringRef Mnemonic);\n"; - OS << " OperandMatchResultTy TryCustomParseOperand(\n"; + OS << " OperandMatchResultTy tryCustomParseOperand(\n"; OS << " SmallVectorImpl<MCParsedAsmOperand*> &Operands,\n"; OS << " unsigned MCK);\n\n"; } @@ -2361,7 +2368,7 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { if (HasMnemonicAliases) { OS << " // Process all MnemonicAliases to remap the mnemonic.\n"; - OS << " ApplyMnemonicAliases(Mnemonic, AvailableFeatures);\n\n"; + OS << " applyMnemonicAliases(Mnemonic, AvailableFeatures);\n\n"; } // Emit code to compute the class list for this operand vector. @@ -2403,7 +2410,7 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { OS << " OperandsValid = (it->Classes[i] == " <<"InvalidMatchClass);\n"; OS << " break;\n"; OS << " }\n"; - OS << " if (ValidateOperandClass(Operands[i+1], " + OS << " if (validateOperandClass(Operands[i+1], " "(MatchClassKind)it->Classes[i]))\n"; OS << " continue;\n"; OS << " // If this operand is broken for all of the instances of this\n"; diff --git a/utils/TableGen/CMakeLists.txt b/utils/TableGen/CMakeLists.txt index 9e87515..ac33f99 100644 --- a/utils/TableGen/CMakeLists.txt +++ b/utils/TableGen/CMakeLists.txt @@ -17,6 +17,7 @@ add_tablegen(llvm-tblgen LLVM DAGISelMatcherGen.cpp DAGISelMatcherOpt.cpp DAGISelMatcher.cpp + DFAPacketizerEmitter.cpp DisassemblerEmitter.cpp EDEmitter.cpp FastISelEmitter.cpp diff --git a/utils/TableGen/CodeGenRegisters.cpp b/utils/TableGen/CodeGenRegisters.cpp index 8de4615..3aadf50 100644 --- a/utils/TableGen/CodeGenRegisters.cpp +++ b/utils/TableGen/CodeGenRegisters.cpp @@ -582,6 +582,23 @@ void CodeGenRegBank::addToMaps(CodeGenRegisterClass *RC) { Key2RC.insert(std::make_pair(K, RC)); } +// Create a synthetic sub-class if it is missing. +CodeGenRegisterClass* +CodeGenRegBank::getOrCreateSubClass(const CodeGenRegisterClass *RC, + const CodeGenRegister::Set *Members, + StringRef Name) { + // Synthetic sub-class has the same size and alignment as RC. + CodeGenRegisterClass::Key K(Members, RC->SpillSize, RC->SpillAlignment); + RCKeyMap::const_iterator FoundI = Key2RC.find(K); + if (FoundI != Key2RC.end()) + return FoundI->second; + + // Sub-class doesn't exist, create a new one. + CodeGenRegisterClass *NewRC = new CodeGenRegisterClass(Name, K); + addToMaps(NewRC); + return NewRC; +} + CodeGenRegisterClass *CodeGenRegBank::getRegClass(Record *Def) { if (CodeGenRegisterClass *RC = Def2RC[Def]) return RC; @@ -739,61 +756,102 @@ void CodeGenRegBank::computeDerivedInfo() { computeComposites(); } +// +// Synthesize missing register class intersections. +// +// Make sure that sub-classes of RC exists such that getCommonSubClass(RC, X) +// returns a maximal register class for all X. +// +void CodeGenRegBank::inferCommonSubClass(CodeGenRegisterClass *RC) { + for (unsigned rci = 0, rce = RegClasses.size(); rci != rce; ++rci) { + CodeGenRegisterClass *RC1 = RC; + CodeGenRegisterClass *RC2 = RegClasses[rci]; + if (RC1 == RC2) + continue; + + // Compute the set intersection of RC1 and RC2. + const CodeGenRegister::Set &Memb1 = RC1->getMembers(); + const CodeGenRegister::Set &Memb2 = RC2->getMembers(); + CodeGenRegister::Set Intersection; + std::set_intersection(Memb1.begin(), Memb1.end(), + Memb2.begin(), Memb2.end(), + std::inserter(Intersection, Intersection.begin()), + CodeGenRegister::Less()); + + // Skip disjoint class pairs. + if (Intersection.empty()) + continue; + + // If RC1 and RC2 have different spill sizes or alignments, use the + // larger size for sub-classing. If they are equal, prefer RC1. + if (RC2->SpillSize > RC1->SpillSize || + (RC2->SpillSize == RC1->SpillSize && + RC2->SpillAlignment > RC1->SpillAlignment)) + std::swap(RC1, RC2); + + getOrCreateSubClass(RC1, &Intersection, + RC1->getName() + "_and_" + RC2->getName()); + } +} + +// +// Synthesize missing sub-classes for getSubClassWithSubReg(). +// +// Make sure that the set of registers in RC with a given SubIdx sub-register +// form a register class. Update RC->SubClassWithSubReg. +// +void CodeGenRegBank::inferSubClassWithSubReg(CodeGenRegisterClass *RC) { + // Map SubRegIndex to set of registers in RC supporting that SubRegIndex. + typedef std::map<Record*, CodeGenRegister::Set, LessRecord> SubReg2SetMap; + + // Compute the set of registers supporting each SubRegIndex. + SubReg2SetMap SRSets; + for (CodeGenRegister::Set::const_iterator RI = RC->getMembers().begin(), + RE = RC->getMembers().end(); RI != RE; ++RI) { + const CodeGenRegister::SubRegMap &SRM = (*RI)->getSubRegs(); + for (CodeGenRegister::SubRegMap::const_iterator I = SRM.begin(), + E = SRM.end(); I != E; ++I) + SRSets[I->first].insert(*RI); + } + + // Find matching classes for all SRSets entries. Iterate in SubRegIndex + // numerical order to visit synthetic indices last. + for (unsigned sri = 0, sre = SubRegIndices.size(); sri != sre; ++sri) { + Record *SubIdx = SubRegIndices[sri]; + SubReg2SetMap::const_iterator I = SRSets.find(SubIdx); + // Unsupported SubRegIndex. Skip it. + if (I == SRSets.end()) + continue; + // In most cases, all RC registers support the SubRegIndex. + if (I->second.size() == RC->getMembers().size()) { + RC->setSubClassWithSubReg(SubIdx, RC); + continue; + } + // This is a real subset. See if we have a matching class. + CodeGenRegisterClass *SubRC = + getOrCreateSubClass(RC, &I->second, + RC->getName() + "_with_" + I->first->getName()); + RC->setSubClassWithSubReg(SubIdx, SubRC); + } +} + +// // Infer missing register classes. // -// For every register class RC, make sure that the set of registers in RC with -// a given SubIxx sub-register form a register class. void CodeGenRegBank::computeInferredRegisterClasses() { // When this function is called, the register classes have not been sorted // and assigned EnumValues yet. That means getSubClasses(), // getSuperClasses(), and hasSubClass() functions are defunct. - // Map SubRegIndex to register set. - typedef std::map<Record*, CodeGenRegister::Set, LessRecord> SubReg2SetMap; - // Visit all register classes, including the ones being added by the loop. for (unsigned rci = 0; rci != RegClasses.size(); ++rci) { - CodeGenRegisterClass &RC = *RegClasses[rci]; - - // Compute the set of registers supporting each SubRegIndex. - SubReg2SetMap SRSets; - for (CodeGenRegister::Set::const_iterator RI = RC.getMembers().begin(), - RE = RC.getMembers().end(); RI != RE; ++RI) { - const CodeGenRegister::SubRegMap &SRM = (*RI)->getSubRegs(); - for (CodeGenRegister::SubRegMap::const_iterator I = SRM.begin(), - E = SRM.end(); I != E; ++I) - SRSets[I->first].insert(*RI); - } + CodeGenRegisterClass *RC = RegClasses[rci]; - // Find matching classes for all SRSets entries. Iterate in SubRegIndex - // numerical order to visit synthetic indices last. - for (unsigned sri = 0, sre = SubRegIndices.size(); sri != sre; ++sri) { - Record *SubIdx = SubRegIndices[sri]; - SubReg2SetMap::const_iterator I = SRSets.find(SubIdx); - // Unsupported SubRegIndex. Skip it. - if (I == SRSets.end()) - continue; - // In most cases, all RC registers support the SubRegIndex. - if (I->second.size() == RC.getMembers().size()) { - RC.setSubClassWithSubReg(SubIdx, &RC); - continue; - } + // Synthesize answers for getSubClassWithSubReg(). + inferSubClassWithSubReg(RC); - // This is a real subset. See if we have a matching class. - CodeGenRegisterClass::Key K(&I->second, RC.SpillSize, RC.SpillAlignment); - RCKeyMap::const_iterator FoundI = Key2RC.find(K); - if (FoundI != Key2RC.end()) { - RC.setSubClassWithSubReg(SubIdx, FoundI->second); - continue; - } - - // Class doesn't exist. - CodeGenRegisterClass *NewRC = - new CodeGenRegisterClass(RC.getName() + "_with_" + - I->first->getName(), K); - addToMaps(NewRC); - RC.setSubClassWithSubReg(SubIdx, NewRC); - } + // Synthesize answers for getCommonSubClass(). + inferCommonSubClass(RC); } } diff --git a/utils/TableGen/CodeGenRegisters.h b/utils/TableGen/CodeGenRegisters.h index 4fc34b0..c74cfd6 100644 --- a/utils/TableGen/CodeGenRegisters.h +++ b/utils/TableGen/CodeGenRegisters.h @@ -237,8 +237,15 @@ namespace llvm { // Add RC to *2RC maps. void addToMaps(CodeGenRegisterClass*); + // Create a synthetic sub-class if it is missing. + CodeGenRegisterClass *getOrCreateSubClass(const CodeGenRegisterClass *RC, + const CodeGenRegister::Set *Membs, + StringRef Name); + // Infer missing register classes. void computeInferredRegisterClasses(); + void inferCommonSubClass(CodeGenRegisterClass *RC); + void inferSubClassWithSubReg(CodeGenRegisterClass *RC); // Composite SubRegIndex instances. // Map (SubRegIndex, SubRegIndex) -> SubRegIndex. diff --git a/utils/TableGen/CodeGenTarget.cpp b/utils/TableGen/CodeGenTarget.cpp index 6e1872e..c8d2b00 100644 --- a/utils/TableGen/CodeGenTarget.cpp +++ b/utils/TableGen/CodeGenTarget.cpp @@ -267,6 +267,7 @@ void CodeGenTarget::ComputeInstrsByEnum() const { "DBG_VALUE", "REG_SEQUENCE", "COPY", + "BUNDLE", 0 }; const DenseMap<const Record*, CodeGenInstruction*> &Insts = getInstructions(); diff --git a/utils/TableGen/DFAPacketizerEmitter.cpp b/utils/TableGen/DFAPacketizerEmitter.cpp new file mode 100644 index 0000000..2862d0c --- /dev/null +++ b/utils/TableGen/DFAPacketizerEmitter.cpp @@ -0,0 +1,512 @@ +//===- DFAPacketizerEmitter.cpp - Packetization DFA for a VLIW machine-----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class parses the Schedule.td file and produces an API that can be used +// to reason about whether an instruction can be added to a packet on a VLIW +// architecture. The class internally generates a deterministic finite +// automaton (DFA) that models all possible mappings of machine instructions +// to functional units as instructions are added to a packet. +// +//===----------------------------------------------------------------------===// + +#include "llvm/MC/MCInstrDesc.h" +#include "llvm/MC/MCInstrItineraries.h" +#include "llvm/TableGen/Record.h" +#include "CodeGenTarget.h" +#include "DFAPacketizerEmitter.h" +#include <list> + +using namespace llvm; + +// +// +// State represents the usage of machine resources if the packet contains +// a set of instruction classes. +// +// Specifically, currentState is a set of bit-masks. +// The nth bit in a bit-mask indicates whether the nth resource is being used +// by this state. The set of bit-masks in a state represent the different +// possible outcomes of transitioning to this state. +// For example: consider a two resource architecture: resource L and resource M +// with three instruction classes: L, M, and L_or_M. +// From the initial state (currentState = 0x00), if we add instruction class +// L_or_M we will transition to a state with currentState = [0x01, 0x10]. This +// represents the possible resource states that can result from adding a L_or_M +// instruction +// +// Another way of thinking about this transition is we are mapping a NDFA with +// two states [0x01] and [0x10] into a DFA with a single state [0x01, 0x10]. +// +// +namespace { +class State { + public: + static int currentStateNum; + int stateNum; + bool isInitial; + std::set<unsigned> stateInfo; + + State(); + State(const State &S); + + // + // canAddInsnClass - Returns true if an instruction of type InsnClass is a + // valid transition from this state, i.e., can an instruction of type InsnClass + // be added to the packet represented by this state. + // + // PossibleStates is the set of valid resource states that ensue from valid + // transitions. + // + bool canAddInsnClass(unsigned InsnClass, std::set<unsigned> &PossibleStates); +}; +} // End anonymous namespace. + + +namespace { +struct Transition { + public: + static int currentTransitionNum; + int transitionNum; + State *from; + unsigned input; + State *to; + + Transition(State *from_, unsigned input_, State *to_); +}; +} // End anonymous namespace. + + +// +// Comparators to keep set of states sorted. +// +namespace { +struct ltState { + bool operator()(const State *s1, const State *s2) const; +}; +} // End anonymous namespace. + + +// +// class DFA: deterministic finite automaton for processor resource tracking. +// +namespace { +class DFA { +public: + DFA(); + + // Set of states. Need to keep this sorted to emit the transition table. + std::set<State*, ltState> states; + + // Map from a state to the list of transitions with that state as source. + std::map<State*, SmallVector<Transition*, 16>, ltState> stateTransitions; + State *currentState; + + // Highest valued Input seen. + unsigned LargestInput; + + // + // Modify the DFA. + // + void initialize(); + void addState(State *); + void addTransition(Transition *); + + // + // getTransition - Return the state when a transition is made from + // State From with Input I. If a transition is not found, return NULL. + // + State *getTransition(State *, unsigned); + + // + // isValidTransition: Predicate that checks if there is a valid transition + // from state From on input InsnClass. + // + bool isValidTransition(State *From, unsigned InsnClass); + + // + // writeTable: Print out a table representing the DFA. + // + void writeTableAndAPI(raw_ostream &OS, const std::string &ClassName); +}; +} // End anonymous namespace. + + +// +// Constructors for State, Transition, and DFA +// +State::State() : + stateNum(currentStateNum++), isInitial(false) {} + + +State::State(const State &S) : + stateNum(currentStateNum++), isInitial(S.isInitial), + stateInfo(S.stateInfo) {} + + +Transition::Transition(State *from_, unsigned input_, State *to_) : + transitionNum(currentTransitionNum++), from(from_), input(input_), + to(to_) {} + + +DFA::DFA() : + LargestInput(0) {} + + +bool ltState::operator()(const State *s1, const State *s2) const { + return (s1->stateNum < s2->stateNum); +} + + +// +// canAddInsnClass - Returns true if an instruction of type InsnClass is a +// valid transition from this state i.e., can an instruction of type InsnClass +// be added to the packet represented by this state. +// +// PossibleStates is the set of valid resource states that ensue from valid +// transitions. +// +bool State::canAddInsnClass(unsigned InsnClass, + std::set<unsigned> &PossibleStates) { + // + // Iterate over all resource states in currentState. + // + bool AddedState = false; + + for (std::set<unsigned>::iterator SI = stateInfo.begin(); + SI != stateInfo.end(); ++SI) { + unsigned thisState = *SI; + + // + // Iterate over all possible resources used in InsnClass. + // For ex: for InsnClass = 0x11, all resources = {0x01, 0x10}. + // + + DenseSet<unsigned> VisitedResourceStates; + for (unsigned int j = 0; j < sizeof(InsnClass) * 8; ++j) { + if ((0x1 << j) & InsnClass) { + // + // For each possible resource used in InsnClass, generate the + // resource state if that resource was used. + // + unsigned ResultingResourceState = thisState | (0x1 << j); + // + // Check if the resulting resource state can be accommodated in this + // packet. + // We compute ResultingResourceState OR thisState. + // If the result of the OR is different than thisState, it implies + // that there is at least one resource that can be used to schedule + // InsnClass in the current packet. + // Insert ResultingResourceState into PossibleStates only if we haven't + // processed ResultingResourceState before. + // + if ((ResultingResourceState != thisState) && + (VisitedResourceStates.count(ResultingResourceState) == 0)) { + VisitedResourceStates.insert(ResultingResourceState); + PossibleStates.insert(ResultingResourceState); + AddedState = true; + } + } + } + } + + return AddedState; +} + + +void DFA::initialize() { + currentState->isInitial = true; +} + + +void DFA::addState(State *S) { + assert(!states.count(S) && "State already exists"); + states.insert(S); +} + + +void DFA::addTransition(Transition *T) { + // Update LargestInput. + if (T->input > LargestInput) + LargestInput = T->input; + + // Add the new transition. + stateTransitions[T->from].push_back(T); +} + + +// +// getTransition - Return the state when a transition is made from +// State From with Input I. If a transition is not found, return NULL. +// +State *DFA::getTransition(State *From, unsigned I) { + // Do we have a transition from state From? + if (!stateTransitions.count(From)) + return NULL; + + // Do we have a transition from state From with Input I? + for (SmallVector<Transition*, 16>::iterator VI = + stateTransitions[From].begin(); + VI != stateTransitions[From].end(); ++VI) + if ((*VI)->input == I) + return (*VI)->to; + + return NULL; +} + + +bool DFA::isValidTransition(State *From, unsigned InsnClass) { + return (getTransition(From, InsnClass) != NULL); +} + + +int State::currentStateNum = 0; +int Transition::currentTransitionNum = 0; + +DFAGen::DFAGen(RecordKeeper &R): + TargetName(CodeGenTarget(R).getName()), + allInsnClasses(), Records(R) {} + + +// +// writeTableAndAPI - Print out a table representing the DFA and the +// associated API to create a DFA packetizer. +// +// Format: +// DFAStateInputTable[][2] = pairs of <Input, Transition> for all valid +// transitions. +// DFAStateEntryTable[i] = Index of the first entry in DFAStateInputTable for +// the ith state. +// +// +void DFA::writeTableAndAPI(raw_ostream &OS, const std::string &TargetName) { + std::set<State*, ltState>::iterator SI = states.begin(); + // This table provides a map to the beginning of the transitions for State s + // in DFAStateInputTable. + std::vector<int> StateEntry(states.size()); + + OS << "namespace llvm {\n\n"; + OS << "const int " << TargetName << "DFAStateInputTable[][2] = {\n"; + + // Tracks the total valid transitions encountered so far. It is used + // to construct the StateEntry table. + int ValidTransitions = 0; + for (unsigned i = 0; i < states.size(); ++i, ++SI) { + StateEntry[i] = ValidTransitions; + for (unsigned j = 0; j <= LargestInput; ++j) { + assert (((*SI)->stateNum == (int) i) && "Mismatch in state numbers"); + if (!isValidTransition(*SI, j)) + continue; + + OS << "{" << j << ", " + << getTransition(*SI, j)->stateNum + << "}, "; + ++ValidTransitions; + } + + // If there are no valid transitions from this stage, we need a sentinel + // transition. + if (ValidTransitions == StateEntry[i]) + OS << "{-1, -1},"; + + OS << "\n"; + } + OS << "};\n\n"; + OS << "const unsigned int " << TargetName << "DFAStateEntryTable[] = {\n"; + + // Multiply i by 2 since each entry in DFAStateInputTable is a set of + // two numbers. + for (unsigned i = 0; i < states.size(); ++i) + OS << StateEntry[i] << ", "; + + OS << "\n};\n"; + OS << "} // namespace\n"; + + + // + // Emit DFA Packetizer tables if the target is a VLIW machine. + // + std::string SubTargetClassName = TargetName + "GenSubtargetInfo"; + OS << "\n" << "#include \"llvm/CodeGen/DFAPacketizer.h\"\n"; + OS << "namespace llvm {\n"; + OS << "DFAPacketizer *" << SubTargetClassName << "::" + << "createDFAPacketizer(const InstrItineraryData *IID) const {\n" + << " return new DFAPacketizer(IID, " << TargetName + << "DFAStateInputTable, " << TargetName << "DFAStateEntryTable);\n}\n\n"; + OS << "} // End llvm namespace \n"; +} + + +// +// collectAllInsnClasses - Populate allInsnClasses which is a set of units +// used in each stage. +// +void DFAGen::collectAllInsnClasses(const std::string &Name, + Record *ItinData, + unsigned &NStages, + raw_ostream &OS) { + // Collect processor itineraries. + std::vector<Record*> ProcItinList = + Records.getAllDerivedDefinitions("ProcessorItineraries"); + + // If just no itinerary then don't bother. + if (ProcItinList.size() < 2) + return; + std::map<std::string, unsigned> NameToBitsMap; + + // Parse functional units for all the itineraries. + for (unsigned i = 0, N = ProcItinList.size(); i < N; ++i) { + Record *Proc = ProcItinList[i]; + std::vector<Record*> FUs = Proc->getValueAsListOfDefs("FU"); + + // Convert macros to bits for each stage. + for (unsigned i = 0, N = FUs.size(); i < N; ++i) + NameToBitsMap[FUs[i]->getName()] = (unsigned) (1U << i); + } + + const std::vector<Record*> &StageList = + ItinData->getValueAsListOfDefs("Stages"); + + // The number of stages. + NStages = StageList.size(); + + // For each unit. + unsigned UnitBitValue = 0; + + // Compute the bitwise or of each unit used in this stage. + for (unsigned i = 0; i < NStages; ++i) { + const Record *Stage = StageList[i]; + + // Get unit list. + const std::vector<Record*> &UnitList = + Stage->getValueAsListOfDefs("Units"); + + for (unsigned j = 0, M = UnitList.size(); j < M; ++j) { + // Conduct bitwise or. + std::string UnitName = UnitList[j]->getName(); + assert(NameToBitsMap.count(UnitName)); + UnitBitValue |= NameToBitsMap[UnitName]; + } + + if (UnitBitValue != 0) + allInsnClasses.insert(UnitBitValue); + } +} + + +// +// Run the worklist algorithm to generate the DFA. +// +void DFAGen::run(raw_ostream &OS) { + EmitSourceFileHeader("Target DFA Packetizer Tables", OS); + + // Collect processor iteraries. + std::vector<Record*> ProcItinList = + Records.getAllDerivedDefinitions("ProcessorItineraries"); + + // + // Collect the instruction classes. + // + for (unsigned i = 0, N = ProcItinList.size(); i < N; i++) { + Record *Proc = ProcItinList[i]; + + // Get processor itinerary name. + const std::string &Name = Proc->getName(); + + // Skip default. + if (Name == "NoItineraries") + continue; + + // Sanity check for at least one instruction itinerary class. + unsigned NItinClasses = + Records.getAllDerivedDefinitions("InstrItinClass").size(); + if (NItinClasses == 0) + return; + + // Get itinerary data list. + std::vector<Record*> ItinDataList = Proc->getValueAsListOfDefs("IID"); + + // Collect instruction classes for all itinerary data. + for (unsigned j = 0, M = ItinDataList.size(); j < M; j++) { + Record *ItinData = ItinDataList[j]; + unsigned NStages; + collectAllInsnClasses(Name, ItinData, NStages, OS); + } + } + + + // + // Run a worklist algorithm to generate the DFA. + // + DFA D; + State *Initial = new State; + Initial->isInitial = true; + Initial->stateInfo.insert(0x0); + D.addState(Initial); + SmallVector<State*, 32> WorkList; + std::map<std::set<unsigned>, State*> Visited; + + WorkList.push_back(Initial); + + // + // Worklist algorithm to create a DFA for processor resource tracking. + // C = {set of InsnClasses} + // Begin with initial node in worklist. Initial node does not have + // any consumed resources, + // ResourceState = 0x0 + // Visited = {} + // While worklist != empty + // S = first element of worklist + // For every instruction class C + // if we can accommodate C in S: + // S' = state with resource states = {S Union C} + // Add a new transition: S x C -> S' + // If S' is not in Visited: + // Add S' to worklist + // Add S' to Visited + // + while (!WorkList.empty()) { + State *current = WorkList.pop_back_val(); + for (DenseSet<unsigned>::iterator CI = allInsnClasses.begin(), + CE = allInsnClasses.end(); CI != CE; ++CI) { + unsigned InsnClass = *CI; + + std::set<unsigned> NewStateResources; + // + // If we haven't already created a transition for this input + // and the state can accommodate this InsnClass, create a transition. + // + if (!D.getTransition(current, InsnClass) && + current->canAddInsnClass(InsnClass, NewStateResources)) { + State *NewState = NULL; + + // + // If we have seen this state before, then do not create a new state. + // + // + std::map<std::set<unsigned>, State*>::iterator VI; + if ((VI = Visited.find(NewStateResources)) != Visited.end()) + NewState = VI->second; + else { + NewState = new State; + NewState->stateInfo = NewStateResources; + D.addState(NewState); + Visited[NewStateResources] = NewState; + WorkList.push_back(NewState); + } + + Transition *NewTransition = new Transition(current, InsnClass, + NewState); + D.addTransition(NewTransition); + } + } + } + + // Print out the table. + D.writeTableAndAPI(OS, TargetName); +} diff --git a/utils/TableGen/DFAPacketizerEmitter.h b/utils/TableGen/DFAPacketizerEmitter.h new file mode 100644 index 0000000..8f47dde --- /dev/null +++ b/utils/TableGen/DFAPacketizerEmitter.h @@ -0,0 +1,54 @@ +//===- DFAPacketizerEmitter.h - Packetization DFA for a VLIW machine-------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class parses the Schedule.td file and produces an API that can be used +// to reason about whether an instruction can be added to a packet on a VLIW +// architecture. The class internally generates a deterministic finite +// automaton (DFA) that models all possible mappings of machine instructions +// to functional units as instructions are added to a packet. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/TableGen/TableGenBackend.h" +#include <map> +#include <string> + +namespace llvm { +// +// class DFAGen: class that generates and prints out the DFA for resource +// tracking. +// +class DFAGen : public TableGenBackend { +private: + std::string TargetName; + // + // allInsnClasses is the set of all possible resources consumed by an + // InstrStage. + // + DenseSet<unsigned> allInsnClasses; + RecordKeeper &Records; + +public: + DFAGen(RecordKeeper &R); + + // + // collectAllInsnClasses: Populate allInsnClasses which is a set of units + // used in each stage. + // + void collectAllInsnClasses(const std::string &Name, + Record *ItinData, + unsigned &NStages, + raw_ostream &OS); + + void run(raw_ostream &OS); +}; +} diff --git a/utils/TableGen/EDEmitter.cpp b/utils/TableGen/EDEmitter.cpp index 1953dad..6c3cae2 100644 --- a/utils/TableGen/EDEmitter.cpp +++ b/utils/TableGen/EDEmitter.cpp @@ -576,6 +576,8 @@ static int ARMFlagFromOpName(LiteralConstantEmitter *type, REG("VecListThreeD"); REG("VecListFourD"); REG("VecListTwoQ"); + REG("VecListOneDAllLanes"); + REG("VecListTwoDAllLanes"); IMM("i32imm"); IMM("i32imm_hilo16"); @@ -608,6 +610,14 @@ static int ARMFlagFromOpName(LiteralConstantEmitter *type, IMM("nImmSplatI64"); IMM("nImmVMOVI32"); IMM("nImmVMOVF32"); + IMM("imm8"); + IMM("imm16"); + IMM("imm32"); + IMM("imm1_7"); + IMM("imm1_15"); + IMM("imm1_31"); + IMM("imm0_1"); + IMM("imm0_3"); IMM("imm0_7"); IMM("imm0_15"); IMM("imm0_255"); @@ -746,7 +756,7 @@ static void ARMPopulateOperands( errs() << "Operand type: " << rec.getName() << '\n'; errs() << "Operand name: " << operandInfo.Name << '\n'; errs() << "Instruction name: " << inst.TheDef->getName() << '\n'; - llvm_unreachable("Unhandled type"); + throw("Unhandled type in EDEmitter"); } } } diff --git a/utils/TableGen/LLVMBuild.txt b/utils/TableGen/LLVMBuild.txt index b2a8ac6..b0081eb 100644 --- a/utils/TableGen/LLVMBuild.txt +++ b/utils/TableGen/LLVMBuild.txt @@ -20,4 +20,3 @@ type = BuildTool name = tblgen parent = BuildTools required_libraries = Support TableGen - diff --git a/utils/TableGen/SubtargetEmitter.cpp b/utils/TableGen/SubtargetEmitter.cpp index 3a6ff4e..34cd9a9 100644 --- a/utils/TableGen/SubtargetEmitter.cpp +++ b/utils/TableGen/SubtargetEmitter.cpp @@ -711,9 +711,13 @@ void SubtargetEmitter::run(raw_ostream &OS) { std::string ClassName = Target + "GenSubtargetInfo"; OS << "namespace llvm {\n"; + OS << "class DFAPacketizer;\n"; OS << "struct " << ClassName << " : public TargetSubtargetInfo {\n" << " explicit " << ClassName << "(StringRef TT, StringRef CPU, " << "StringRef FS);\n" + << "public:\n" + << " DFAPacketizer *createDFAPacketizer(const InstrItineraryData *IID)" + << " const;\n" << "};\n"; OS << "} // End llvm namespace \n"; diff --git a/utils/TableGen/TableGen.cpp b/utils/TableGen/TableGen.cpp index 12ebc57..3899a41 100644 --- a/utils/TableGen/TableGen.cpp +++ b/utils/TableGen/TableGen.cpp @@ -16,6 +16,7 @@ #include "CallingConvEmitter.h" #include "CodeEmitterGen.h" #include "DAGISelEmitter.h" +#include "DFAPacketizerEmitter.h" #include "DisassemblerEmitter.h" #include "EDEmitter.h" #include "FastISelEmitter.h" @@ -47,6 +48,7 @@ enum ActionType { GenPseudoLowering, GenCallingConv, GenDAGISel, + GenDFAPacketizer, GenFastISel, GenSubtarget, GenIntrinsic, @@ -79,6 +81,8 @@ namespace { "Generate assembly instruction matcher"), clEnumValN(GenDAGISel, "gen-dag-isel", "Generate a DAG instruction selector"), + clEnumValN(GenDFAPacketizer, "gen-dfa-packetizer", + "Generate DFA Packetizer for VLIW targets"), clEnumValN(GenFastISel, "gen-fast-isel", "Generate a \"fast\" instruction selector"), clEnumValN(GenSubtarget, "gen-subtarget", @@ -134,6 +138,9 @@ public: case GenDAGISel: DAGISelEmitter(Records).run(OS); break; + case GenDFAPacketizer: + DFAGen(Records).run(OS); + break; case GenFastISel: FastISelEmitter(Records).run(OS); break; diff --git a/utils/buildit/GNUmakefile b/utils/buildit/GNUmakefile index 470ee76..a362fa6 100644 --- a/utils/buildit/GNUmakefile +++ b/utils/buildit/GNUmakefile @@ -32,7 +32,7 @@ DSTROOT = $(OBJROOT)/../dst ####################################################################### -PREFIX = /Developer/usr/local +PREFIX = /usr/local # Unless assertions are forced on in the GMAKE command line, disable them. ifndef ENABLE_ASSERTIONS diff --git a/utils/lit/lit/TestRunner.py b/utils/lit/lit/TestRunner.py index f5f7c19..4b2d626 100644 --- a/utils/lit/lit/TestRunner.py +++ b/utils/lit/lit/TestRunner.py @@ -23,6 +23,43 @@ kUseCloseFDs = not kIsWindows # Use temporary files to replace /dev/null on Windows. kAvoidDevNull = kIsWindows +def RemoveForce(f): + try: + os.remove(f) + except OSError: + pass + +def WinRename(f_o, f_n): + import time + retry_cnt = 256 + while (True): + try: + os.rename(f_o, f_n) + break + except WindowsError, (winerror, strerror): + retry_cnt = retry_cnt - 1 + if retry_cnt <= 0: + raise + elif winerror == 32: # ERROR_SHARING_VIOLATION + time.sleep(0.01) + else: + raise + +def WinWaitReleased(f): + import random + t = "%s%06d" % (f, random.randint(0, 999999)) + RemoveForce(t) + try: + WinRename(f, t) # rename + WinRename(t, f) # restore + except WindowsError, (winerror, strerror): + if winerror in (2, 3): + # 2: ERROR_FILE_NOT_FOUND + # 3: ERROR_PATH_NOT_FOUND + pass + else: + raise + def executeCommand(command, cwd=None, env=None): p = subprocess.Popen(command, cwd=cwd, stdin=subprocess.PIPE, @@ -68,6 +105,7 @@ def executeShCmd(cmd, cfg, cwd, results): input = subprocess.PIPE stderrTempFiles = [] opened_files = [] + written_files = [] named_temp_files = [] # To avoid deadlock, we use a single stderr stream for piped # output. This is null until we have seen some output using @@ -124,6 +162,8 @@ def executeShCmd(cmd, cfg, cwd, results): if r[1] == 'a': r[2].seek(0, 2) opened_files.append(r[2]) + if r[1] in 'aw': + written_files.append(r[0]) result = r[2] final_redirects.append(result) @@ -224,12 +264,14 @@ def executeShCmd(cmd, cfg, cwd, results): else: exitCode = res + # Make sure written_files is released by other (child) processes. + if (kIsWindows): + for f in written_files: + WinWaitReleased(f) + # Remove any named temporary files we created. for f in named_temp_files: - try: - os.remove(f) - except OSError: - pass + RemoveForce(f) if cmd.negate: exitCode = not exitCode diff --git a/utils/llvm-build/llvmbuild/componentinfo.py b/utils/llvm-build/llvmbuild/componentinfo.py index b9a2d4f..230ae21 100644 --- a/utils/llvm-build/llvmbuild/componentinfo.py +++ b/utils/llvm-build/llvmbuild/componentinfo.py @@ -42,6 +42,13 @@ class ComponentInfo(object): self.parent_instance = None self.children = [] + # The original source path. + self._source_path = None + + # A flag to mark "special" components which have some amount of magic + # handling (generally based on command line options). + self._is_special_group = False + def set_parent_instance(self, parent): assert parent.name == self.parent, "Unexpected parent!" self.parent_instance = parent @@ -138,6 +145,23 @@ class LibraryComponentInfo(ComponentInfo): def get_library_name(self): return self.library_name or self.name + def get_prefixed_library_name(self): + """ + get_prefixed_library_name() -> str + + Return the library name prefixed by the project name. This is generally + what the library name will be on disk. + """ + + basename = self.get_library_name() + + # FIXME: We need to get the prefix information from an explicit project + # object, or something. + if basename in ('gtest', 'gtest_main'): + return basename + + return 'LLVM%s' % basename + def get_llvmconfig_component_name(self): return self.get_library_name().lower() @@ -177,7 +201,7 @@ class LibraryGroupComponentInfo(ComponentInfo): print >>result, 'type = %s' % self.type_name print >>result, 'name = %s' % self.name print >>result, 'parent = %s' % self.parent - if self.required_libraries: + if self.required_libraries and not self._is_special_group: print >>result, 'required_libraries = %s' % ' '.join( self.required_libraries) if self.add_to_library_groups: @@ -357,6 +381,16 @@ def load_from_path(path, subpath): parser = ConfigParser.RawConfigParser() parser.read(path) + # Extract the common section. + if parser.has_section("common"): + common = IniFormatParser(parser.items("common")) + parser.remove_section("common") + else: + common = IniFormatParser({}) + + return common, _read_components_from_parser(parser, path, subpath) + +def _read_components_from_parser(parser, path, subpath): # We load each section which starts with 'component' as a distinct component # description (so multiple components can be described in one file). for section in parser.sections(): @@ -390,4 +424,5 @@ def load_from_path(path, subpath): fatal("unable to load component %r in %r: %s" % ( section, path, e.message)) + info._source_path = path yield info diff --git a/utils/llvm-build/llvmbuild/main.py b/utils/llvm-build/llvmbuild/main.py index 21cf392..36bca87 100644 --- a/utils/llvm-build/llvmbuild/main.py +++ b/utils/llvm-build/llvmbuild/main.py @@ -1,3 +1,4 @@ +import StringIO import os import sys @@ -63,31 +64,25 @@ def make_install_dir(path): class LLVMProjectInfo(object): @staticmethod def load_infos_from_path(llvmbuild_source_root): - # FIXME: Implement a simple subpath file list cache, so we don't restat - # directories we have already traversed. + def recurse(subpath): + # Load the LLVMBuild file. + llvmbuild_path = os.path.join(llvmbuild_source_root + subpath, + 'LLVMBuild.txt') + if not os.path.exists(llvmbuild_path): + fatal("missing LLVMBuild.txt file at: %r" % (llvmbuild_path,)) + + # Parse the components from it. + common,info_iter = componentinfo.load_from_path(llvmbuild_path, + subpath) + for info in info_iter: + yield info - # First, discover all the LLVMBuild.txt files. - # - # FIXME: We would like to use followlinks=True here, but that isn't - # compatible with Python 2.4. Instead, we will either have to special - # case projects we would expect to possibly be linked to, or implement - # our own walk that can follow links. For now, it doesn't matter since - # we haven't picked up the LLVMBuild system in any other LLVM projects. - for dirpath,dirnames,filenames in os.walk(llvmbuild_source_root): - # If there is no LLVMBuild.txt file in a directory, we don't recurse - # past it. This is a simple way to prune our search, although it - # makes it easy for users to add LLVMBuild.txt files in places they - # won't be seen. - if 'LLVMBuild.txt' not in filenames: - del dirnames[:] - continue + # Recurse into the specified subdirectories. + for subdir in common.get_list("subdirectories"): + for item in recurse(os.path.join(subpath, subdir)): + yield item - # Otherwise, load the LLVMBuild file in this directory. - assert dirpath.startswith(llvmbuild_source_root) - subpath = '/' + dirpath[len(llvmbuild_source_root)+1:] - llvmbuild_path = os.path.join(dirpath, 'LLVMBuild.txt') - for info in componentinfo.load_from_path(llvmbuild_path, subpath): - yield info + return recurse("/") @staticmethod def load_from_path(source_root, llvmbuild_source_root): @@ -213,14 +208,45 @@ class LLVMProjectInfo(object): info_basedir[ci.subpath] = info_basedir.get(ci.subpath, []) + [ci] + # Compute the list of subdirectories to scan. + subpath_subdirs = {} + for ci in self.component_infos: + # Ignore root components. + if ci.subpath == '/': + continue + + # Otherwise, append this subpath to the parent list. + parent_path = os.path.dirname(ci.subpath) + subpath_subdirs[parent_path] = parent_list = subpath_subdirs.get( + parent_path, set()) + parent_list.add(os.path.basename(ci.subpath)) + # Generate the build files. for subpath, infos in info_basedir.items(): # Order the components by name to have a canonical ordering. infos.sort(key = lambda ci: ci.name) # Format the components into llvmbuild fragments. - fragments = filter(None, [ci.get_llvmbuild_fragment() - for ci in infos]) + fragments = [] + + # Add the common fragments. + subdirectories = subpath_subdirs.get(subpath) + if subdirectories: + fragment = """\ +subdirectories = %s +""" % (" ".join(sorted(subdirectories)),) + fragments.append(("common", fragment)) + + # Add the component fragments. + num_common_fragments = len(fragments) + for ci in infos: + fragment = ci.get_llvmbuild_fragment() + if fragment is None: + continue + + name = "component_%d" % (len(fragments) - num_common_fragments) + fragments.append((name, fragment)) + if not fragments: continue @@ -231,7 +257,22 @@ class LLVMProjectInfo(object): if not os.path.exists(directory_path): os.makedirs(directory_path) - # Create the LLVMBuild file. + # In an effort to preserve comments (which aren't parsed), read in + # the original file and extract the comments. We only know how to + # associate comments that prefix a section name. + f = open(infos[0]._source_path) + comments_map = {} + comment_block = "" + for ln in f: + if ln.startswith(';'): + comment_block += ln + elif ln.startswith('[') and ln.endswith(']\n'): + comments_map[ln[1:-2]] = comment_block + else: + comment_block = "" + f.close() + + # Create the LLVMBuild fil[e. file_path = os.path.join(directory_path, 'LLVMBuild.txt') f = open(file_path, "w") @@ -259,10 +300,16 @@ class LLVMProjectInfo(object): ;===------------------------------------------------------------------------===; """ % header_string - for i,fragment in enumerate(fragments): - print >>f, '[component_%d]' % i + # Write out each fragment.each component fragment. + for name,fragment in fragments: + comment = comments_map.get(name) + if comment is not None: + f.write(comment) + print >>f, "[%s]" % name f.write(fragment) - print >>f + if fragment is not fragments[-1][1]: + print >>f + f.close() def write_library_table(self, output_path): @@ -282,7 +329,7 @@ class LLVMProjectInfo(object): # Get the library name, or None for LibraryGroups. if c.type_name == 'Library': - library_name = c.get_library_name() + library_name = c.get_prefixed_library_name() else: library_name = None @@ -344,9 +391,7 @@ class LLVMProjectInfo(object): if library_name is None: library_name_as_cstr = '0' else: - # If we had a project level component, we could derive the - # library prefix. - library_name_as_cstr = '"libLLVM%s.a"' % library_name + library_name_as_cstr = '"lib%s.a"' % library_name print >>f, ' { "%s", %s, { %s } },' % ( name, library_name_as_cstr, ', '.join('"%s"' % dep @@ -354,6 +399,37 @@ class LLVMProjectInfo(object): print >>f, '};' f.close() + def get_required_libraries_for_component(self, ci, traverse_groups = False): + """ + get_required_libraries_for_component(component_info) -> iter + + Given a Library component info descriptor, return an iterator over all + of the directly required libraries for linking with this component. If + traverse_groups is True, then library and target groups will be + traversed to include their required libraries. + """ + + assert ci.type_name in ('Library', 'LibraryGroup', 'TargetGroup') + + for name in ci.required_libraries: + # Get the dependency info. + dep = self.component_info_map[name] + + # If it is a library, yield it. + if dep.type_name == 'Library': + yield dep + continue + + # Otherwise if it is a group, yield or traverse depending on what + # was requested. + if dep.type_name in ('LibraryGroup', 'TargetGroup'): + if not traverse_groups: + yield dep + continue + + for res in self.get_required_libraries_for_component(dep, True): + yield res + def get_fragment_dependencies(self): """ get_fragment_dependencies() -> iter @@ -366,9 +442,15 @@ class LLVMProjectInfo(object): # Construct a list of all the dependencies of the Makefile fragment # itself. These include all the LLVMBuild files themselves, as well as # all of our own sources. + # + # Many components may come from the same file, so we make sure to unique + # these. + build_paths = set() for ci in self.component_infos: - yield os.path.join(self.source_root, ci.subpath[1:], - 'LLVMBuild.txt') + p = os.path.join(self.source_root, ci.subpath[1:], 'LLVMBuild.txt') + if p not in build_paths: + yield p + build_paths.add(p) # Gather the list of necessary sources by just finding all loaded # modules that are inside the LLVM source tree. @@ -447,6 +529,24 @@ configure_file(\"%s\" ${CMAKE_CURRENT_BINARY_DIR}/DummyConfigureOutput)""" % ( cmake_quote_path(dep),) + # Write the properties we use to encode the required library dependency + # information in a form CMake can easily use directly. + print >>f, """ +# Explicit library dependency information. +# +# The following property assignments effectively create a map from component +# names to required libraries, in a way that is easily accessed from CMake.""" + for ci in self.ordered_component_infos: + # We only write the information for libraries currently. + if ci.type_name != 'Library': + continue + + print >>f, """\ +set_property(GLOBAL PROPERTY LLVMBUILD_LIB_DEPS_%s %s)""" % ( + ci.get_prefixed_library_name(), " ".join(sorted( + dep.get_prefixed_library_name() + for dep in self.get_required_libraries_for_component(ci)))) + f.close() def write_make_fragment(self, output_path): @@ -590,6 +690,7 @@ def add_magic_target_components(parser, project, opts): fatal("special component %r must have empty %r list" % ( name, 'add_to_library_groups')) + info._is_special_group = True return info info_map = dict((ci.name, ci) for ci in project.component_infos) diff --git a/utils/release/tag.sh b/utils/release/tag.sh index d2d9911..80da47a 100755 --- a/utils/release/tag.sh +++ b/utils/release/tag.sh @@ -25,6 +25,7 @@ function usage() { echo " " echo " -release <num> The version number of the release" echo " -rc <num> The release candidate number" + echo " -final Tag final release candidate" } function tag_version() { @@ -45,10 +46,10 @@ function tag_release_candidate() { 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 - if ! svn ls $base_url/$proj/tags/RELEASE_$release/rc$rc > /dev/null 2>&1 ; then + if ! svn ls $base_url/$proj/tags/RELEASE_$release/$rc > /dev/null 2>&1 ; then svn copy -m "Creating release candidate $rc from release_$release branch" \ $base_url/$proj/branches/release_$release \ - $base_url/$proj/tags/RELEASE_$release/rc$rc + $base_url/$proj/tags/RELEASE_$release/$rc fi done set +x @@ -62,7 +63,10 @@ while [ $# -gt 0 ]; do ;; -rc | --rc ) shift - rc=$1 + rc="rc$1" + ;; + -final | --final ) + rc="final" ;; -h | --help | -help ) usage diff --git a/utils/release/test-release.sh b/utils/release/test-release.sh index c0f283d..6ec2861 100755 --- a/utils/release/test-release.sh +++ b/utils/release/test-release.sh @@ -42,6 +42,7 @@ function usage() { echo "" echo " -release X.Y The release number to test." echo " -rc NUM The pre-release candidate number." + echo " -final The final release candidate." echo " -j NUM Number of compile jobs to run. [default: 3]" echo " -build-dir DIR Directory to perform testing in. [default: pwd]" echo " -no-checkout Don't checkout the sources from SVN." @@ -64,7 +65,10 @@ while [ $# -gt 0 ]; do ;; -rc | --rc | -RC | --RC ) shift - RC=$1 + RC="rc$1" + ;; + -final | --final ) + RC=final ;; -j* ) NumJobs="`echo $1 | sed -e 's,-j\([0-9]*\),\1,g'`" @@ -142,7 +146,7 @@ if [ -z "$NumJobs" ]; then fi # Go to the build directory (may be different from CWD) -BuildDir=$BuildDir/rc$RC +BuildDir=$BuildDir/$RC mkdir -p $BuildDir cd $BuildDir @@ -177,7 +181,7 @@ function check_valid_urls() { for proj in $projects ; do echo "# Validating $proj SVN URL" - if ! svn ls $Base_url/$proj/tags/RELEASE_$Release_no_dot/rc$RC > /dev/null 2>&1 ; then + if ! svn ls $Base_url/$proj/tags/RELEASE_$Release_no_dot/$RC > /dev/null 2>&1 ; then echo "llvm $Release release candidate $RC doesn't exist!" exit 1 fi @@ -190,7 +194,7 @@ function export_sources() { for proj in $projects ; do echo "# Exporting $proj $Release-RC$RC sources" - if ! svn export -q $Base_url/$proj/tags/RELEASE_$Release_no_dot/rc$RC $proj.src ; then + if ! svn export -q $Base_url/$proj/tags/RELEASE_$Release_no_dot/$RC $proj.src ; then echo "error: failed to export $proj project" exit 1 fi @@ -238,7 +242,7 @@ function configure_llvmCore() { echo "# Using C++ compiler: $cxx_compiler" cd $ObjDir - echo "# Configuring llvm $Release-rc$RC $Flavor" + echo "# Configuring llvm $Release-$RC $Flavor" echo "# $BuildDir/llvm.src/configure --prefix=$InstallDir \ --enable-optimized=$Optimized \ --enable-assertions=$Assertions" @@ -262,12 +266,12 @@ function build_llvmCore() { fi cd $ObjDir - echo "# Compiling llvm $Release-rc$RC $Flavor" + echo "# Compiling llvm $Release-$RC $Flavor" echo "# ${MAKE} -j $NumJobs VERBOSE=1 $ExtraOpts" ${MAKE} -j $NumJobs VERBOSE=1 $ExtraOpts \ 2>&1 | tee $LogDir/llvm.make-Phase$Phase-$Flavor.log - echo "# Installing llvm $Release-rc$RC $Flavor" + echo "# Installing llvm $Release-$RC $Flavor" echo "# ${MAKE} install" ${MAKE} install \ 2>&1 | tee $LogDir/llvm.install-Phase$Phase-$Flavor.log @@ -285,7 +289,7 @@ function build_dragonegg() { echo "# Targeted compiler: $gcc_compiler" cd $DragonEggObjDir - echo "# Compiling phase $Phase dragonegg $Release-rc$RC $Flavor" + echo "# Compiling phase $Phase dragonegg $Release-$RC $Flavor" echo -n "# CXX=$cxx_compiler TOP_DIR=$TOP_DIR GCC=$gcc_compiler " echo -n "LLVM_CONFIG=$LLVM_CONFIG ${MAKE} -f $TOP_DIR/Makefile " echo "-j $NumJobs VERBOSE=1" @@ -331,7 +335,7 @@ for Flavor in $Flavors ; do echo "" echo "" echo "********************************************************************************" - echo " Release: $Release-rc$RC" + echo " Release: $Release-$RC" echo " Build: $Flavor" echo " System Info: " echo " `uname -a`" @@ -341,21 +345,21 @@ for Flavor in $Flavors ; do c_compiler="$CC" cxx_compiler="$CXX" - llvmCore_phase1_objdir=$BuildDir/Phase1/$Flavor/llvmCore-$Release-rc$RC.obj - llvmCore_phase1_installdir=$BuildDir/Phase1/$Flavor/llvmCore-$Release-rc$RC.install - dragonegg_phase1_objdir=$BuildDir/Phase1/$Flavor/DragonEgg-$Release-rc$RC.obj + llvmCore_phase1_objdir=$BuildDir/Phase1/$Flavor/llvmCore-$Release-$RC.obj + llvmCore_phase1_installdir=$BuildDir/Phase1/$Flavor/llvmCore-$Release-$RC.install + dragonegg_phase1_objdir=$BuildDir/Phase1/$Flavor/DragonEgg-$Release-$RC.obj - llvmCore_phase2_objdir=$BuildDir/Phase2/$Flavor/llvmCore-$Release-rc$RC.obj - llvmCore_phase2_installdir=$BuildDir/Phase2/$Flavor/llvmCore-$Release-rc$RC.install - llvmCore_de_phase2_objdir=$BuildDir/Phase2/$Flavor/llvmCore-DragonEgg-$Release-rc$RC.obj - llvmCore_de_phase2_installdir=$BuildDir/Phase2/$Flavor/llvmCore-DragonEgg-$Release-rc$RC.install - dragonegg_phase2_objdir=$BuildDir/Phase2/$Flavor/DragonEgg-$Release-rc$RC.obj + llvmCore_phase2_objdir=$BuildDir/Phase2/$Flavor/llvmCore-$Release-$RC.obj + llvmCore_phase2_installdir=$BuildDir/Phase2/$Flavor/llvmCore-$Release-$RC.install + llvmCore_de_phase2_objdir=$BuildDir/Phase2/$Flavor/llvmCore-DragonEgg-$Release-$RC.obj + llvmCore_de_phase2_installdir=$BuildDir/Phase2/$Flavor/llvmCore-DragonEgg-$Release-$RC.install + dragonegg_phase2_objdir=$BuildDir/Phase2/$Flavor/DragonEgg-$Release-$RC.obj - llvmCore_phase3_objdir=$BuildDir/Phase3/$Flavor/llvmCore-$Release-rc$RC.obj - llvmCore_phase3_installdir=$BuildDir/Phase3/$Flavor/llvmCore-$Release-rc$RC.install - llvmCore_de_phase3_objdir=$BuildDir/Phase3/$Flavor/llvmCore-DragonEgg-$Release-rc$RC.obj - llvmCore_de_phase3_installdir=$BuildDir/Phase3/$Flavor/llvmCore-DragonEgg-$Release-rc$RC.install - dragonegg_phase3_objdir=$BuildDir/Phase3/$Flavor/DragonEgg-$Release-rc$RC.obj + llvmCore_phase3_objdir=$BuildDir/Phase3/$Flavor/llvmCore-$Release-$RC.obj + llvmCore_phase3_installdir=$BuildDir/Phase3/$Flavor/llvmCore-$Release-$RC.install + llvmCore_de_phase3_objdir=$BuildDir/Phase3/$Flavor/llvmCore-DragonEgg-$Release-$RC.obj + llvmCore_de_phase3_installdir=$BuildDir/Phase3/$Flavor/llvmCore-DragonEgg-$Release-$RC.install + dragonegg_phase3_objdir=$BuildDir/Phase3/$Flavor/DragonEgg-$Release-$RC.obj rm -rf $llvmCore_phase1_objdir rm -rf $llvmCore_phase1_installdir @@ -494,7 +498,7 @@ for Flavor in $Flavors ; do test_llvmCore 1 $Flavor $llvmCore_phase1_objdir fi done -) 2>&1 | tee $LogDir/testing.$Release-rc$RC.log +) 2>&1 | tee $LogDir/testing.$Release-$RC.log set +e diff --git a/utils/unittest/CMakeLists.txt b/utils/unittest/CMakeLists.txt index d882de2..73491f0 100644 --- a/utils/unittest/CMakeLists.txt +++ b/utils/unittest/CMakeLists.txt @@ -40,11 +40,3 @@ add_llvm_library(gtest add_llvm_library(gtest_main UnitTestMain/TestMain.cpp ) - -add_llvm_library_dependencies(gtest - LLVMSupport - ) - -add_llvm_library_dependencies(gtest_main - gtest - ) diff --git a/utils/unittest/UnitTestMain/Makefile b/utils/unittest/UnitTestMain/Makefile index 3082779..7bcb724 100644 --- a/utils/unittest/UnitTestMain/Makefile +++ b/utils/unittest/UnitTestMain/Makefile @@ -11,7 +11,7 @@ LEVEL = ../../.. include $(LEVEL)/Makefile.config -LIBRARYNAME = UnitTestMain +LIBRARYNAME = gtest_main BUILD_ARCHIVE = 1 REQUIRES_RTTI = 1 diff --git a/utils/unittest/googletest/Makefile b/utils/unittest/googletest/Makefile index 21b29ff..22c8f36 100644 --- a/utils/unittest/googletest/Makefile +++ b/utils/unittest/googletest/Makefile @@ -11,7 +11,7 @@ LEVEL := ../../.. include $(LEVEL)/Makefile.config -LIBRARYNAME = GoogleTest +LIBRARYNAME = gtest BUILD_ARCHIVE = 1 REQUIRES_RTTI = 1 |