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/FormatVariadic.h"
19 #include <algorithm>
20 #include <memory>
21 #include <queue>
22 
23 #define DEBUG_TYPE "GLR.cpp"
24 
25 namespace clang {
26 namespace pseudo {
27 
28 using StateID = LRTable::StateID;
29 
30 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const GSS::Node &N) {
31   std::vector<std::string> ParentStates;
32   for (const auto *Parent : N.parents())
33     ParentStates.push_back(llvm::formatv("{0}", Parent->State));
34   OS << llvm::formatv("state {0}, parsed symbol {1}, parents {2}", N.State,
35                       N.Payload->symbol(), llvm::join(ParentStates, ", "));
36   return OS;
37 }
38 
39 // Apply all pending shift actions.
40 // In theory, LR parsing doesn't have shift/shift conflicts on a single head.
41 // But we may have multiple active heads, and each head has a shift action.
42 //
43 // We merge the stack -- if multiple heads will reach the same state after
44 // shifting a token, we shift only once by combining these heads.
45 //
46 // E.g. we have two heads (2, 3) in the GSS, and will shift both to reach 4:
47 //   0---1---2
48 //       └---3
49 // After the shift action, the GSS is:
50 //   0---1---2---4
51 //       └---3---┘
52 void glrShift(llvm::ArrayRef<const GSS::Node *> OldHeads,
53               const ForestNode &NewTok, const ParseParams &Params,
54               std::vector<const GSS::Node *> &NewHeads) {
55   assert(NewTok.kind() == ForestNode::Terminal);
56   LLVM_DEBUG(llvm::dbgs() << llvm::formatv("  Shift {0} ({1} active heads):\n",
57                                            Params.G.symbolName(NewTok.symbol()),
58                                            OldHeads.size()));
59 
60   // We group pending shifts by their target state so we can merge them.
61   llvm::SmallVector<std::pair<StateID, const GSS::Node *>, 8> Shifts;
62   for (const auto *H : OldHeads)
63     if (auto S = Params.Table.getShiftState(H->State, NewTok.symbol()))
64       Shifts.push_back({*S, H});
65   llvm::stable_sort(Shifts, llvm::less_first{});
66 
67   auto Rest = llvm::makeArrayRef(Shifts);
68   llvm::SmallVector<const GSS::Node *> Parents;
69   while (!Rest.empty()) {
70     // Collect the batch of PendingShift that have compatible shift states.
71     // Their heads become TempParents, the parents of the new GSS node.
72     StateID NextState = Rest.front().first;
73 
74     Parents.clear();
75     for (const auto &Base : Rest) {
76       if (Base.first != NextState)
77         break;
78       Parents.push_back(Base.second);
79     }
80     Rest = Rest.drop_front(Parents.size());
81 
82     LLVM_DEBUG(llvm::dbgs() << llvm::formatv("    --> S{0} ({1} heads)\n",
83                                              NextState, Parents.size()));
84     NewHeads.push_back(Params.GSStack.addNode(NextState, &NewTok, Parents));
85   }
86 }
87 
88 namespace {
89 // A KeyedQueue yields pairs of keys and values in order of the keys.
90 template <typename Key, typename Value>
91 using KeyedQueue =
92     std::priority_queue<std::pair<Key, Value>,
93                         std::vector<std::pair<Key, Value>>, llvm::less_first>;
94 
95 template <typename T> void sortAndUnique(std::vector<T> &Vec) {
96   llvm::sort(Vec);
97   Vec.erase(std::unique(Vec.begin(), Vec.end()), Vec.end());
98 }
99 
100 // Perform reduces until no more are possible.
101 //
102 // Generally this means walking up from the heads gathering ForestNodes that
103 // will match the RHS of the rule we're reducing into a sequence ForestNode,
104 // and ending up at a base node.
105 // Then we push a new GSS node onto that base, taking care to:
106 //  - pack alternative sequence ForestNodes into an ambiguous ForestNode.
107 //  - use the same GSS node for multiple heads if the parse state matches.
108 //
109 // Examples of reduction:
110 //   Before (simple):
111 //     0--1(expr)--2(semi)
112 //   After reducing 2 by `stmt := expr semi`:
113 //     0--3(stmt)                // 3 is goto(0, stmt)
114 //
115 //   Before (splitting due to R/R conflict):
116 //     0--1(IDENTIFIER)
117 //   After reducing 1 by `class-name := IDENTIFIER` & `enum-name := IDENTIFIER`:
118 //     0--2(class-name)          // 2 is goto(0, class-name)
119 //     └--3(enum-name)           // 3 is goto(0, enum-name)
120 //
121 //   Before (splitting due to multiple bases):
122 //     0--2(class-name)--4(STAR)
123 //     └--3(enum-name)---┘
124 //   After reducing 4 by `ptr-operator := STAR`:
125 //     0--2(class-name)--5(ptr-operator)    // 5 is goto(2, ptr-operator)
126 //     └--3(enum-name)---6(ptr-operator)    // 6 is goto(3, ptr-operator)
127 //
128 //   Before (joining due to same goto state, multiple bases):
129 //     0--1(cv-qualifier)--3(class-name)
130 //     └--2(cv-qualifier)--4(enum-name)
131 //   After reducing 3 by `type-name := class-name` and
132 //                  4 by `type-name := enum-name`:
133 //     0--1(cv-qualifier)--5(type-name)  // 5 is goto(1, type-name) and
134 //     └--2(cv-qualifier)--┘             //      goto(2, type-name)
135 //
136 //   Before (joining due to same goto state, the same base):
137 //     0--1(class-name)--3(STAR)
138 //     └--2(enum-name)--4(STAR)
139 //   After reducing 3 by `pointer := class-name STAR` and
140 //                  2 by`enum-name := class-name STAR`:
141 //     0--5(pointer)       // 5 is goto(0, pointer)
142 //
143 // (This is a functor rather than a function to allow it to reuse scratch
144 // storage across calls).
145 class GLRReduce {
146   const ParseParams &Params;
147 
148   // There are two interacting complications:
149   // 1.  Performing one reduce can unlock new reduces on the newly-created head.
150   // 2a. The ambiguous ForestNodes must be complete (have all sequence nodes).
151   //     This means we must have unlocked all the reduces that contribute to it.
152   // 2b. Similarly, the new GSS nodes must be complete (have all parents).
153   //
154   // We define a "family" of reduces as those that produce the same symbol and
155   // cover the same range of tokens. These are exactly the set of reductions
156   // whose sequence nodes would be covered by the same ambiguous node.
157   // We wish to process a whole family at a time (to satisfy complication 2),
158   // and can address complication 1 by carefully ordering the families:
159   // - Process families covering fewer tokens first.
160   //   A reduce can't depend on a longer reduce!
161   // - For equal token ranges: if S := T, process T families before S families.
162   //   Parsing T can't depend on an equal-length S, as the grammar is acyclic.
163   //
164   // This isn't quite enough: we don't know the token length of the reduction
165   // until we walk up the stack to perform the pop.
166   // So we perform the pop part upfront, and place the push specification on
167   // priority queues such that we can retrieve a family at a time.
168 
169   // A reduction family is characterized by its token range and symbol produced.
170   // It is used as a key in the priority queues to group pushes by family.
171   struct Family {
172     // The start of the token range of the reduce.
173     Token::Index Start;
174     SymbolID Symbol;
175     // Rule must produce Symbol and can otherwise be arbitrary.
176     // RuleIDs have the topological order based on the acyclic grammar.
177     // FIXME: should SymbolIDs be so ordered instead?
178     RuleID Rule;
179 
180     bool operator==(const Family &Other) const {
181       return Start == Other.Start && Symbol == Other.Symbol;
182     }
183     // The larger Family is the one that should be processed first.
184     bool operator<(const Family &Other) const {
185       if (Start != Other.Start)
186         return Start < Other.Start;
187       if (Symbol != Other.Symbol)
188         return Rule > Other.Rule;
189       assert(*this == Other);
190       return false;
191     }
192   };
193 
194   // A sequence is the ForestNode payloads of the GSS nodes we are reducing.
195   using Sequence = llvm::SmallVector<const ForestNode *, Rule::MaxElements>;
196   // Like ArrayRef<const ForestNode*>, but with the missing operator<.
197   // (Sequences are big to move by value as the collections gets rearranged).
198   struct SequenceRef {
199     SequenceRef(const Sequence &S) : S(S) {}
200     llvm::ArrayRef<const ForestNode *> S;
201     friend bool operator==(SequenceRef A, SequenceRef B) { return A.S == B.S; }
202     friend bool operator<(const SequenceRef &A, const SequenceRef &B) {
203       return std::lexicographical_compare(A.S.begin(), A.S.end(), B.S.begin(),
204                                           B.S.end());
205     }
206   };
207   // Underlying storage for sequences pointed to by stored SequenceRefs.
208   std::deque<Sequence> SequenceStorage;
209   // We don't actually destroy the sequences between calls, to reuse storage.
210   // Everything SequenceStorage[ >=SequenceStorageCount ] is reusable scratch.
211   unsigned SequenceStorageCount;
212 
213   // Halfway through a reduction (after the pop, before the push), we have
214   // collected nodes for the RHS of a rule, and reached a base node.
215   // They specify a sequence ForestNode we may build (but we dedup first).
216   // (The RuleID is not stored here, but rather in the Family).
217   struct PushSpec {
218     // The last node popped before pushing. Its parent is the reduction base(s).
219     // (Base is more fundamental, but this is cheaper to store).
220     const GSS::Node* LastPop = nullptr;
221     Sequence *Seq = nullptr;
222   };
223   KeyedQueue<Family, PushSpec> Sequences; // FIXME: rename => PendingPushes?
224 
225   // We treat Heads as a queue of Pop operations still to be performed.
226   // PoppedHeads is our position within it.
227   std::vector<const GSS::Node *> *Heads;
228   unsigned NextPopHead;
229   SymbolID Lookahead;
230 
231   Sequence TempSequence;
232 public:
233   GLRReduce(const ParseParams &Params) : Params(Params) {}
234 
235   void operator()(std::vector<const GSS::Node *> &Heads, SymbolID Lookahead) {
236     assert(isToken(Lookahead));
237 
238     NextPopHead = 0;
239     this->Heads = &Heads;
240     this->Lookahead = Lookahead;
241     assert(Sequences.empty());
242     SequenceStorageCount = 0;
243 
244     popPending();
245     while (!Sequences.empty()) {
246       pushNext();
247       popPending();
248     }
249   }
250 
251 private:
252   // pop walks up the parent chain(s) for a reduction from Head by to Rule.
253   // Once we reach the end, record the bases and sequences.
254   void pop(const GSS::Node *Head, RuleID RID) {
255     LLVM_DEBUG(llvm::dbgs() << "  Pop " << Params.G.dumpRule(RID) << "\n");
256     const auto &Rule = Params.G.lookupRule(RID);
257     Family F{/*Start=*/0, /*Symbol=*/Rule.Target, /*Rule=*/RID};
258     TempSequence.resize_for_overwrite(Rule.Size);
259     auto DFS = [&](const GSS::Node *N, unsigned I, auto &DFS) {
260       TempSequence[Rule.Size - 1 - I] = N->Payload;
261       if (I + 1 == Rule.Size) {
262         F.Start = TempSequence.front()->startTokenIndex();
263         LLVM_DEBUG({
264           for (const auto *B : N->parents())
265             llvm::dbgs() << "    --> base at S" << B->State << "\n";
266         });
267 
268         // Copy the chain to stable storage so it can be enqueued.
269         if (SequenceStorageCount == SequenceStorage.size())
270           SequenceStorage.emplace_back();
271         SequenceStorage[SequenceStorageCount] = TempSequence;
272         Sequence *Seq = &SequenceStorage[SequenceStorageCount++];
273 
274         Sequences.emplace(F, PushSpec{N, Seq});
275         return;
276       }
277       for (const GSS::Node *Parent : N->parents())
278         DFS(Parent, I + 1, DFS);
279     };
280     DFS(Head, 0, DFS);
281   }
282 
283   // popPending pops every available reduction.
284   void popPending() {
285     for (; NextPopHead < Heads->size(); ++NextPopHead) {
286       // In trivial cases, we perform the complete reduce here!
287       if (popAndPushTrivial())
288         continue;
289       for (const auto &A :
290            Params.Table.getActions((*Heads)[NextPopHead]->State, Lookahead)) {
291         if (A.kind() != LRTable::Action::Reduce)
292           continue;
293         pop((*Heads)[NextPopHead], A.getReduceRule());
294       }
295     }
296   }
297 
298   // Storage reused by each call to pushNext.
299   std::vector<std::pair</*Goto*/ StateID, const GSS::Node *>> FamilyBases;
300   std::vector<std::pair<RuleID, SequenceRef>> FamilySequences;
301   std::vector<const GSS::Node *> Parents;
302   std::vector<const ForestNode *> SequenceNodes;
303 
304   // Process one push family, forming a forest node.
305   // This produces new GSS heads which may enable more pops.
306   void pushNext() {
307     assert(!Sequences.empty());
308     Family F = Sequences.top().first;
309 
310     LLVM_DEBUG(llvm::dbgs() << "  Push " << Params.G.symbolName(F.Symbol)
311                             << " from token " << F.Start << "\n");
312 
313     // Grab the sequences and bases for this family.
314     // We don't care which rule yielded each base. If Family.Symbol is S, the
315     // base includes an item X := ... • S ... and since the grammar is
316     // context-free, *all* parses of S are valid here.
317     FamilySequences.clear();
318     FamilyBases.clear();
319     do {
320       const PushSpec &Push = Sequences.top().second;
321       FamilySequences.emplace_back(Sequences.top().first.Rule, *Push.Seq);
322       for (const GSS::Node *Base : Push.LastPop->parents())
323         FamilyBases.emplace_back(
324             Params.Table.getGoToState(Base->State, F.Symbol), Base);
325 
326       Sequences.pop();
327     } while (!Sequences.empty() && Sequences.top().first == F);
328     // Build a forest node for each unique sequence.
329     sortAndUnique(FamilySequences);
330     SequenceNodes.clear();
331     for (const auto &SequenceSpec : FamilySequences)
332       SequenceNodes.push_back(&Params.Forest.createSequence(
333           F.Symbol, SequenceSpec.first, SequenceSpec.second.S));
334     // Wrap in an ambiguous node if needed.
335     const ForestNode *Parsed =
336         SequenceNodes.size() == 1
337             ? SequenceNodes.front()
338             : &Params.Forest.createAmbiguous(F.Symbol, SequenceNodes);
339     LLVM_DEBUG(llvm::dbgs() << "    --> " << Parsed->dump(Params.G) << "\n");
340 
341     // Bases for this family, deduplicate them, and group by the goTo State.
342     sortAndUnique(FamilyBases);
343     // Create a GSS node for each unique goto state.
344     llvm::ArrayRef<decltype(FamilyBases)::value_type> BasesLeft = FamilyBases;
345     while (!BasesLeft.empty()) {
346       StateID NextState = BasesLeft.front().first;
347       Parents.clear();
348       for (const auto &Base : BasesLeft) {
349         if (Base.first != NextState)
350           break;
351         Parents.push_back(Base.second);
352       }
353       BasesLeft = BasesLeft.drop_front(Parents.size());
354       Heads->push_back(Params.GSStack.addNode(NextState, Parsed, Parents));
355     }
356   }
357 
358   // In general we split a reduce into a pop/push, so concurrently-available
359   // reductions can run in the correct order. The data structures are expensive.
360   //
361   // When only one reduction is possible at a time, we can skip this:
362   // we pop and immediately push, as an LR parser (as opposed to GLR) would.
363   // This is valid whenever there's only one concurrent PushSpec.
364   //
365   // This function handles a trivial but common subset of these cases:
366   //  - there must be no pending pushes, and only one poppable head
367   //  - the head must have only one reduction rule
368   //  - the reduction path must be a straight line (no multiple parents)
369   // (Roughly this means there's no local ambiguity, so the LR algorithm works).
370   bool popAndPushTrivial() {
371     if (!Sequences.empty() || Heads->size() != NextPopHead + 1)
372       return false;
373     const GSS::Node *Head = Heads->back();
374     llvm::Optional<RuleID> RID;
375     for (auto &A : Params.Table.getActions(Head->State, Lookahead)) {
376       if (A.kind() != LRTable::Action::Reduce)
377         continue;
378       if (RID.hasValue())
379         return false;
380       RID = A.getReduceRule();
381     }
382     if (!RID.hasValue())
383       return true; // no reductions available, but we've processed the head!
384     const auto &Rule = Params.G.lookupRule(*RID);
385     const GSS::Node *Base = Head;
386     TempSequence.resize_for_overwrite(Rule.Size);
387     for (unsigned I = 0; I < Rule.Size; ++I) {
388       if (Base->parents().size() != 1)
389         return false;
390       TempSequence[Rule.Size - 1 - I] = Base->Payload;
391       Base = Base->parents().front();
392     }
393     const ForestNode *Parsed =
394         &Params.Forest.createSequence(Rule.Target, *RID, TempSequence);
395     StateID NextState = Params.Table.getGoToState(Base->State, Rule.Target);
396     Heads->push_back(Params.GSStack.addNode(NextState, Parsed, {Base}));
397     return true;
398   }
399 };
400 
401 } // namespace
402 
403 const ForestNode &glrParse(const TokenStream &Tokens, const ParseParams &Params,
404                            SymbolID StartSymbol) {
405   GLRReduce Reduce(Params);
406   assert(isNonterminal(StartSymbol) && "Start symbol must be a nonterminal");
407   llvm::ArrayRef<ForestNode> Terminals = Params.Forest.createTerminals(Tokens);
408   auto &G = Params.G;
409   (void)G;
410   auto &GSS = Params.GSStack;
411 
412   StateID StartState = Params.Table.getStartState(StartSymbol);
413   // Heads correspond to the parse of tokens [0, I), NextHeads to [0, I+1).
414   std::vector<const GSS::Node *> Heads = {GSS.addNode(/*State=*/StartState,
415                                                       /*ForestNode=*/nullptr,
416                                                       {})};
417   std::vector<const GSS::Node *> NextHeads;
418   auto MaybeGC = [&, Roots(std::vector<const GSS::Node *>{}), I(0u)]() mutable {
419     assert(NextHeads.empty() && "Running GC at the wrong time!");
420     if (++I != 20) // Run periodically to balance CPU and memory usage.
421       return;
422     I = 0;
423 
424     // We need to copy the list: Roots is consumed by the GC.
425     Roots = Heads;
426     GSS.gc(std::move(Roots));
427   };
428   // Each iteration fully processes a single token.
429   for (unsigned I = 0; I < Terminals.size(); ++I) {
430     LLVM_DEBUG(llvm::dbgs() << llvm::formatv(
431                    "Next token {0} (id={1})\n",
432                    G.symbolName(Terminals[I].symbol()), Terminals[I].symbol()));
433     // Consume the token.
434     glrShift(Heads, Terminals[I], Params, NextHeads);
435     // Form nonterminals containing the token we just consumed.
436     SymbolID Lookahead = I + 1 == Terminals.size() ? tokenSymbol(tok::eof)
437                                                    : Terminals[I + 1].symbol();
438     Reduce(NextHeads, Lookahead);
439     // Prepare for the next token.
440     std::swap(Heads, NextHeads);
441     NextHeads.clear();
442     MaybeGC();
443   }
444   LLVM_DEBUG(llvm::dbgs() << llvm::formatv("Reached eof\n"));
445 
446   StateID AcceptState = Params.Table.getGoToState(StartState, StartSymbol);
447   const ForestNode *Result = nullptr;
448   for (const auto *Head : Heads) {
449     if (Head->State == AcceptState) {
450       assert(Head->Payload->symbol() == StartSymbol);
451       assert(Result == nullptr && "multiple results!");
452       Result = Head->Payload;
453     }
454   }
455   if (Result)
456     return *Result;
457   // We failed to parse the input, returning an opaque forest node for recovery.
458   //
459   // FIXME: We will need to invoke our generic error-recovery handlers when we
460   // reach EOF without reaching accept state, and involving the eof
461   // token in the above main for-loopmay be the best way to reuse the code).
462   return Params.Forest.createOpaque(StartSymbol, /*Token::Index=*/0);
463 }
464 
465 void glrReduce(std::vector<const GSS::Node *> &Heads, SymbolID Lookahead,
466                const ParseParams &Params) {
467   // Create a new GLRReduce each time for tests, performance doesn't matter.
468   GLRReduce{Params}(Heads, Lookahead);
469 }
470 
471 const GSS::Node *GSS::addNode(LRTable::StateID State, const ForestNode *Symbol,
472                               llvm::ArrayRef<const Node *> Parents) {
473   Node *Result = new (allocate(Parents.size()))
474       Node({State, GCParity, static_cast<unsigned>(Parents.size())});
475   Alive.push_back(Result);
476   ++NodesCreated;
477   Result->Payload = Symbol;
478   if (!Parents.empty())
479     llvm::copy(Parents, reinterpret_cast<const Node **>(Result + 1));
480   return Result;
481 }
482 
483 GSS::Node *GSS::allocate(unsigned Parents) {
484   if (FreeList.size() <= Parents)
485     FreeList.resize(Parents + 1);
486   auto &SizedList = FreeList[Parents];
487   if (!SizedList.empty()) {
488     auto *Result = SizedList.back();
489     SizedList.pop_back();
490     return Result;
491   }
492   return static_cast<Node *>(
493       Arena.Allocate(sizeof(Node) + Parents * sizeof(Node *), alignof(Node)));
494 }
495 
496 void GSS::destroy(Node *N) {
497   unsigned ParentCount = N->ParentCount;
498   N->~Node();
499   assert(FreeList.size() > ParentCount && "established on construction!");
500   FreeList[ParentCount].push_back(N);
501 }
502 
503 unsigned GSS::gc(std::vector<const Node *> &&Queue) {
504 #ifndef NDEBUG
505   auto ParityMatches = [&](const Node *N) { return N->GCParity == GCParity; };
506   assert("Before GC" && llvm::all_of(Alive, ParityMatches));
507   auto Deferred = llvm::make_scope_exit(
508       [&] { assert("After GC" && llvm::all_of(Alive, ParityMatches)); });
509   assert(llvm::all_of(
510       Queue, [&](const Node *R) { return llvm::is_contained(Alive, R); }));
511 #endif
512   unsigned InitialCount = Alive.size();
513 
514   // Mark
515   GCParity = !GCParity;
516   while (!Queue.empty()) {
517     Node *N = const_cast<Node *>(Queue.back()); // Safe: we created these nodes.
518     Queue.pop_back();
519     if (N->GCParity != GCParity) { // Not seen yet
520       N->GCParity = GCParity;      // Mark as seen
521       for (const Node *P : N->parents()) // And walk parents
522         Queue.push_back(P);
523     }
524   }
525   // Sweep
526   llvm::erase_if(Alive, [&](Node *N) {
527     if (N->GCParity == GCParity) // Walk reached this node.
528       return false;
529     destroy(N);
530     return true;
531   });
532 
533   LLVM_DEBUG(llvm::dbgs() << "GC pruned " << (InitialCount - Alive.size())
534                           << "/" << InitialCount << " GSS nodes\n");
535   return InitialCount - Alive.size();
536 }
537 
538 } // namespace pseudo
539 } // namespace clang
540