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