mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 15:10:12 +01:00
We already go to some effort to avoid moving these, but we end up still moving them twice: once when adding to the worklist and again when reversing a chunk of the worklist. * To avoid a move when constructing the worklist, add an `EmplaceResult` utility that allows the result of a function call to be emplaced into a container. * To avoid moves when reversing the list, stop reversing it. Instead of reversing the list and popping tasks as we run them, we accumulate a sequence of tasks for a deferred definition region, run them in the order they were enqueued, then pop them all at the end. This will in some cases increase the high-water-mark of the size of the worklist, but not asymptotically. The same high-water-mark could be reached with the old approach by reordering the declarations in the source file. In passing, we no longer create `LeaveDeferredDefinitionRegion` tasks for non-nested regions. We don't need them, because we can detect that condition by our reaching the end of the worklist. This means that the enter / leave region actions are now always in correspondence -- we only create them for *nested* regions. The tasks have been renamed to convey this. We still move the suspended function states around if the worklist grows to over 64 entries and gets reallocated. We could potentially address that issue too by switching to a chunked allocation strategy as is used by `ValueStore` and then make the tasks noncopyable, but I'm not attempting that in this PR. --------- Co-authored-by: Dana Jansens <danakj@orodu.net>
97 lines
3.5 KiB
C++
97 lines
3.5 KiB
C++
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
|
// Exceptions. See /LICENSE for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
#ifndef CARBON_TOOLCHAIN_CHECK_NODE_ID_TRAVERSAL_H_
|
|
#define CARBON_TOOLCHAIN_CHECK_NODE_ID_TRAVERSAL_H_
|
|
|
|
#include "common/ostream.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "toolchain/check/context.h"
|
|
#include "toolchain/check/deferred_definition_worklist.h"
|
|
#include "toolchain/parse/tree.h"
|
|
|
|
namespace Carbon::Check {
|
|
|
|
// A traversal of the node IDs in the parse tree, in the order in which we need
|
|
// to check them.
|
|
class NodeIdTraversal {
|
|
public:
|
|
// `context` must not be null.
|
|
explicit NodeIdTraversal(Context* context, llvm::raw_ostream* vlog_stream);
|
|
|
|
// Finds the next `NodeId` to type-check. Returns nullopt if the traversal is
|
|
// complete.
|
|
auto Next() -> std::optional<Parse::NodeId>;
|
|
|
|
// Performs any processing necessary after we type-check a node.
|
|
auto Handle(Parse::NodeKind parse_kind) -> void;
|
|
|
|
private:
|
|
// State used to track the next deferred function definition that we will
|
|
// encounter and need to reorder.
|
|
class NextDeferredDefinitionCache {
|
|
public:
|
|
explicit NextDeferredDefinitionCache(const Parse::Tree* tree);
|
|
|
|
// Set the specified deferred definition index as being the next one that
|
|
// will be encountered.
|
|
auto SkipTo(Parse::DeferredDefinitionIndex next_index) -> void;
|
|
|
|
// Returns the index of the next deferred definition to be encountered.
|
|
auto index() const -> Parse::DeferredDefinitionIndex { return index_; }
|
|
|
|
// Returns the ID of the start node of the next deferred definition.
|
|
auto start_id() const -> Parse::NodeId { return start_id_; }
|
|
|
|
private:
|
|
const Parse::Tree* tree_;
|
|
Parse::DeferredDefinitionIndex index_ =
|
|
Parse::DeferredDefinitionIndex::None;
|
|
Parse::NodeId start_id_ = Parse::NodeId::None;
|
|
};
|
|
|
|
// A chunk of the parse tree that we need to type-check.
|
|
struct Chunk {
|
|
Parse::Tree::PostorderIterator it;
|
|
Parse::Tree::PostorderIterator end;
|
|
// The next definition that will be encountered after this chunk completes.
|
|
Parse::DeferredDefinitionIndex next_definition;
|
|
// Whether we are currently checking deferred definitions, rather than the
|
|
// tokens of this chunk. If so, we'll pull tasks off `worklist` and execute
|
|
// them until we're done with this batch of deferred definitions. Otherwise,
|
|
// we'll pull node IDs from `*it` until it reaches `end`.
|
|
bool checking_deferred_definitions = false;
|
|
// If we're checking deferred definitions, the index of the first task to
|
|
// execute from `worklist`.
|
|
size_t first_worklist_index;
|
|
// If we're checking deferred definitions, the index of the next task to
|
|
// execute from `worklist`.
|
|
size_t next_worklist_index;
|
|
};
|
|
|
|
// Re-enter a nested deferred definition scope.
|
|
auto PerformTask(
|
|
DeferredDefinitionWorklist::EnterNestedDeferredDefinitionScope&& enter)
|
|
-> void;
|
|
|
|
// Leave a nested deferred definition scope.
|
|
auto PerformTask(
|
|
DeferredDefinitionWorklist::LeaveNestedDeferredDefinitionScope&& leave)
|
|
-> void;
|
|
|
|
// Resume checking a deferred definition.
|
|
auto PerformTask(
|
|
DeferredDefinitionWorklist::CheckSkippedDefinition&& parse_definition)
|
|
-> void;
|
|
|
|
Context* context_;
|
|
NextDeferredDefinitionCache next_deferred_definition_;
|
|
DeferredDefinitionWorklist worklist_;
|
|
llvm::SmallVector<Chunk> chunks_;
|
|
};
|
|
|
|
} // namespace Carbon::Check
|
|
|
|
#endif // CARBON_TOOLCHAIN_CHECK_NODE_ID_TRAVERSAL_H_
|