//===- 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 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 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& 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 states; // Map from a state to the list of transitions with that state as source std::map, 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& PossibleStates) { // // Iterate over all resource states in currentState // bool AddedState = false; for (std::set::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 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::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 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::iterator SI = states.begin(); // This table provides a map to the beginning of the transitions for State s // in DFAStateInputTable i.e., std::vector 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 ProcItinList = Records.getAllDerivedDefinitions("ProcessorItineraries"); // If just no itinerary then don't bother if (ProcItinList.size() < 2) return; std::map NameToBitsMap; // Parse functional units for all the itineraries. for (unsigned i = 0, N = ProcItinList.size(); i < N; ++i) { Record *Proc = ProcItinList[i]; std::vector 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 &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 &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 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 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 WorkList; std::map, 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::iterator CI = allInsnClasses.begin(), CE = allInsnClasses.end(); CI != CE; ++CI) { unsigned InsnClass = *CI; std::set 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, 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); }