diff --git a/clang-tools-extra/pseudo/lib/GLR.cpp b/clang-tools-extra/pseudo/lib/GLR.cpp --- a/clang-tools-extra/pseudo/lib/GLR.cpp +++ b/clang-tools-extra/pseudo/lib/GLR.cpp @@ -192,13 +192,32 @@ }; // A sequence is the ForestNode payloads of the GSS nodes we are reducing. - // These are the RHS of the rule, the RuleID is stored in the Family. - // They specify a sequence ForestNode we may build (but we dedup first). using Sequence = llvm::SmallVector; + // Like ArrayRef, but with the missing operator<. + // (Sequences are big to move by value as the collections gets rearranged). + struct SequenceRef { + SequenceRef(const Sequence &S) : S(S) {} + llvm::ArrayRef S; + friend bool operator==(SequenceRef A, SequenceRef B) { return A.S == B.S; } + friend bool operator<(const SequenceRef &A, const SequenceRef &B) { + return std::lexicographical_compare(A.S.begin(), A.S.end(), B.S.begin(), + B.S.end()); + } + }; + // Underlying storage for sequences pointed to by stored SequenceRefs. + std::deque SequenceStorage; + // We don't actually destroy the sequences between calls, to reuse storage. + // Everything SequenceStorage[ >=SequenceStorageCount ] is reusable scratch. + unsigned SequenceStorageCount; + + // Halfway through a reduction (after the pop, before the push), we have + // collected nodes for the RHS of a rule, and reached a base node. + // They specify a sequence ForestNode we may build (but we dedup first). + // (The RuleID is not stored here, but rather in the Family). struct PushSpec { // A base node is the head after popping the GSS nodes we are reducing. const GSS::Node* Base = nullptr; - Sequence Seq; + Sequence *Seq = nullptr; }; KeyedQueue Sequences; // FIXME: rename => PendingPushes? @@ -219,6 +238,7 @@ this->Heads = &Heads; this->Lookahead = Lookahead; assert(Sequences.empty()); + SequenceStorageCount = 0; popPending(); while (!Sequences.empty()) { @@ -239,7 +259,14 @@ if (I == Rule.Size) { F.Start = TempSequence.front()->startTokenIndex(); LLVM_DEBUG(llvm::dbgs() << " --> base at S" << N->State << "\n"); - Sequences.emplace(F, PushSpec{N, TempSequence}); + + // Copy the chain to stable storage so it can be enqueued. + if (SequenceStorageCount == SequenceStorage.size()) + SequenceStorage.emplace_back(); + SequenceStorage[SequenceStorageCount] = TempSequence; + Sequence *Seq = &SequenceStorage[SequenceStorageCount++]; + + Sequences.emplace(F, PushSpec{N, Seq}); return; } TempSequence[Rule.Size - 1 - I] = N->Payload; @@ -266,7 +293,7 @@ // Storage reused by each call to pushNext. std::vector> FamilyBases; - std::vector> FamilySequences; + std::vector> FamilySequences; std::vector Parents; std::vector SequenceNodes; @@ -287,7 +314,7 @@ FamilyBases.clear(); do { FamilySequences.emplace_back(Sequences.top().first.Rule, - Sequences.top().second.Seq); + *Sequences.top().second.Seq); FamilyBases.emplace_back( Params.Table.getGoToState(Sequences.top().second.Base->State, F.Symbol), @@ -300,7 +327,7 @@ SequenceNodes.clear(); for (const auto &SequenceSpec : FamilySequences) SequenceNodes.push_back(&Params.Forest.createSequence( - F.Symbol, SequenceSpec.first, SequenceSpec.second)); + F.Symbol, SequenceSpec.first, SequenceSpec.second.S)); // Wrap in an ambiguous node if needed. const ForestNode *Parsed = SequenceNodes.size() == 1