| //===- 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 "CodeGenTarget.h" |
| #include "llvm/ADT/DenseSet.h" |
| #include "llvm/ADT/STLExtras.h" |
| #include "llvm/TableGen/Record.h" |
| #include "llvm/TableGen/TableGenBackend.h" |
| #include <list> |
| #include <map> |
| #include <string> |
| using namespace llvm; |
| |
| // |
| // class DFAPacketizerEmitter: class that generates and prints out the DFA |
| // for resource tracking. |
| // |
| namespace { |
| class DFAPacketizerEmitter { |
| private: |
| std::string TargetName; |
| // |
| // allInsnClasses is the set of all possible resources consumed by an |
| // InstrStage. |
| // |
| DenseSet<unsigned> allInsnClasses; |
| RecordKeeper &Records; |
| |
| public: |
| DFAPacketizerEmitter(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); |
| }; |
| } // End anonymous namespace. |
| |
| // |
| // |
| // 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]. |
| // |
| // A State instance also contains a collection of transitions from that state: |
| // a map from inputs to new states. |
| // |
| namespace { |
| class State { |
| public: |
| static int currentStateNum; |
| int stateNum; |
| bool isInitial; |
| std::set<unsigned> stateInfo; |
| typedef std::map<unsigned, State *> TransitionMap; |
| TransitionMap Transitions; |
| |
| State(); |
| State(const State &S); |
| |
| bool operator<(const State &s) const { |
| return stateNum < s.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 canAddInsnClass(unsigned InsnClass) const; |
| // |
| // AddInsnClass - Return all combinations of resource reservation |
| // which are possible from this state (PossibleStates). |
| // |
| void AddInsnClass(unsigned InsnClass, std::set<unsigned> &PossibleStates); |
| // |
| // addTransition - Add a transition from this state given the input InsnClass |
| // |
| void addTransition(unsigned InsnClass, State *To); |
| // |
| // hasTransition - Returns true if there is a transition from this state |
| // given the input InsnClass |
| // |
| bool hasTransition(unsigned InsnClass); |
| }; |
| } // End anonymous namespace. |
| |
| // |
| // class DFA: deterministic finite automaton for processor resource tracking. |
| // |
| namespace { |
| class DFA { |
| public: |
| DFA(); |
| ~DFA(); |
| |
| // Set of states. Need to keep this sorted to emit the transition table. |
| typedef std::set<State *, less_ptr<State> > StateSet; |
| StateSet states; |
| |
| State *currentState; |
| |
| // |
| // Modify the DFA. |
| // |
| void initialize(); |
| void addState(State *); |
| |
| // |
| // writeTable: Print out a table representing the DFA. |
| // |
| void writeTableAndAPI(raw_ostream &OS, const std::string &ClassName); |
| }; |
| } // End anonymous namespace. |
| |
| |
| // |
| // Constructors and destructors for State and DFA |
| // |
| State::State() : |
| stateNum(currentStateNum++), isInitial(false) {} |
| |
| |
| State::State(const State &S) : |
| stateNum(currentStateNum++), isInitial(S.isInitial), |
| stateInfo(S.stateInfo) {} |
| |
| DFA::DFA(): currentState(NULL) {} |
| |
| DFA::~DFA() { |
| DeleteContainerPointers(states); |
| } |
| |
| // |
| // addTransition - Add a transition from this state given the input InsnClass |
| // |
| void State::addTransition(unsigned InsnClass, State *To) { |
| assert(!Transitions.count(InsnClass) && |
| "Cannot have multiple transitions for the same input"); |
| Transitions[InsnClass] = To; |
| } |
| |
| // |
| // hasTransition - Returns true if there is a transition from this state |
| // given the input InsnClass |
| // |
| bool State::hasTransition(unsigned InsnClass) { |
| return Transitions.count(InsnClass) > 0; |
| } |
| |
| // |
| // AddInsnClass - Return all combinations of resource reservation |
| // which are possible from this state (PossibleStates). |
| // |
| void State::AddInsnClass(unsigned InsnClass, |
| std::set<unsigned> &PossibleStates) { |
| // |
| // Iterate over all resource states in currentState. |
| // |
| |
| 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); |
| } |
| } |
| } |
| } |
| |
| } |
| |
| |
| // |
| // canAddInsnClass - Quickly verifies 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. |
| // |
| bool State::canAddInsnClass(unsigned InsnClass) const { |
| for (std::set<unsigned>::const_iterator SI = stateInfo.begin(); |
| SI != stateInfo.end(); ++SI) { |
| if (~*SI & InsnClass) |
| return true; |
| } |
| return false; |
| } |
| |
| |
| void DFA::initialize() { |
| assert(currentState && "Missing current state"); |
| currentState->isInitial = true; |
| } |
| |
| |
| void DFA::addState(State *S) { |
| assert(!states.count(S) && "State already exists"); |
| states.insert(S); |
| } |
| |
| |
| int State::currentStateNum = 0; |
| |
| DFAPacketizerEmitter::DFAPacketizerEmitter(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) { |
| static const std::string SentinelEntry = "{-1, -1}"; |
| DFA::StateSet::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) { |
| assert (((*SI)->stateNum == (int) i) && "Mismatch in state numbers"); |
| StateEntry[i] = ValidTransitions; |
| for (State::TransitionMap::iterator |
| II = (*SI)->Transitions.begin(), IE = (*SI)->Transitions.end(); |
| II != IE; ++II) { |
| OS << "{" << II->first << ", " |
| << II->second->stateNum |
| << "}, "; |
| } |
| ValidTransitions += (*SI)->Transitions.size(); |
| |
| // If there are no valid transitions from this stage, we need a sentinel |
| // transition. |
| if (ValidTransitions == StateEntry[i]) { |
| OS << SentinelEntry << ","; |
| ++ValidTransitions; |
| } |
| |
| OS << "\n"; |
| } |
| |
| // Print out a sentinel entry at the end of the StateInputTable. This is |
| // needed to iterate over StateInputTable in DFAPacketizer::ReadTable() |
| OS << SentinelEntry << "\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] << ", "; |
| |
| // Print out the index to the sentinel entry in StateInputTable |
| OS << ValidTransitions << ", "; |
| |
| 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 DFAPacketizerEmitter::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 DFAPacketizerEmitter::run(raw_ostream &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 (!current->hasTransition(InsnClass) && |
| current->canAddInsnClass(InsnClass)) { |
| State *NewState = NULL; |
| current->AddInsnClass(InsnClass, NewStateResources); |
| assert(NewStateResources.size() && "New states must be generated"); |
| |
| // |
| // 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); |
| } |
| |
| current->addTransition(InsnClass, NewState); |
| } |
| } |
| } |
| |
| // Print out the table. |
| D.writeTableAndAPI(OS, TargetName); |
| } |
| |
| namespace llvm { |
| |
| void EmitDFAPacketizer(RecordKeeper &RK, raw_ostream &OS) { |
| emitSourceFileHeader("Target DFA Packetizer Tables", OS); |
| DFAPacketizerEmitter(RK).run(OS); |
| } |
| |
| } // End llvm namespace |