mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 20:30:14 +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>
67 lines
2.6 KiB
C++
67 lines
2.6 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_COMMON_EMPLACE_BY_CALLING_H_
|
|
#define CARBON_COMMON_EMPLACE_BY_CALLING_H_
|
|
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
namespace Carbon {
|
|
|
|
// A utility to use when calling an `emplace` function to emplace the result of
|
|
// a function call. Expected usage is:
|
|
//
|
|
// my_widget_vec.emplace_back(EmplaceByCalling([&] {
|
|
// return ConstructAWidget(...);
|
|
// }));
|
|
//
|
|
// In this example, the result of `ConstructAWidget` will be constructed
|
|
// directly into the new element of `my_widget_vec`, without performing a copy
|
|
// or move.
|
|
//
|
|
// Note that the type of the argument to `emplace_back` is an `EmplaceByCalling`
|
|
// instance, not the type `DestT` stored in the container. When the `DestT`
|
|
// instance is eventually initialized directly from the `EmplaceByCalling`, a
|
|
// conversion function on `EmplaceByCalling` is used that converts to the type
|
|
// `DestT` being emplaced. This `DestT` initialization does not call an
|
|
// additional `DestT` copy or move constructor to initialize the result, and
|
|
// instead initializes it in-place in the container's storage, per the C++17
|
|
// guaranteed copy elision rules. Similarly, within the conversion function, the
|
|
// result is initialized directly by calling `make_fn`, again relying on
|
|
// guaranteed copy elision.
|
|
//
|
|
// Because the make function is called from the conversion function,
|
|
// `EmplaceByCalling` should only be used in contexts where it will be used to
|
|
// initialize a `DestT` object exactly once. This is generally true of `emplace`
|
|
// functions. Also, because the `make_fn` callback will be called after the
|
|
// container has made space for the new element, it should not inspect or modify
|
|
// the container that is being emplaced into.
|
|
template <typename MakeFnT>
|
|
class EmplaceByCalling {
|
|
public:
|
|
explicit(false) EmplaceByCalling(MakeFnT make_fn)
|
|
: make_fn_(std::move(make_fn)) {}
|
|
|
|
// Convert to the exact return type of the make function, by calling the make
|
|
// function to construct the result. No implicit conversions are permitted
|
|
// here, as that would mean we are not constructing the result in place.
|
|
template <typename DestT>
|
|
requires std::same_as<DestT, std::invoke_result_t<MakeFnT&&>>
|
|
// NOLINTNEXTLINE(google-explicit-constructor)
|
|
explicit(false) operator DestT() && {
|
|
return std::move(make_fn_)();
|
|
}
|
|
|
|
private:
|
|
MakeFnT make_fn_;
|
|
};
|
|
|
|
template <typename MakeFnT>
|
|
EmplaceByCalling(MakeFnT) -> EmplaceByCalling<MakeFnT>;
|
|
|
|
} // namespace Carbon
|
|
|
|
#endif // CARBON_COMMON_EMPLACE_BY_CALLING_H_
|