| //===- 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 i.e., |
| 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); |
| } |