//===- 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/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},"; ++ValidTransitions; } 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); }