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, const Rule &Rule) { 255 LLVM_DEBUG(llvm::dbgs() << " Pop " << Params.G.dumpRule(RID) << "\n"); 256 Family F{/*Start=*/0, /*Symbol=*/Rule.Target, /*Rule=*/RID}; 257 TempSequence.resize_for_overwrite(Rule.Size); 258 auto DFS = [&](const GSS::Node *N, unsigned I, auto &DFS) { 259 TempSequence[Rule.Size - 1 - I] = N->Payload; 260 if (I + 1 == Rule.Size) { 261 F.Start = TempSequence.front()->startTokenIndex(); 262 LLVM_DEBUG({ 263 for (const auto *B : N->parents()) 264 llvm::dbgs() << " --> base at S" << B->State << "\n"; 265 }); 266 267 // Copy the chain to stable storage so it can be enqueued. 268 if (SequenceStorageCount == SequenceStorage.size()) 269 SequenceStorage.emplace_back(); 270 SequenceStorage[SequenceStorageCount] = TempSequence; 271 Sequence *Seq = &SequenceStorage[SequenceStorageCount++]; 272 273 Sequences.emplace(F, PushSpec{N, Seq}); 274 return; 275 } 276 for (const GSS::Node *Parent : N->parents()) 277 DFS(Parent, I + 1, DFS); 278 }; 279 DFS(Head, 0, DFS); 280 } 281 282 // popPending pops every available reduction. 283 void popPending() { 284 for (; NextPopHead < Heads->size(); ++NextPopHead) { 285 // In trivial cases, we perform the complete reduce here! 286 if (popAndPushTrivial()) 287 continue; 288 for (RuleID RID : 289 Params.Table.getReduceRules((*Heads)[NextPopHead]->State)) { 290 const auto &Rule = Params.G.lookupRule(RID); 291 if (Params.Table.canFollow(Rule.Target, Lookahead)) 292 pop((*Heads)[NextPopHead], RID, Rule); 293 } 294 } 295 } 296 297 // Storage reused by each call to pushNext. 298 std::vector<std::pair</*Goto*/ StateID, const GSS::Node *>> FamilyBases; 299 std::vector<std::pair<RuleID, SequenceRef>> FamilySequences; 300 std::vector<const GSS::Node *> Parents; 301 std::vector<const ForestNode *> SequenceNodes; 302 303 // Process one push family, forming a forest node. 304 // This produces new GSS heads which may enable more pops. 305 void pushNext() { 306 assert(!Sequences.empty()); 307 Family F = Sequences.top().first; 308 309 LLVM_DEBUG(llvm::dbgs() << " Push " << Params.G.symbolName(F.Symbol) 310 << " from token " << F.Start << "\n"); 311 312 // Grab the sequences and bases for this family. 313 // We don't care which rule yielded each base. If Family.Symbol is S, the 314 // base includes an item X := ... • S ... and since the grammar is 315 // context-free, *all* parses of S are valid here. 316 FamilySequences.clear(); 317 FamilyBases.clear(); 318 do { 319 const PushSpec &Push = Sequences.top().second; 320 FamilySequences.emplace_back(Sequences.top().first.Rule, *Push.Seq); 321 for (const GSS::Node *Base : Push.LastPop->parents()) 322 FamilyBases.emplace_back( 323 Params.Table.getGoToState(Base->State, F.Symbol), Base); 324 325 Sequences.pop(); 326 } while (!Sequences.empty() && Sequences.top().first == F); 327 // Build a forest node for each unique sequence. 328 sortAndUnique(FamilySequences); 329 SequenceNodes.clear(); 330 for (const auto &SequenceSpec : FamilySequences) 331 SequenceNodes.push_back(&Params.Forest.createSequence( 332 F.Symbol, SequenceSpec.first, SequenceSpec.second.S)); 333 // Wrap in an ambiguous node if needed. 334 const ForestNode *Parsed = 335 SequenceNodes.size() == 1 336 ? SequenceNodes.front() 337 : &Params.Forest.createAmbiguous(F.Symbol, SequenceNodes); 338 LLVM_DEBUG(llvm::dbgs() << " --> " << Parsed->dump(Params.G) << "\n"); 339 340 // Bases for this family, deduplicate them, and group by the goTo State. 341 sortAndUnique(FamilyBases); 342 // Create a GSS node for each unique goto state. 343 llvm::ArrayRef<decltype(FamilyBases)::value_type> BasesLeft = FamilyBases; 344 while (!BasesLeft.empty()) { 345 StateID NextState = BasesLeft.front().first; 346 Parents.clear(); 347 for (const auto &Base : BasesLeft) { 348 if (Base.first != NextState) 349 break; 350 Parents.push_back(Base.second); 351 } 352 BasesLeft = BasesLeft.drop_front(Parents.size()); 353 Heads->push_back(Params.GSStack.addNode(NextState, Parsed, Parents)); 354 } 355 } 356 357 // In general we split a reduce into a pop/push, so concurrently-available 358 // reductions can run in the correct order. The data structures are expensive. 359 // 360 // When only one reduction is possible at a time, we can skip this: 361 // we pop and immediately push, as an LR parser (as opposed to GLR) would. 362 // This is valid whenever there's only one concurrent PushSpec. 363 // 364 // This function handles a trivial but common subset of these cases: 365 // - there must be no pending pushes, and only one poppable head 366 // - the head must have only one reduction rule 367 // - the reduction path must be a straight line (no multiple parents) 368 // (Roughly this means there's no local ambiguity, so the LR algorithm works). 369 // 370 // Returns true if we successfully consumed the next unpopped head. 371 bool popAndPushTrivial() { 372 if (!Sequences.empty() || Heads->size() != NextPopHead + 1) 373 return false; 374 const GSS::Node *Head = Heads->back(); 375 llvm::Optional<RuleID> RID; 376 for (RuleID R : Params.Table.getReduceRules(Head->State)) { 377 if (RID.hasValue()) 378 return false; 379 RID = R; 380 } 381 if (!RID) 382 return true; // no reductions available, but we've processed the head! 383 const auto &Rule = Params.G.lookupRule(*RID); 384 if (!Params.Table.canFollow(Rule.Target, Lookahead)) 385 return true; // reduction is not available 386 const GSS::Node *Base = Head; 387 TempSequence.resize_for_overwrite(Rule.Size); 388 for (unsigned I = 0; I < Rule.Size; ++I) { 389 if (Base->parents().size() != 1) 390 return false; 391 TempSequence[Rule.Size - 1 - I] = Base->Payload; 392 Base = Base->parents().front(); 393 } 394 const ForestNode *Parsed = 395 &Params.Forest.createSequence(Rule.Target, *RID, TempSequence); 396 StateID NextState = Params.Table.getGoToState(Base->State, Rule.Target); 397 Heads->push_back(Params.GSStack.addNode(NextState, Parsed, {Base})); 398 return true; 399 } 400 }; 401 402 } // namespace 403 404 const ForestNode &glrParse(const TokenStream &Tokens, const ParseParams &Params, 405 SymbolID StartSymbol) { 406 GLRReduce Reduce(Params); 407 assert(isNonterminal(StartSymbol) && "Start symbol must be a nonterminal"); 408 llvm::ArrayRef<ForestNode> Terminals = Params.Forest.createTerminals(Tokens); 409 auto &G = Params.G; 410 (void)G; 411 auto &GSS = Params.GSStack; 412 413 StateID StartState = Params.Table.getStartState(StartSymbol); 414 // Heads correspond to the parse of tokens [0, I), NextHeads to [0, I+1). 415 std::vector<const GSS::Node *> Heads = {GSS.addNode(/*State=*/StartState, 416 /*ForestNode=*/nullptr, 417 {})}; 418 std::vector<const GSS::Node *> NextHeads; 419 auto MaybeGC = [&, Roots(std::vector<const GSS::Node *>{}), I(0u)]() mutable { 420 assert(NextHeads.empty() && "Running GC at the wrong time!"); 421 if (++I != 20) // Run periodically to balance CPU and memory usage. 422 return; 423 I = 0; 424 425 // We need to copy the list: Roots is consumed by the GC. 426 Roots = Heads; 427 GSS.gc(std::move(Roots)); 428 }; 429 // Each iteration fully processes a single token. 430 for (unsigned I = 0; I < Terminals.size(); ++I) { 431 LLVM_DEBUG(llvm::dbgs() << llvm::formatv( 432 "Next token {0} (id={1})\n", 433 G.symbolName(Terminals[I].symbol()), Terminals[I].symbol())); 434 // Consume the token. 435 glrShift(Heads, Terminals[I], Params, NextHeads); 436 // Form nonterminals containing the token we just consumed. 437 SymbolID Lookahead = I + 1 == Terminals.size() ? tokenSymbol(tok::eof) 438 : Terminals[I + 1].symbol(); 439 Reduce(NextHeads, Lookahead); 440 // Prepare for the next token. 441 std::swap(Heads, NextHeads); 442 NextHeads.clear(); 443 MaybeGC(); 444 } 445 LLVM_DEBUG(llvm::dbgs() << llvm::formatv("Reached eof\n")); 446 447 StateID AcceptState = Params.Table.getGoToState(StartState, StartSymbol); 448 const ForestNode *Result = nullptr; 449 for (const auto *Head : Heads) { 450 if (Head->State == AcceptState) { 451 assert(Head->Payload->symbol() == StartSymbol); 452 assert(Result == nullptr && "multiple results!"); 453 Result = Head->Payload; 454 } 455 } 456 if (Result) 457 return *Result; 458 // We failed to parse the input, returning an opaque forest node for recovery. 459 // 460 // FIXME: We will need to invoke our generic error-recovery handlers when we 461 // reach EOF without reaching accept state, and involving the eof 462 // token in the above main for-loopmay be the best way to reuse the code). 463 return Params.Forest.createOpaque(StartSymbol, /*Token::Index=*/0); 464 } 465 466 void glrReduce(std::vector<const GSS::Node *> &Heads, SymbolID Lookahead, 467 const ParseParams &Params) { 468 // Create a new GLRReduce each time for tests, performance doesn't matter. 469 GLRReduce{Params}(Heads, Lookahead); 470 } 471 472 const GSS::Node *GSS::addNode(LRTable::StateID State, const ForestNode *Symbol, 473 llvm::ArrayRef<const Node *> Parents) { 474 Node *Result = new (allocate(Parents.size())) 475 Node({State, GCParity, static_cast<unsigned>(Parents.size())}); 476 Alive.push_back(Result); 477 ++NodesCreated; 478 Result->Payload = Symbol; 479 if (!Parents.empty()) 480 llvm::copy(Parents, reinterpret_cast<const Node **>(Result + 1)); 481 return Result; 482 } 483 484 GSS::Node *GSS::allocate(unsigned Parents) { 485 if (FreeList.size() <= Parents) 486 FreeList.resize(Parents + 1); 487 auto &SizedList = FreeList[Parents]; 488 if (!SizedList.empty()) { 489 auto *Result = SizedList.back(); 490 SizedList.pop_back(); 491 return Result; 492 } 493 return static_cast<Node *>( 494 Arena.Allocate(sizeof(Node) + Parents * sizeof(Node *), alignof(Node))); 495 } 496 497 void GSS::destroy(Node *N) { 498 unsigned ParentCount = N->ParentCount; 499 N->~Node(); 500 assert(FreeList.size() > ParentCount && "established on construction!"); 501 FreeList[ParentCount].push_back(N); 502 } 503 504 unsigned GSS::gc(std::vector<const Node *> &&Queue) { 505 #ifndef NDEBUG 506 auto ParityMatches = [&](const Node *N) { return N->GCParity == GCParity; }; 507 assert("Before GC" && llvm::all_of(Alive, ParityMatches)); 508 auto Deferred = llvm::make_scope_exit( 509 [&] { assert("After GC" && llvm::all_of(Alive, ParityMatches)); }); 510 assert(llvm::all_of( 511 Queue, [&](const Node *R) { return llvm::is_contained(Alive, R); })); 512 #endif 513 unsigned InitialCount = Alive.size(); 514 515 // Mark 516 GCParity = !GCParity; 517 while (!Queue.empty()) { 518 Node *N = const_cast<Node *>(Queue.back()); // Safe: we created these nodes. 519 Queue.pop_back(); 520 if (N->GCParity != GCParity) { // Not seen yet 521 N->GCParity = GCParity; // Mark as seen 522 for (const Node *P : N->parents()) // And walk parents 523 Queue.push_back(P); 524 } 525 } 526 // Sweep 527 llvm::erase_if(Alive, [&](Node *N) { 528 if (N->GCParity == GCParity) // Walk reached this node. 529 return false; 530 destroy(N); 531 return true; 532 }); 533 534 LLVM_DEBUG(llvm::dbgs() << "GC pruned " << (InitialCount - Alive.size()) 535 << "/" << InitialCount << " GSS nodes\n"); 536 return InitialCount - Alive.size(); 537 } 538 539 } // namespace pseudo 540 } // namespace clang 541