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