1 //===--- GLR.cpp   -----------------------------------------------*- C++-*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "clang-pseudo/GLR.h"
10 #include "clang-pseudo/grammar/Grammar.h"
11 #include "clang-pseudo/grammar/LRTable.h"
12 #include "clang/Basic/TokenKinds.h"
13 #include "llvm/ADT/ArrayRef.h"
14 #include "llvm/ADT/STLExtras.h"
15 #include "llvm/ADT/ScopeExit.h"
16 #include "llvm/ADT/StringExtras.h"
17 #include "llvm/Support/Debug.h"
18 #include "llvm/Support/ErrorHandling.h"
19 #include "llvm/Support/FormatVariadic.h"
20 #include <algorithm>
21 #include <memory>
22 #include <queue>
23 
24 #define DEBUG_TYPE "GLR.cpp"
25 
26 namespace clang {
27 namespace pseudo {
28 
29 using StateID = LRTable::StateID;
30 
31 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const GSS::Node &N) {
32   std::vector<std::string> ParentStates;
33   for (const auto *Parent : N.parents())
34     ParentStates.push_back(llvm::formatv("{0}", Parent->State));
35   OS << llvm::formatv("state {0}, parsed symbol {1}, parents {2}", N.State,
36                       N.Payload->symbol(), llvm::join(ParentStates, ", "));
37   return OS;
38 }
39 
40 const ForestNode &glrParse(const TokenStream &Tokens, const ParseParams &Params,
41                            SymbolID StartSymbol) {
42   assert(isNonterminal(StartSymbol) && "Start symbol must be a nonterminal");
43   llvm::ArrayRef<ForestNode> Terminals = Params.Forest.createTerminals(Tokens);
44   auto &G = Params.G;
45   (void)G;
46   auto &GSS = Params.GSStack;
47 
48   // Lists of active shift, reduce actions.
49   std::vector<ParseStep> PendingShift, PendingReduce;
50   auto AddSteps = [&](const GSS::Node *Head, SymbolID NextTok) {
51     for (const auto &Action : Params.Table.getActions(Head->State, NextTok)) {
52       switch (Action.kind()) {
53       case LRTable::Action::Shift:
54         PendingShift.push_back({Head, Action});
55         break;
56       case LRTable::Action::Reduce:
57         PendingReduce.push_back({Head, Action});
58         break;
59       default:
60         llvm_unreachable("unexpected action kind!");
61       }
62     }
63   };
64   StateID StartState = Params.Table.getStartState(StartSymbol);
65   std::vector<const GSS::Node *> NewHeads = {
66       GSS.addNode(/*State=*/StartState,
67                   /*ForestNode=*/nullptr, {})};
68   auto MaybeGC = [&, Roots(std::vector<const GSS::Node *>{}), I(0u)]() mutable {
69     assert(PendingShift.empty() && PendingReduce.empty() &&
70            "Running GC at the wrong time!");
71 
72     if (++I != 20) // Run periodically to balance CPU and memory usage.
73       return;
74     I = 0;
75 
76     // We need to copy the list: Roots is consumed by the GC.
77     Roots = NewHeads;
78     GSS.gc(std::move(Roots));
79   };
80   for (const ForestNode &Terminal : Terminals) {
81     LLVM_DEBUG(llvm::dbgs() << llvm::formatv("Next token {0} (id={1})\n",
82                                              G.symbolName(Terminal.symbol()),
83                                              Terminal.symbol()));
84     for (const auto *Head : NewHeads)
85       AddSteps(Head, Terminal.symbol());
86     NewHeads.clear();
87     glrReduce(PendingReduce, Params,
88               [&](const GSS::Node * NewHead) {
89                 // A reduce will enable more steps.
90                 AddSteps(NewHead, Terminal.symbol());
91               });
92 
93     glrShift(PendingShift, Terminal, Params,
94              [&](const GSS::Node *NewHead) { NewHeads.push_back(NewHead); });
95     MaybeGC();
96   }
97   LLVM_DEBUG(llvm::dbgs() << llvm::formatv("Next is eof\n"));
98   for (const auto *Heads : NewHeads)
99     AddSteps(Heads, tokenSymbol(tok::eof));
100 
101   StateID AcceptState = Params.Table.getGoToState(StartState, StartSymbol);
102   // Collect new heads created from the final reduce.
103   std::vector<const GSS::Node*> Heads;
104   glrReduce(PendingReduce, Params, [&](const GSS::Node *NewHead) {
105     Heads.push_back(NewHead);
106     // A reduce will enable more steps.
107     AddSteps(NewHead, tokenSymbol(tok::eof));
108   });
109 
110   const ForestNode *Result = nullptr;
111   for (const auto *Head : Heads) {
112     if (Head->State == AcceptState) {
113       assert(Head->Payload->symbol() == StartSymbol);
114       assert(Result == nullptr && "multiple results!");
115       Result = Head->Payload;
116     }
117   }
118   if (Result)
119     return *Result;
120   // We failed to parse the input, returning an opaque forest node for recovery.
121   //
122   // FIXME: We will need to invoke our generic error-recovery handlers when we
123   // reach EOF without reaching accept state, and involving the eof
124   // token in the above main for-loopmay be the best way to reuse the code).
125   return Params.Forest.createOpaque(StartSymbol, /*Token::Index=*/0);
126 }
127 
128 // Apply all pending shift actions.
129 // In theory, LR parsing doesn't have shift/shift conflicts on a single head.
130 // But we may have multiple active heads, and each head has a shift action.
131 //
132 // We merge the stack -- if multiple heads will reach the same state after
133 // shifting a token, we shift only once by combining these heads.
134 //
135 // E.g. we have two heads (2, 3) in the GSS, and will shift both to reach 4:
136 //   0---1---2
137 //       └---3
138 // After the shift action, the GSS is:
139 //   0---1---2---4
140 //       └---3---┘
141 void glrShift(std::vector<ParseStep> &PendingShift, const ForestNode &NewTok,
142               const ParseParams &Params, NewHeadCallback NewHeadCB) {
143   assert(NewTok.kind() == ForestNode::Terminal);
144   assert(llvm::all_of(PendingShift,
145                       [](const ParseStep &Step) {
146                         return Step.Action.kind() == LRTable::Action::Shift;
147                       }) &&
148          "Pending shift actions must be shift actions");
149   LLVM_DEBUG(llvm::dbgs() << llvm::formatv("  Shift {0} ({1} active heads):\n",
150                                            Params.G.symbolName(NewTok.symbol()),
151                                            PendingShift.size()));
152 
153   // We group pending shifts by their target state so we can merge them.
154   llvm::stable_sort(PendingShift, [](const ParseStep &L, const ParseStep &R) {
155     return L.Action.getShiftState() < R.Action.getShiftState();
156   });
157   auto Rest = llvm::makeArrayRef(PendingShift);
158   llvm::SmallVector<const GSS::Node *> Parents;
159   while (!Rest.empty()) {
160     // Collect the batch of PendingShift that have compatible shift states.
161     // Their heads become TempParents, the parents of the new GSS node.
162     StateID NextState = Rest.front().Action.getShiftState();
163 
164     Parents.clear();
165     for (const auto &Base : Rest) {
166       if (Base.Action.getShiftState() != NextState)
167         break;
168       Parents.push_back(Base.Head);
169     }
170     Rest = Rest.drop_front(Parents.size());
171 
172     LLVM_DEBUG(llvm::dbgs() << llvm::formatv("    --> S{0} ({1} heads)\n",
173                                              NextState, Parents.size()));
174     NewHeadCB(Params.GSStack.addNode(NextState, &NewTok, Parents));
175   }
176   PendingShift.clear();
177 }
178 
179 namespace {
180 // A KeyedQueue yields pairs of keys and values in order of the keys.
181 template <typename Key, typename Value>
182 using KeyedQueue =
183     std::priority_queue<std::pair<Key, Value>,
184                         std::vector<std::pair<Key, Value>>, llvm::less_first>;
185 
186 template <typename T> void sortAndUnique(std::vector<T> &Vec) {
187   llvm::sort(Vec);
188   Vec.erase(std::unique(Vec.begin(), Vec.end()), Vec.end());
189 }
190 } // namespace
191 
192 // Perform reduces until no more are possible.
193 //
194 // Generally this means walking up from the heads gathering ForestNodes that
195 // will match the RHS of the rule we're reducing into a sequence ForestNode,
196 // and ending up at a base node.
197 // Then we push a new GSS node onto that base, taking care to:
198 //  - pack alternative sequence ForestNodes into an ambiguous ForestNode.
199 //  - use the same GSS node for multiple heads if the parse state matches.
200 //
201 // Examples of reduction:
202 //   Before (simple):
203 //     0--1(expr)--2(semi)
204 //   After reducing 2 by `stmt := expr semi`:
205 //     0--3(stmt)                // 3 is goto(0, stmt)
206 //
207 //   Before (splitting due to R/R conflict):
208 //     0--1(IDENTIFIER)
209 //   After reducing 1 by `class-name := IDENTIFIER` & `enum-name := IDENTIFIER`:
210 //     0--2(class-name)          // 2 is goto(0, class-name)
211 //     └--3(enum-name)           // 3 is goto(0, enum-name)
212 //
213 //   Before (splitting due to multiple bases):
214 //     0--2(class-name)--4(STAR)
215 //     └--3(enum-name)---┘
216 //   After reducing 4 by `ptr-operator := STAR`:
217 //     0--2(class-name)--5(ptr-operator)    // 5 is goto(2, ptr-operator)
218 //     └--3(enum-name)---6(ptr-operator)    // 6 is goto(3, ptr-operator)
219 //
220 //   Before (joining due to same goto state, multiple bases):
221 //     0--1(cv-qualifier)--3(class-name)
222 //     └--2(cv-qualifier)--4(enum-name)
223 //   After reducing 3 by `type-name := class-name` and
224 //                  4 by `type-name := enum-name`:
225 //     0--1(cv-qualifier)--5(type-name)  // 5 is goto(1, type-name) and
226 //     └--2(cv-qualifier)--┘             //      goto(2, type-name)
227 //
228 //   Before (joining due to same goto state, the same base):
229 //     0--1(class-name)--3(STAR)
230 //     └--2(enum-name)--4(STAR)
231 //   After reducing 3 by `pointer := class-name STAR` and
232 //                  2 by`enum-name := class-name STAR`:
233 //     0--5(pointer)       // 5 is goto(0, pointer)
234 void glrReduce(std::vector<ParseStep> &PendingReduce, const ParseParams &Params,
235                NewHeadCallback NewHeadCB) {
236   // There are two interacting complications:
237   // 1.  Performing one reduce can unlock new reduces on the newly-created head.
238   // 2a. The ambiguous ForestNodes must be complete (have all sequence nodes).
239   //     This means we must have unlocked all the reduces that contribute to it.
240   // 2b. Similarly, the new GSS nodes must be complete (have all parents).
241   //
242   // We define a "family" of reduces as those that produce the same symbol and
243   // cover the same range of tokens. These are exactly the set of reductions
244   // whose sequence nodes would be covered by the same ambiguous node.
245   // We wish to process a whole family at a time (to satisfy complication 2),
246   // and can address complication 1 by carefully ordering the families:
247   // - Process families covering fewer tokens first.
248   //   A reduce can't depend on a longer reduce!
249   // - For equal token ranges: if S := T, process T families before S families.
250   //   Parsing T can't depend on an equal-length S, as the grammar is acyclic.
251   //
252   // This isn't quite enough: we don't know the token length of the reduction
253   // until we walk up the stack to perform the pop.
254   // So we perform the pop part upfront, and place the push specification on
255   // priority queues such that we can retrieve a family at a time.
256 
257   // A reduction family is characterized by its token range and symbol produced.
258   // It is used as a key in the priority queues to group pushes by family.
259   struct Family {
260     // The start of the token range of the reduce.
261     Token::Index Start;
262     SymbolID Symbol;
263     // Rule must produce Symbol and can otherwise be arbitrary.
264     // RuleIDs have the topological order based on the acyclic grammar.
265     // FIXME: should SymbolIDs be so ordered instead?
266     RuleID Rule;
267 
268     bool operator==(const Family &Other) const {
269       return Start == Other.Start && Symbol == Other.Symbol;
270     }
271     // The larger Family is the one that should be processed first.
272     bool operator<(const Family &Other) const {
273       if (Start != Other.Start)
274         return Start < Other.Start;
275       if (Symbol != Other.Symbol)
276         return Rule > Other.Rule;
277       assert(*this == Other);
278       return false;
279     }
280   };
281 
282   // A sequence is the ForestNode payloads of the GSS nodes we are reducing.
283   // These are the RHS of the rule, the RuleID is stored in the Family.
284   // They specify a sequence ForestNode we may build (but we dedup first).
285   using Sequence = llvm::SmallVector<const ForestNode *, Rule::MaxElements>;
286   struct PushSpec {
287     // A base node is the head after popping the GSS nodes we are reducing.
288     const GSS::Node* Base = nullptr;
289     Sequence Seq;
290   };
291   KeyedQueue<Family, PushSpec> Sequences;
292 
293   Sequence TempSequence;
294   // Pop walks up the parent chain(s) for a reduction from Head by to Rule.
295   // Once we reach the end, record the bases and sequences.
296   auto Pop = [&](const GSS::Node *Head, RuleID RID) {
297     LLVM_DEBUG(llvm::dbgs() << "  Pop " << Params.G.dumpRule(RID) << "\n");
298     const auto &Rule = Params.G.lookupRule(RID);
299     Family F{/*Start=*/0, /*Symbol=*/Rule.Target, /*Rule=*/RID};
300     TempSequence.resize_for_overwrite(Rule.Size);
301     auto DFS = [&](const GSS::Node *N, unsigned I, auto &DFS) {
302       if (I == Rule.Size) {
303         F.Start = TempSequence.front()->startTokenIndex();
304         LLVM_DEBUG(llvm::dbgs() << "    --> base at S" << N->State << "\n");
305         Sequences.emplace(F, PushSpec{N, TempSequence});
306         return;
307       }
308       TempSequence[Rule.Size - 1 - I] = N->Payload;
309       for (const GSS::Node *Parent : N->parents())
310         DFS(Parent, I + 1, DFS);
311     };
312     DFS(Head, 0, DFS);
313   };
314   auto PopPending = [&] {
315     for (const ParseStep &Pending : PendingReduce)
316       Pop(Pending.Head, Pending.Action.getReduceRule());
317     PendingReduce.clear();
318   };
319 
320   std::vector<std::pair</*Goto*/ StateID, const GSS::Node *>> FamilyBases;
321   std::vector<std::pair<RuleID, Sequence>> FamilySequences;
322 
323   std::vector<const GSS::Node *> TempGSSNodes;
324   std::vector<const ForestNode *> TempForestNodes;
325 
326   // Main reduction loop:
327   //  - pop as much as we can
328   //  - process one family at a time, forming a forest node
329   //  - produces new GSS heads which may enable more pops
330   PopPending();
331   while (!Sequences.empty()) {
332     Family F = Sequences.top().first;
333 
334     LLVM_DEBUG(llvm::dbgs() << "  Push " << Params.G.symbolName(F.Symbol)
335                             << " from token " << F.Start << "\n");
336 
337     // Grab the sequences and bases for this family.
338     // We don't care which rule yielded each base. If Family.Symbol is S, the
339     // base includes an item X := ... • S ... and since the grammar is
340     // context-free, *all* parses of S are valid here.
341     FamilySequences.clear();
342     FamilyBases.clear();
343     do {
344       FamilySequences.emplace_back(Sequences.top().first.Rule,
345                                    Sequences.top().second.Seq);
346       FamilyBases.emplace_back(
347           Params.Table.getGoToState(Sequences.top().second.Base->State,
348                                     F.Symbol),
349           Sequences.top().second.Base);
350 
351       Sequences.pop();
352     } while (!Sequences.empty() && Sequences.top().first == F);
353     // Build a forest node for each unique sequence.
354     sortAndUnique(FamilySequences);
355     auto &SequenceNodes = TempForestNodes;
356     SequenceNodes.clear();
357     for (const auto &SequenceSpec : FamilySequences)
358       SequenceNodes.push_back(&Params.Forest.createSequence(
359           F.Symbol, SequenceSpec.first, SequenceSpec.second));
360     // Wrap in an ambiguous node if needed.
361     const ForestNode *Parsed =
362         SequenceNodes.size() == 1
363             ? SequenceNodes.front()
364             : &Params.Forest.createAmbiguous(F.Symbol, SequenceNodes);
365     LLVM_DEBUG(llvm::dbgs() << "    --> " << Parsed->dump(Params.G) << "\n");
366 
367     // Bases for this family, deduplicate them, and group by the goTo State.
368     sortAndUnique(FamilyBases);
369     // Create a GSS node for each unique goto state.
370     llvm::ArrayRef<decltype(FamilyBases)::value_type> BasesLeft = FamilyBases;
371     while (!BasesLeft.empty()) {
372       StateID NextState = BasesLeft.front().first;
373       auto &Parents = TempGSSNodes;
374       Parents.clear();
375       for (const auto &Base : BasesLeft) {
376         if (Base.first != NextState)
377           break;
378         Parents.push_back(Base.second);
379       }
380       BasesLeft = BasesLeft.drop_front(Parents.size());
381 
382       // Invoking the callback for new heads, a real GLR parser may add new
383       // reduces to the PendingReduce queue!
384       NewHeadCB(Params.GSStack.addNode(NextState, Parsed, Parents));
385     }
386     PopPending();
387   }
388   assert(Sequences.empty());
389 }
390 
391 const GSS::Node *GSS::addNode(LRTable::StateID State, const ForestNode *Symbol,
392                               llvm::ArrayRef<const Node *> Parents) {
393   Node *Result = new (allocate(Parents.size()))
394       Node({State, GCParity, static_cast<unsigned>(Parents.size())});
395   Alive.push_back(Result);
396   ++NodesCreated;
397   Result->Payload = Symbol;
398   if (!Parents.empty())
399     llvm::copy(Parents, reinterpret_cast<const Node **>(Result + 1));
400   return Result;
401 }
402 
403 GSS::Node *GSS::allocate(unsigned Parents) {
404   if (FreeList.size() <= Parents)
405     FreeList.resize(Parents + 1);
406   auto &SizedList = FreeList[Parents];
407   if (!SizedList.empty()) {
408     auto *Result = SizedList.back();
409     SizedList.pop_back();
410     return Result;
411   }
412   return static_cast<Node *>(
413       Arena.Allocate(sizeof(Node) + Parents * sizeof(Node *), alignof(Node)));
414 }
415 
416 void GSS::destroy(Node *N) {
417   unsigned ParentCount = N->ParentCount;
418   N->~Node();
419   assert(FreeList.size() > ParentCount && "established on construction!");
420   FreeList[ParentCount].push_back(N);
421 }
422 
423 unsigned GSS::gc(std::vector<const Node *> &&Queue) {
424 #ifndef NDEBUG
425   auto ParityMatches = [&](const Node *N) { return N->GCParity == GCParity; };
426   assert("Before GC" && llvm::all_of(Alive, ParityMatches));
427   auto Deferred = llvm::make_scope_exit(
428       [&] { assert("After GC" && llvm::all_of(Alive, ParityMatches)); });
429   assert(llvm::all_of(
430       Queue, [&](const Node *R) { return llvm::is_contained(Alive, R); }));
431 #endif
432   unsigned InitialCount = Alive.size();
433 
434   // Mark
435   GCParity = !GCParity;
436   while (!Queue.empty()) {
437     Node *N = const_cast<Node *>(Queue.back()); // Safe: we created these nodes.
438     Queue.pop_back();
439     if (N->GCParity != GCParity) { // Not seen yet
440       N->GCParity = GCParity;      // Mark as seen
441       for (const Node *P : N->parents()) // And walk parents
442         Queue.push_back(P);
443     }
444   }
445   // Sweep
446   llvm::erase_if(Alive, [&](Node *N) {
447     if (N->GCParity == GCParity) // Walk reached this node.
448       return false;
449     destroy(N);
450     return true;
451   });
452 
453   LLVM_DEBUG(llvm::dbgs() << "GC pruned " << (InitialCount - Alive.size())
454                           << "/" << InitialCount << " GSS nodes\n");
455   return InitialCount - Alive.size();
456 }
457 
458 } // namespace pseudo
459 } // namespace clang
460