| //===- DFAEmitter.cpp - Finite state automaton emitter --------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This class can produce a generic deterministic finite state automaton (DFA), | |
| // given a set of possible states and transitions. | |
| // | |
| // The input transitions can be nondeterministic - this class will produce the | |
| // deterministic equivalent state machine. | |
| // | |
| // The generated code can run the DFA and produce an accepted / not accepted | |
| // state and also produce, given a sequence of transitions that results in an | |
| // accepted state, the sequence of intermediate states. This is useful if the | |
| // initial automaton was nondeterministic - it allows mapping back from the DFA | |
| // to the NFA. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "DFAEmitter.h" | |
| #include "Basic/SequenceToOffsetTable.h" | |
| #include "llvm/ADT/SmallVector.h" | |
| #include "llvm/ADT/StringExtras.h" | |
| #include "llvm/ADT/UniqueVector.h" | |
| #include "llvm/Support/Debug.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include "llvm/TableGen/Record.h" | |
| #include "llvm/TableGen/TableGenBackend.h" | |
| #include <cassert> | |
| #include <cstdint> | |
| #include <deque> | |
| #include <set> | |
| #include <string> | |
| #include <variant> | |
| #include <vector> | |
| #define DEBUG_TYPE "dfa-emitter" | |
| using namespace llvm; | |
| //===----------------------------------------------------------------------===// | |
| // DfaEmitter implementation. This is independent of the GenAutomaton backend. | |
| //===----------------------------------------------------------------------===// | |
| void DfaEmitter::addTransition(state_type From, state_type To, action_type A) { | |
| Actions.insert(A); | |
| NfaStates.insert(From); | |
| NfaStates.insert(To); | |
| NfaTransitions[{From, A}].push_back(To); | |
| ++NumNfaTransitions; | |
| } | |
| void DfaEmitter::visitDfaState(const DfaState &DS) { | |
| // For every possible action... | |
| auto FromId = DfaStates.idFor(DS); | |
| for (action_type A : Actions) { | |
| DfaState NewStates; | |
| DfaTransitionInfo TI; | |
| // For every represented state, word pair in the original NFA... | |
| for (state_type FromState : DS) { | |
| // If this action is possible from this state add the transitioned-to | |
| // states to NewStates. | |
| auto I = NfaTransitions.find({FromState, A}); | |
| if (I == NfaTransitions.end()) | |
| continue; | |
| for (state_type &ToState : I->second) { | |
| NewStates.push_back(ToState); | |
| TI.emplace_back(FromState, ToState); | |
| } | |
| } | |
| if (NewStates.empty()) | |
| continue; | |
| // Sort and unique. | |
| sort(NewStates); | |
| NewStates.erase(llvm::unique(NewStates), NewStates.end()); | |
| sort(TI); | |
| TI.erase(llvm::unique(TI), TI.end()); | |
| unsigned ToId = DfaStates.insert(NewStates); | |
| DfaTransitions.emplace(std::pair(FromId, A), std::pair(ToId, TI)); | |
| } | |
| } | |
| void DfaEmitter::constructDfa() { | |
| DfaState Initial(1, /*NFA initial state=*/0); | |
| DfaStates.insert(Initial); | |
| // Note that UniqueVector starts indices at 1, not zero. | |
| unsigned DfaStateId = 1; | |
| while (DfaStateId <= DfaStates.size()) { | |
| DfaState S = DfaStates[DfaStateId]; | |
| visitDfaState(S); | |
| DfaStateId++; | |
| } | |
| } | |
| void DfaEmitter::emit(StringRef Name, raw_ostream &OS) { | |
| constructDfa(); | |
| OS << "// Input NFA has " << NfaStates.size() << " states with " | |
| << NumNfaTransitions << " transitions.\n"; | |
| OS << "// Generated DFA has " << DfaStates.size() << " states with " | |
| << DfaTransitions.size() << " transitions.\n\n"; | |
| // Implementation note: We don't bake a simple std::pair<> here as it requires | |
| // significantly more effort to parse. A simple test with a large array of | |
| // struct-pairs (N=100000) took clang-10 6s to parse. The same array of | |
| // std::pair<uint64_t, uint64_t> took 242s. Instead we allow the user to | |
| // define the pair type. | |
| // | |
| // FIXME: It may make sense to emit these as ULEB sequences instead of | |
| // pairs of uint64_t. | |
| OS << "// A zero-terminated sequence of NFA state transitions. Every DFA\n"; | |
| OS << "// transition implies a set of NFA transitions. These are referred\n"; | |
| OS << "// to by index in " << Name << "Transitions[].\n"; | |
| SequenceToOffsetTable<DfaTransitionInfo> Table; | |
| for (auto &T : DfaTransitions) | |
| Table.add(T.second.second); | |
| Table.layout(); | |
| OS << "const std::array<NfaStatePair, " << Table.size() << "> " << Name | |
| << "TransitionInfo = {{\n"; | |
| Table.emit(OS, [](raw_ostream &OS, std::pair<uint64_t, uint64_t> P) { | |
| OS << "{" << P.first << ", " << P.second << "}"; | |
| }); | |
| OS << "}};\n\n"; | |
| OS << "// A transition in the generated " << Name << " DFA.\n"; | |
| OS << "struct " << Name << "Transition {\n"; | |
| OS << " unsigned FromDfaState; // The transitioned-from DFA state.\n"; | |
| OS << " "; | |
| printActionType(OS); | |
| OS << " Action; // The input symbol that causes this transition.\n"; | |
| OS << " unsigned ToDfaState; // The transitioned-to DFA state.\n"; | |
| OS << " unsigned InfoIdx; // Start index into " << Name | |
| << "TransitionInfo.\n"; | |
| OS << "};\n\n"; | |
| OS << "// A table of DFA transitions, ordered by {FromDfaState, Action}.\n"; | |
| OS << "// The initial state is 1, not zero.\n"; | |
| OS << "const std::array<" << Name << "Transition, " << DfaTransitions.size() | |
| << "> " << Name << "Transitions = {{\n"; | |
| for (auto &KV : DfaTransitions) { | |
| dfa_state_type From = KV.first.first; | |
| dfa_state_type To = KV.second.first; | |
| action_type A = KV.first.second; | |
| unsigned InfoIdx = Table.get(KV.second.second); | |
| OS << " {" << From << ", "; | |
| printActionValue(A, OS); | |
| OS << ", " << To << ", " << InfoIdx << "},\n"; | |
| } | |
| OS << "\n}};\n\n"; | |
| } | |
| void DfaEmitter::printActionType(raw_ostream &OS) { OS << "uint64_t"; } | |
| void DfaEmitter::printActionValue(action_type A, raw_ostream &OS) { OS << A; } | |
| //===----------------------------------------------------------------------===// | |
| // AutomatonEmitter implementation | |
| //===----------------------------------------------------------------------===// | |
| namespace { | |
| using Action = std::variant<const Record *, unsigned, std::string>; | |
| using ActionTuple = std::vector<Action>; | |
| class Automaton; | |
| class Transition { | |
| uint64_t NewState; | |
| // The tuple of actions that causes this transition. | |
| ActionTuple Actions; | |
| // The types of the actions; this is the same across all transitions. | |
| SmallVector<std::string, 4> Types; | |
| public: | |
| Transition(const Record *R, Automaton *Parent); | |
| const ActionTuple &getActions() { return Actions; } | |
| SmallVector<std::string, 4> getTypes() { return Types; } | |
| bool canTransitionFrom(uint64_t State); | |
| uint64_t transitionFrom(uint64_t State); | |
| }; | |
| class Automaton { | |
| const RecordKeeper &Records; | |
| const Record *R; | |
| std::vector<Transition> Transitions; | |
| /// All possible action tuples, uniqued. | |
| UniqueVector<ActionTuple> Actions; | |
| /// The fields within each Transition object to find the action symbols. | |
| std::vector<StringRef> ActionSymbolFields; | |
| public: | |
| Automaton(const RecordKeeper &Records, const Record *R); | |
| void emit(raw_ostream &OS); | |
| ArrayRef<StringRef> getActionSymbolFields() { return ActionSymbolFields; } | |
| /// If the type of action A has been overridden (there exists a field | |
| /// "TypeOf_A") return that, otherwise return the empty string. | |
| StringRef getActionSymbolType(StringRef A); | |
| }; | |
| class AutomatonEmitter { | |
| const RecordKeeper &Records; | |
| public: | |
| AutomatonEmitter(const RecordKeeper &R) : Records(R) {} | |
| void run(raw_ostream &OS); | |
| }; | |
| /// A DfaEmitter implementation that can print our variant action type. | |
| class CustomDfaEmitter : public DfaEmitter { | |
| const UniqueVector<ActionTuple> &Actions; | |
| std::string TypeName; | |
| public: | |
| CustomDfaEmitter(const UniqueVector<ActionTuple> &Actions, StringRef TypeName) | |
| : Actions(Actions), TypeName(TypeName) {} | |
| void printActionType(raw_ostream &OS) override; | |
| void printActionValue(action_type A, raw_ostream &OS) override; | |
| }; | |
| } // namespace | |
| void AutomatonEmitter::run(raw_ostream &OS) { | |
| for (const Record *R : Records.getAllDerivedDefinitions("GenericAutomaton")) { | |
| Automaton A(Records, R); | |
| OS << "#ifdef GET_" << R->getName() << "_DECL\n"; | |
| A.emit(OS); | |
| OS << "#endif // GET_" << R->getName() << "_DECL\n"; | |
| } | |
| } | |
| Automaton::Automaton(const RecordKeeper &Records, const Record *R) | |
| : Records(Records), R(R) { | |
| LLVM_DEBUG(dbgs() << "Emitting automaton for " << R->getName() << "\n"); | |
| ActionSymbolFields = R->getValueAsListOfStrings("SymbolFields"); | |
| } | |
| void Automaton::emit(raw_ostream &OS) { | |
| StringRef TransitionClass = R->getValueAsString("TransitionClass"); | |
| for (const Record *T : Records.getAllDerivedDefinitions(TransitionClass)) { | |
| assert(T->isSubClassOf("Transition")); | |
| Transitions.emplace_back(T, this); | |
| Actions.insert(Transitions.back().getActions()); | |
| } | |
| LLVM_DEBUG(dbgs() << " Action alphabet cardinality: " << Actions.size() | |
| << "\n"); | |
| LLVM_DEBUG(dbgs() << " Each state has " << Transitions.size() | |
| << " potential transitions.\n"); | |
| StringRef Name = R->getName(); | |
| CustomDfaEmitter Emitter(Actions, Name.str() + "Action"); | |
| // Starting from the initial state, build up a list of possible states and | |
| // transitions. | |
| std::deque<uint64_t> Worklist(1, 0); | |
| std::set<uint64_t> SeenStates; | |
| unsigned NumTransitions = 0; | |
| SeenStates.insert(Worklist.front()); | |
| while (!Worklist.empty()) { | |
| uint64_t State = Worklist.front(); | |
| Worklist.pop_front(); | |
| for (Transition &T : Transitions) { | |
| if (!T.canTransitionFrom(State)) | |
| continue; | |
| uint64_t NewState = T.transitionFrom(State); | |
| if (SeenStates.emplace(NewState).second) | |
| Worklist.emplace_back(NewState); | |
| ++NumTransitions; | |
| Emitter.addTransition(State, NewState, Actions.idFor(T.getActions())); | |
| } | |
| } | |
| LLVM_DEBUG(dbgs() << " NFA automaton has " << SeenStates.size() | |
| << " states with " << NumTransitions << " transitions.\n"); | |
| (void)NumTransitions; | |
| const auto &ActionTypes = Transitions.back().getTypes(); | |
| OS << "// The type of an action in the " << Name << " automaton.\n"; | |
| if (ActionTypes.size() == 1) { | |
| OS << "using " << Name << "Action = " << ActionTypes[0] << ";\n"; | |
| } else { | |
| OS << "using " << Name << "Action = std::tuple<" << join(ActionTypes, ", ") | |
| << ">;\n"; | |
| } | |
| OS << "\n"; | |
| Emitter.emit(Name, OS); | |
| } | |
| StringRef Automaton::getActionSymbolType(StringRef A) { | |
| Twine Ty = "TypeOf_" + A; | |
| if (!R->getValue(Ty.str())) | |
| return ""; | |
| return R->getValueAsString(Ty.str()); | |
| } | |
| Transition::Transition(const Record *R, Automaton *Parent) { | |
| const BitsInit *NewStateInit = R->getValueAsBitsInit("NewState"); | |
| assert(NewStateInit->getNumBits() <= sizeof(uint64_t) * 8 && | |
| "State cannot be represented in 64 bits!"); | |
| NewState = NewStateInit->convertKnownBitsToInt(); | |
| for (StringRef A : Parent->getActionSymbolFields()) { | |
| const RecordVal *SymbolV = R->getValue(A); | |
| if (const auto *Ty = dyn_cast<RecordRecTy>(SymbolV->getType())) { | |
| Actions.emplace_back(R->getValueAsDef(A)); | |
| Types.emplace_back(Ty->getAsString()); | |
| } else if (isa<IntRecTy>(SymbolV->getType())) { | |
| Actions.emplace_back(static_cast<unsigned>(R->getValueAsInt(A))); | |
| Types.emplace_back("unsigned"); | |
| } else if (isa<StringRecTy>(SymbolV->getType())) { | |
| Actions.emplace_back(R->getValueAsString(A).str()); | |
| Types.emplace_back("std::string"); | |
| } else { | |
| report_fatal_error("Unhandled symbol type!"); | |
| } | |
| StringRef TypeOverride = Parent->getActionSymbolType(A); | |
| if (!TypeOverride.empty()) | |
| Types.back() = TypeOverride.str(); | |
| } | |
| } | |
| bool Transition::canTransitionFrom(uint64_t State) { | |
| if ((State & NewState) == 0) | |
| // The bits we want to set are not set; | |
| return true; | |
| return false; | |
| } | |
| uint64_t Transition::transitionFrom(uint64_t State) { return State | NewState; } | |
| void CustomDfaEmitter::printActionType(raw_ostream &OS) { OS << TypeName; } | |
| void CustomDfaEmitter::printActionValue(action_type A, raw_ostream &OS) { | |
| const ActionTuple &AT = Actions[A]; | |
| if (AT.size() > 1) | |
| OS << "{"; | |
| ListSeparator LS; | |
| for (const auto &SingleAction : AT) { | |
| OS << LS; | |
| if (const auto *R = std::get_if<const Record *>(&SingleAction)) | |
| OS << (*R)->getName(); | |
| else if (const auto *S = std::get_if<std::string>(&SingleAction)) | |
| OS << '"' << *S << '"'; | |
| else | |
| OS << std::get<unsigned>(SingleAction); | |
| } | |
| if (AT.size() > 1) | |
| OS << "}"; | |
| } | |
| static TableGen::Emitter::OptClass<AutomatonEmitter> | |
| X("gen-automata", "Generate generic automata"); |