Bladeren bron

Stop using Map for the cache in InstFingerprinter (#6019)

This takes the debug runtime of
`toolchain/check/testdata/interop/cpp/function/arithmetic_types_bridged.carbon`
from 4.7s down to about 4s (so 15% faster overall).

There's still lots of room to improve this test which seems to be
hitting lots of pathological behaviour, but InstNamer is 30% of the
runtime, with fingerprinting's `InstFingerprinter::GetOrCompute`
consuming 10% of cycles. We reduce its impact by using a vector of
vectors instead of a Map for the cache of fingerprints. After this
change InstNamer drops below 24% of the runtime.

Also move the instruction name when giving it to `AllocateName` since it
receives std::string by value, though this doesn't show up in the
profile for the test.
Dana Jansens 7 maanden geleden
bovenliggende
commit
64139e5d65

+ 16 - 0
toolchain/base/fixed_size_value_store.h

@@ -5,6 +5,9 @@
 #ifndef CARBON_TOOLCHAIN_BASE_FIXED_SIZE_VALUE_STORE_H_
 #define CARBON_TOOLCHAIN_BASE_FIXED_SIZE_VALUE_STORE_H_
 
+#include <concepts>
+#include <type_traits>
+
 #include "common/check.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
@@ -51,6 +54,19 @@ class FixedSizeValueStore {
     return store;
   }
 
+  // Makes a ValueStore of the specified size, initialized to values returned
+  // from a callback. This allows initializing non-copyable values.
+  static auto MakeWithExplicitSizeFrom(
+      size_t size, llvm::function_ref<auto()->ValueT> callable)
+      -> FixedSizeValueStore {
+    FixedSizeValueStore store;
+    store.values_.reserve(size);
+    for (auto _ : llvm::seq(size)) {
+      store.values_.push_back(callable());
+    }
+    return store;
+  }
+
   // Makes a ValueStore of the same size as a source `ValueStoreT`. This is
   // the safest constructor to use, since it ensures everything's initialized to
   // a default, and verifies a matching `IdT` for the size.

+ 4 - 3
toolchain/check/check.cpp

@@ -332,7 +332,7 @@ static auto BuildApiMapAndDiagnosePackaging(
 
 // Handles printing of formatted SemIR.
 static auto MaybeDumpFormattedSemIR(
-    const SemIR::File& sem_ir,
+    const SemIR::File& sem_ir, int total_ir_count,
     Parse::GetTreeAndSubtreesFn tree_and_subtrees_getter, bool include_in_dumps,
     const CheckParseTreesOptions& options) -> void {
   bool dump = options.dump_stream && include_in_dumps;
@@ -351,7 +351,7 @@ static auto MaybeDumpFormattedSemIR(
       options.dump_sem_ir_ranges !=
           CheckParseTreesOptions::DumpSemIRRanges::Ignore &&
       tokens.has_dump_sem_ir_ranges();
-  SemIR::Formatter formatter(&sem_ir, tree_and_subtrees_getter,
+  SemIR::Formatter formatter(&sem_ir, total_ir_count, tree_and_subtrees_getter,
                              options.include_in_dumps, use_dump_sem_ir_ranges);
   formatter.Format();
   if (options.vlog_stream) {
@@ -387,7 +387,8 @@ static auto MaybeDumpSemIR(
     }
 
     MaybeDumpFormattedSemIR(
-        *unit.sem_ir, tree_and_subtrees_getters.Get(unit.sem_ir->check_ir_id()),
+        *unit.sem_ir, units.size(),
+        tree_and_subtrees_getters.Get(unit.sem_ir->check_ir_id()),
         include_in_dumps, options);
   }
 }

+ 2 - 0
toolchain/check/check.h

@@ -27,6 +27,8 @@ struct Unit {
 
   // The unit's SemIR, provided as empty and filled in by CheckParseTrees.
   SemIR::File* sem_ir;
+  // The total number of files.
+  int total_ir_count;
 
   // Storage for the unit's Clang AST. The unique_ptr should start empty, and
   // can be assigned as part of checking.

+ 4 - 4
toolchain/check/check_unit.cpp

@@ -63,15 +63,15 @@ CheckUnit::CheckUnit(
     : unit_and_imports_(unit_and_imports),
       tree_and_subtrees_getter_(tree_and_subtrees_getters->Get(
           unit_and_imports->unit->sem_ir->check_ir_id())),
-      total_ir_count_(tree_and_subtrees_getters->size()),
       fs_(std::move(fs)),
       clang_invocation_(std::move(clang_invocation)),
       emitter_(&unit_and_imports_->err_tracker, tree_and_subtrees_getters,
                unit_and_imports_->unit->sem_ir),
       context_(&emitter_, tree_and_subtrees_getter_,
                unit_and_imports_->unit->sem_ir,
-               GetImportedIRCount(unit_and_imports), total_ir_count_,
-               gen_implicit_type_impls, vlog_stream) {}
+               GetImportedIRCount(unit_and_imports),
+               unit_and_imports_->unit->total_ir_count, gen_implicit_type_impls,
+               vlog_stream) {}
 
 auto CheckUnit::Run() -> void {
   Timings::ScopedTiming timing(unit_and_imports_->unit->timings, "check");
@@ -195,7 +195,7 @@ auto CheckUnit::CollectTransitiveImports(SemIR::InstId import_decl_id,
   // exported to this IR.
   auto ir_to_result_index =
       FixedSizeValueStore<SemIR::CheckIRId, int>::MakeWithExplicitSize(
-          total_ir_count_, -1);
+          unit_and_imports_->unit->total_ir_count, -1);
 
   // First add direct imports. This means that if an entity is imported both
   // directly and indirectly, the import path will reflect the direct import.

+ 0 - 2
toolchain/check/check_unit.h

@@ -187,8 +187,6 @@ class CheckUnit {
 
   UnitAndImports* unit_and_imports_;
   Parse::GetTreeAndSubtreesFn tree_and_subtrees_getter_;
-  // The number of IRs being checked in total.
-  int total_ir_count_;
   llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs_;
   std::shared_ptr<clang::CompilerInvocation> clang_invocation_;
 

+ 2 - 1
toolchain/check/context.cpp

@@ -20,6 +20,7 @@ Context::Context(DiagnosticEmitterBase* emitter,
     : emitter_(emitter),
       tree_and_subtrees_getter_(tree_and_subtrees_getter),
       sem_ir_(sem_ir),
+      total_ir_count_(total_ir_count),
       gen_implicit_type_impls_(gen_implicit_type_impls),
       vlog_stream_(vlog_stream),
       node_stack_(sem_ir->parse_tree(), vlog_stream),
@@ -33,7 +34,7 @@ Context::Context(DiagnosticEmitterBase* emitter,
       vtable_stack_("vtable_stack_", *sem_ir, vlog_stream),
       check_ir_map_(
           FixedSizeValueStore<SemIR::CheckIRId, SemIR::ImportIRId>::
-              MakeWithExplicitSize(total_ir_count, SemIR::ImportIRId::None)),
+              MakeWithExplicitSize(total_ir_count_, SemIR::ImportIRId::None)),
       global_init_(this),
       region_stack_([this](SemIR::LocId loc_id, std::string label) {
         TODO(loc_id, label);

+ 2 - 0
toolchain/check/context.h

@@ -313,6 +313,8 @@ class Context {
 
   // The SemIR::File being added to.
   SemIR::File* sem_ir_;
+  // The total number of files.
+  int total_ir_count_;
 
   // Whether to generate standard `impl`s for types, such as `Core.Destroy`; see
   // `CheckParseTreesOptions`.

+ 13 - 7
toolchain/driver/compile_subcommand.cpp

@@ -436,8 +436,8 @@ class MultiUnitCache;
 class CompilationUnit {
  public:
   // `driver_env`, `options`, and `consumer` must be non-null.
-  explicit CompilationUnit(SemIR::CheckIRId check_ir_id, DriverEnv* driver_env,
-                           const CompileOptions* options,
+  explicit CompilationUnit(SemIR::CheckIRId check_ir_id, int total_ir_count,
+                           DriverEnv* driver_env, const CompileOptions* options,
                            Diagnostics::Consumer* consumer,
                            llvm::StringRef input_filename);
 
@@ -499,6 +499,8 @@ class CompilationUnit {
 
   // The index of the unit amongst all units.
   SemIR::CheckIRId check_ir_id_;
+  // The number of units in total.
+  int total_ir_count_;
 
   DriverEnv* driver_env_;
   const CompileOptions* options_;
@@ -619,11 +621,12 @@ class MultiUnitCache {
 }  // namespace
 
 CompilationUnit::CompilationUnit(SemIR::CheckIRId check_ir_id,
-                                 DriverEnv* driver_env,
+                                 int total_ir_count, DriverEnv* driver_env,
                                  const CompileOptions* options,
                                  Diagnostics::Consumer* consumer,
                                  llvm::StringRef input_filename)
     : check_ir_id_(check_ir_id),
+      total_ir_count_(total_ir_count),
       driver_env_(driver_env),
       options_(options),
       input_filename_(input_filename),
@@ -724,6 +727,7 @@ auto CompilationUnit::GetCheckUnit() -> Check::Unit {
           .value_stores = &value_stores_,
           .timings = timings_ ? &*timings_ : nullptr,
           .sem_ir = &*sem_ir_,
+          .total_ir_count = total_ir_count_,
           .clang_ast_unit = &clang_ast_unit_};
 }
 
@@ -757,7 +761,7 @@ auto CompilationUnit::RunLower() -> void {
     }
     module_ = Lower::LowerToLLVM(*llvm_context_, driver_env_->fs,
                                  cache_->tree_and_subtrees_getters(), *sem_ir_,
-                                 options);
+                                 total_ir_count_, options);
   });
 }
 
@@ -955,16 +959,18 @@ auto CompileSubcommand::Run(DriverEnv& driver_env) -> DriverResult {
 
   // Prepare CompilationUnits before building scope exit handlers.
   llvm::SmallVector<std::unique_ptr<CompilationUnit>> units;
+  int total_unit_count = prelude.size() + options_.input_filenames.size();
   int unit_index = -1;
   auto unit_builder = [&](llvm::StringRef filename) {
     ++unit_index;
-    return std::make_unique<CompilationUnit>(SemIR::CheckIRId(unit_index),
-                                             &driver_env, &options_,
-                                             &driver_env.consumer, filename);
+    return std::make_unique<CompilationUnit>(
+        SemIR::CheckIRId(unit_index), total_unit_count, &driver_env, &options_,
+        &driver_env.consumer, filename);
   };
   llvm::append_range(units, llvm::map_range(prelude, unit_builder));
   llvm::append_range(units,
                      llvm::map_range(options_.input_filenames, unit_builder));
+  CARBON_CHECK(units.size() == static_cast<size_t>(total_unit_count));
 
   // Add the cache to all units. This must be done after all units are created.
   MultiUnitCache cache(&options_, units);

+ 2 - 1
toolchain/language_server/context.cpp

@@ -154,6 +154,7 @@ auto Context::File::SetText(Context& context, std::optional<int64_t> version,
                                             .value_stores = value_stores_.get(),
                                             .timings = nullptr,
                                             .sem_ir = &sem_ir,
+                                            .total_ir_count = 1,
                                             .clang_ast_unit = &clang_ast_unt}}};
 
   auto getter = [this]() -> const Parse::TreeAndSubtrees& {
@@ -163,7 +164,7 @@ auto Context::File::SetText(Context& context, std::optional<int64_t> version,
   llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs =
       llvm::vfs::getRealFileSystem();
 
-  // TODO: Include the prelude.
+  // TODO: Include the prelude. Make sure `total_ir_count` includes the files.
   Check::CheckParseTreesOptions check_options;
   check_options.vlog_stream = context.vlog_stream();
   auto getters =

+ 4 - 2
toolchain/lower/context.cpp

@@ -18,7 +18,8 @@ Context::Context(
     llvm::LLVMContext* llvm_context,
     llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs, bool want_debug_info,
     const Parse::GetTreeAndSubtreesStore* tree_and_subtrees_getters,
-    llvm::StringRef module_name, llvm::raw_ostream* vlog_stream)
+    llvm::StringRef module_name, int total_ir_count,
+    llvm::raw_ostream* vlog_stream)
     : llvm_context_(llvm_context),
       llvm_module_(std::make_unique<llvm::Module>(module_name, *llvm_context)),
       file_system_(std::move(fs)),
@@ -28,7 +29,8 @@ Context::Context(
               ? BuildDICompileUnit(module_name, *llvm_module_, di_builder_)
               : nullptr),
       tree_and_subtrees_getters_(tree_and_subtrees_getters),
-      vlog_stream_(vlog_stream) {}
+      vlog_stream_(vlog_stream),
+      total_ir_count_(total_ir_count) {}
 
 auto Context::GetFileContext(const SemIR::File* file,
                              const SemIR::InstNamer* inst_namer)

+ 6 - 1
toolchain/lower/context.h

@@ -47,7 +47,8 @@ class Context {
       llvm::LLVMContext* llvm_context,
       llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs, bool want_debug_info,
       const Parse::GetTreeAndSubtreesStore* tree_and_subtrees_getters,
-      llvm::StringRef module_name, llvm::raw_ostream* vlog_stream);
+      llvm::StringRef module_name, int total_ir_count,
+      llvm::raw_ostream* vlog_stream);
 
   // Gets or creates the `FileContext` for a given SemIR file. If an
   // `inst_namer` is specified the first time this is called for a file, it will
@@ -100,6 +101,7 @@ class Context {
   auto tree_and_subtrees_getters() -> const Parse::GetTreeAndSubtreesStore& {
     return *tree_and_subtrees_getters_;
   }
+  auto total_ir_count() -> int { return total_ir_count_; }
 
   auto printf_int_format_string() -> llvm::Value* {
     return printf_int_format_string_;
@@ -139,6 +141,9 @@ class Context {
   // The optional vlog stream.
   llvm::raw_ostream* vlog_stream_;
 
+  // The total number of files.
+  int total_ir_count_;
+
   // The `FileContext`s for each IR that is involved in this lowering action.
   Map<SemIR::CheckIRId, std::unique_ptr<FileContext>> file_contexts_;
 

+ 4 - 4
toolchain/lower/lower.cpp

@@ -18,15 +18,15 @@ auto LowerToLLVM(
     llvm::LLVMContext& llvm_context,
     llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
     const Parse::GetTreeAndSubtreesStore& tree_and_subtrees_getters,
-    const SemIR::File& sem_ir, const LowerToLLVMOptions& options)
-    -> std::unique_ptr<llvm::Module> {
+    const SemIR::File& sem_ir, int total_ir_count,
+    const LowerToLLVMOptions& options) -> std::unique_ptr<llvm::Module> {
   Context context(&llvm_context, std::move(fs), options.want_debug_info,
-                  &tree_and_subtrees_getters, sem_ir.filename(),
+                  &tree_and_subtrees_getters, sem_ir.filename(), total_ir_count,
                   options.vlog_stream);
 
   // TODO: Consider disabling instruction naming by default if we're not
   // producing textual LLVM IR.
-  SemIR::InstNamer inst_namer(&sem_ir);
+  SemIR::InstNamer inst_namer(&sem_ir, total_ir_count);
   context.GetFileContext(&sem_ir, &inst_namer).LowerDefinitions();
 
   std::unique_ptr<llvm::Module> module = std::move(context).Finalize();

+ 2 - 2
toolchain/lower/lower.h

@@ -36,8 +36,8 @@ auto LowerToLLVM(
     llvm::LLVMContext& llvm_context,
     llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
     const Parse::GetTreeAndSubtreesStore& tree_and_subtrees_getters,
-    const SemIR::File& sem_ir, const LowerToLLVMOptions& options)
-    -> std::unique_ptr<llvm::Module>;
+    const SemIR::File& sem_ir, int total_ir_count,
+    const LowerToLLVMOptions& options) -> std::unique_ptr<llvm::Module>;
 
 }  // namespace Carbon::Lower
 

+ 3 - 1
toolchain/lower/mangler.h

@@ -22,7 +22,9 @@ class Mangler {
  public:
   // Initialize a new Mangler instance for mangling entities within the
   // specified `FileContext`.
-  explicit Mangler(FileContext& file_context) : file_context_(file_context) {}
+  explicit Mangler(FileContext& file_context)
+      : file_context_(file_context),
+        fingerprinter_(file_context_.context().total_ir_count()) {}
 
   // Produce a deterministically unique mangled name for the function specified
   // by `function_id` and `specific_id`.

+ 1 - 0
toolchain/sem_ir/BUILD

@@ -172,6 +172,7 @@ cc_library(
         "//common:ostream",
         "//common:raw_string_ostream",
         "//common:type_enum",
+        "//toolchain/base:fixed_size_value_store",
         "//toolchain/base:kind_switch",
         "//toolchain/base:shared_value_stores",
         "//toolchain/base:value_ids",

+ 3 - 2
toolchain/sem_ir/formatter.cpp

@@ -35,11 +35,12 @@
 namespace Carbon::SemIR {
 
 Formatter::Formatter(
-    const File* sem_ir, Parse::GetTreeAndSubtreesFn get_tree_and_subtrees,
+    const File* sem_ir, int total_ir_count,
+    Parse::GetTreeAndSubtreesFn get_tree_and_subtrees,
     const FixedSizeValueStore<SemIR::CheckIRId, bool>* include_ir_in_dumps,
     bool use_dump_sem_ir_ranges)
     : sem_ir_(sem_ir),
-      inst_namer_(sem_ir_),
+      inst_namer_(sem_ir_, total_ir_count),
       get_tree_and_subtrees_(get_tree_and_subtrees),
       include_ir_in_dumps_(include_ir_in_dumps),
       use_dump_sem_ir_ranges_(use_dump_sem_ir_ranges),

+ 2 - 1
toolchain/sem_ir/formatter.h

@@ -21,7 +21,8 @@ class Formatter {
  public:
   // sem_ir and include_ir_in_dumps must be non-null.
   explicit Formatter(
-      const File* sem_ir, Parse::GetTreeAndSubtreesFn get_tree_and_subtrees,
+      const File* sem_ir, int total_ir_count,
+      Parse::GetTreeAndSubtreesFn get_tree_and_subtrees,
       const FixedSizeValueStore<SemIR::CheckIRId, bool>* include_ir_in_dumps,
       bool use_dump_sem_ir_ranges);
 

+ 33 - 9
toolchain/sem_ir/inst_fingerprinter.cpp

@@ -13,6 +13,7 @@
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StableHashing.h"
+#include "toolchain/base/fixed_size_value_store.h"
 #include "toolchain/base/value_ids.h"
 #include "toolchain/sem_ir/entity_with_params_base.h"
 #include "toolchain/sem_ir/ids.h"
@@ -22,6 +23,10 @@ namespace Carbon::SemIR {
 
 namespace {
 struct Worklist {
+  using FingerprintStore = FixedSizeValueStore<InstId, uint64_t>;
+  using FilesFingerprintStores =
+      FixedSizeValueStore<CheckIRId, FingerprintStore>;
+
   // The file containing the instruction we're currently processing.
   const File* sem_ir = nullptr;
   // The instructions we need to compute fingerprints for.
@@ -34,11 +39,31 @@ struct Worklist {
   llvm::SmallVector<llvm::stable_hash> contents = {};
   // Known cached instruction fingerprints. Each item in `todo` will be added to
   // the cache if not already present.
-  Map<std::pair<const File*, InstId>, uint64_t>* fingerprints;
+  FilesFingerprintStores* fingerprints;
 
   // Finish fingerprinting and compute the fingerprint.
   auto Finish() -> uint64_t { return llvm::stable_hash_combine(contents); }
 
+  // Gets the known fingerprint from the cache, or returns 0.
+  auto GetFingerprint(const File* file, InstId inst_id) -> uint64_t {
+    auto& store = fingerprints->Get(file->check_ir_id());
+    if (store.size() == 0) {
+      return 0;
+    }
+    return store.Get(inst_id);
+  }
+
+  // Sets the fingerprint for an instruction in the cache. Since 0 is used to
+  // indicate empty, we map 0 to another fixed value.
+  auto SetFingerprint(const File* file, InstId inst_id, uint64_t fingerprint) {
+    auto& store = fingerprints->Get(file->check_ir_id());
+    if (store.size() == 0) {
+      store = FixedSizeValueStore<InstId, uint64_t>::MakeWithExplicitSize(
+          file->insts().size(), 0);
+    }
+    store.Set(inst_id, fingerprint ? fingerprint : 1);
+  }
+
   // Add an invalid marker to the contents. This is used when the entity
   // contains a `None` ID. This uses an arbitrary fixed value that is assumed
   // to be unlikely to collide with a valid value.
@@ -96,11 +121,11 @@ struct Worklist {
       AddInvalid();
       return;
     }
-    if (auto lookup = fingerprints->Lookup(std::pair(file, inner_id))) {
-      contents.push_back(lookup.value());
-    } else {
-      todo.push_back({file, inner_id});
+    if (auto fingerprint = GetFingerprint(file, inner_id)) {
+      contents.push_back(fingerprint);
+      return;
     }
+    todo.push_back({file, inner_id});
   }
 
   auto Add(InstId inner_id) -> void { AddInFile(sem_ir, inner_id); }
@@ -406,11 +431,10 @@ struct Worklist {
 
       // If we already have a fingerprint for this instruction, we have nothing
       // to do. Just pop it from `todo`.
-      if (auto lookup =
-              fingerprints->Lookup(std::pair(next_sem_ir, next_inst_id))) {
+      if (auto fingerprint = GetFingerprint(next_sem_ir, next_inst_id)) {
         todo.pop_back();
         if (todo.empty()) {
-          return lookup.value();
+          return fingerprint;
         }
         continue;
       }
@@ -438,7 +462,7 @@ struct Worklist {
       // we can compute its fingerprint once we've finished the work we added.
       if (todo.size() == init_size) {
         uint64_t fingerprint = Finish();
-        fingerprints->Insert(std::pair(next_sem_ir, next_inst_id), fingerprint);
+        SetFingerprint(next_sem_ir, next_inst_id, fingerprint);
         todo.pop_back();
         if (todo.empty()) {
           return fingerprint;

+ 11 - 1
toolchain/sem_ir/inst_fingerprinter.h

@@ -5,6 +5,7 @@
 #ifndef CARBON_TOOLCHAIN_SEM_IR_INST_FINGERPRINTER_H_
 #define CARBON_TOOLCHAIN_SEM_IR_INST_FINGERPRINTER_H_
 
+#include "toolchain/base/fixed_size_value_store.h"
 #include "toolchain/sem_ir/file.h"
 #include "toolchain/sem_ir/ids.h"
 
@@ -14,6 +15,12 @@ namespace Carbon::SemIR {
 // stable across compilations and across minor changes to the compiler.
 class InstFingerprinter {
  public:
+  explicit InstFingerprinter(int total_ir_count)
+      : fingerprints_(FilesFingerprintStores::MakeWithExplicitSizeFrom(
+            total_ir_count, [] {
+              return FingerprintStore::MakeForOverwriteWithExplicitSize(0);
+            })) {}
+
   // Gets or computes a fingerprint for the given instruction.
   auto GetOrCompute(const File* file, InstId inst_id) -> uint64_t;
 
@@ -32,7 +39,10 @@ class InstFingerprinter {
   // the `GetOrCompute` overload for `InstBlockId`s, and may save some work if
   // the same canonical inst block is used by multiple instructions, for example
   // as a specific argument list.
-  Map<std::pair<const File*, InstId>, uint64_t> fingerprints_;
+  using FingerprintStore = FixedSizeValueStore<InstId, uint64_t>;
+  using FilesFingerprintStores =
+      FixedSizeValueStore<CheckIRId, FingerprintStore>;
+  FilesFingerprintStores fingerprints_;
 };
 
 }  // namespace Carbon::SemIR

+ 3 - 2
toolchain/sem_ir/inst_namer.cpp

@@ -78,7 +78,8 @@ class InstNamer::NamingContext {
   Inst inst_;
 };
 
-InstNamer::InstNamer(const File* sem_ir) : sem_ir_(sem_ir) {
+InstNamer::InstNamer(const File* sem_ir, int total_ir_count)
+    : sem_ir_(sem_ir), fingerprinter_(total_ir_count) {
   insts_.resize(sem_ir->insts().size(), {ScopeId::None, Namespace::Name()});
   labels_.resize(sem_ir->inst_blocks().size());
   scopes_.resize(GetScopeIdOffset(ScopeIdTypeEnum::None));
@@ -644,7 +645,7 @@ auto InstNamer::NamingContext::AddInstName(std::string name) -> void {
       loc_id_or_fingerprint = LocId(inst_id_);
     }
     auto scoped_name = inst_namer_->GetScopeInfo(scope_id_).insts.AllocateName(
-        *inst_namer_, loc_id_or_fingerprint, name);
+        *inst_namer_, loc_id_or_fingerprint, std::move(name));
     inst_namer_->insts_[inst_id_.index] = {scope_id_, scoped_name};
   } else {
     CARBON_CHECK(old_scope_id == scope_id_,

+ 1 - 1
toolchain/sem_ir/inst_namer.h

@@ -43,7 +43,7 @@ class InstNamer {
 
   // Construct the instruction namer, and assign names to all instructions in
   // the provided file.
-  explicit InstNamer(const File* sem_ir);
+  explicit InstNamer(const File* sem_ir, int total_ir_count);
 
   // Returns the scope ID corresponding to an ID of a function, class, or
   // interface.