diff --git a/toolchain/base/fixed_size_value_store.h b/toolchain/base/fixed_size_value_store.h index 862d3421f2d8..19bd475f8677 100644 --- a/toolchain/base/fixed_size_value_store.h +++ b/toolchain/base/fixed_size_value_store.h @@ -11,30 +11,62 @@ #include "llvm/ADT/StringRef.h" #include "toolchain/base/mem_usage.h" #include "toolchain/base/value_store.h" +#include "toolchain/base/value_store_chunk.h" namespace Carbon { // A value store with a predetermined size. template -class FixedSizeValueStore : public MoveOnly> { +class FixedSizeValueStore { public: + using IdType = IdT; using ValueType = ValueStoreTypes::ValueType; using RefType = ValueStoreTypes::RefType; using ConstRefType = ValueStoreTypes::ConstRefType; // Makes a ValueStore of the specified size, but without initializing values. // Entries must be set before reading. - static auto MakeForOverwrite(size_t size) -> FixedSizeValueStore { + static auto MakeForOverwriteWithExplicitSize(size_t size) + -> FixedSizeValueStore { FixedSizeValueStore store; store.values_.resize_for_overwrite(size); return store; } - // Makes a ValueStore of the specified size, initialized to a default. - explicit FixedSizeValueStore(size_t size, ValueT default_value) { - values_.resize(size, default_value); + // Makes a ValueStore of the same size as a source `ValueStoreT`, but without + // initializing values. Entries must be set before reading. + template + requires std::same_as + static auto MakeForOverwrite(const ValueStoreT& size_source) + -> FixedSizeValueStore { + FixedSizeValueStore store; + store.values_.resize_for_overwrite(size_source.size()); + return store; } + // Makes a ValueStore of the specified size, initialized to a default. + static auto MakeWithExplicitSize(size_t size, ValueT default_value) + -> FixedSizeValueStore { + FixedSizeValueStore store; + store.values_.resize(size, default_value); + 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. + template + requires std::same_as + explicit FixedSizeValueStore(const ValueStoreT& size_source, + ValueT default_value) { + values_.resize(size_source.size(), default_value); + } + + // Move-only. + FixedSizeValueStore(FixedSizeValueStore&&) noexcept = default; + auto operator=(FixedSizeValueStore&&) noexcept + -> FixedSizeValueStore& = default; + // Sets the value for an ID. auto Set(IdT id, ValueType value) -> void { CARBON_DCHECK(id.index >= 0, "{0}", id); @@ -60,9 +92,12 @@ class FixedSizeValueStore : public MoveOnly> { } auto size() const -> size_t { return values_.size(); } + auto values() -> auto { + return llvm::make_range(values_.begin(), values_.end()); + } private: - // Allow default construction for `MakeForOverwrite`. + // Allow default construction for `Make` functions. FixedSizeValueStore() = default; // Storage for the `ValueT` objects, indexed by the id. diff --git a/toolchain/base/value_store.h b/toolchain/base/value_store.h index 4ba03771deeb..d1c70187228c 100644 --- a/toolchain/base/value_store.h +++ b/toolchain/base/value_store.h @@ -65,6 +65,7 @@ class ValueStore typename IdT::ValueType>, Yaml::Printable>, Internal::ValueStoreNotPrintable> { public: + using IdType = IdT; using ValueType = ValueStoreTypes::ValueType; using RefType = ValueStoreTypes::RefType; using ConstRefType = ValueStoreTypes::ConstRefType; diff --git a/toolchain/lower/BUILD b/toolchain/lower/BUILD index c86ca22509e2..a8988aedc58f 100644 --- a/toolchain/lower/BUILD +++ b/toolchain/lower/BUILD @@ -50,6 +50,7 @@ cc_library( "//common:map", "//common:raw_string_ostream", "//common:vlog", + "//toolchain/base:fixed_size_value_store", "//toolchain/base:kind_switch", "//toolchain/parse:tree", "//toolchain/sem_ir:absolute_node_id", diff --git a/toolchain/lower/constant.cpp b/toolchain/lower/constant.cpp index 20098d96a265..f26cc3759032 100644 --- a/toolchain/lower/constant.cpp +++ b/toolchain/lower/constant.cpp @@ -18,7 +18,7 @@ namespace Carbon::Lower { class ConstantContext { public: explicit ConstantContext(FileContext& file_context, - llvm::MutableArrayRef constants) + const FileContext::LoweredConstantStore* constants) : file_context_(&file_context), constants_(constants) {} // Gets the lowered constant value for an instruction, which must have a @@ -40,8 +40,7 @@ class ConstantContext { // This constant hasn't been lowered. return nullptr; } - CARBON_CHECK(inst_id.index >= 0); - return constants_[inst_id.index]; + return constants_->Get(inst_id); } // Returns a constant for the case of a value that should never be used. @@ -91,7 +90,7 @@ class ConstantContext { private: FileContext* file_context_; - llvm::MutableArrayRef constants_; + const FileContext::LoweredConstantStore* constants_; int32_t last_lowered_constant_index_ = -1; }; @@ -283,8 +282,8 @@ static auto MaybeEmitAsConstant(ConstantContext& context, InstT inst) } auto LowerConstants(FileContext& file_context, - llvm::MutableArrayRef constants) -> void { - ConstantContext context(file_context, constants); + FileContext::LoweredConstantStore& constants) -> void { + ConstantContext context(file_context, &constants); // Lower each constant in InstId order. This guarantees we lower the // dependencies of a constant before we lower the constant itself. for (auto [inst_id, const_id] : @@ -317,7 +316,7 @@ auto LowerConstants(FileContext& file_context, #include "toolchain/sem_ir/inst_kind.def" } - constants[inst_id.index] = value; + constants.Set(inst_id, value); context.SetLastLoweredConstantIndex(inst_id.index); } } diff --git a/toolchain/lower/constant.h b/toolchain/lower/constant.h index 5175553bbf0a..54fc729a1593 100644 --- a/toolchain/lower/constant.h +++ b/toolchain/lower/constant.h @@ -6,6 +6,7 @@ #define CARBON_TOOLCHAIN_LOWER_CONSTANT_H_ #include "llvm/ADT/ArrayRef.h" +#include "toolchain/base/fixed_size_value_store.h" #include "toolchain/lower/file_context.h" namespace Carbon::Lower { @@ -15,7 +16,7 @@ namespace Carbon::Lower { // concrete constant instructions are populated with corresponding constant // values. auto LowerConstants(FileContext& file_context, - llvm::MutableArrayRef constants) -> void; + FileContext::LoweredConstantStore& constants) -> void; } // namespace Carbon::Lower diff --git a/toolchain/lower/file_context.cpp b/toolchain/lower/file_context.cpp index 9325456b6d60..1186a2d361ba 100644 --- a/toolchain/lower/file_context.cpp +++ b/toolchain/lower/file_context.cpp @@ -42,7 +42,17 @@ FileContext::FileContext(Context& context, const SemIR::File& sem_ir, : context_(&context), sem_ir_(&sem_ir), inst_namer_(inst_namer), - vlog_stream_(vlog_stream) { + vlog_stream_(vlog_stream), + functions_(LoweredFunctionStore::MakeForOverwrite(sem_ir.functions())), + specific_functions_(sem_ir.specifics(), nullptr), + types_(LoweredTypeStore::MakeWithExplicitSize(sem_ir.insts().size(), + nullptr)), + constants_(LoweredConstantStore::MakeWithExplicitSize( + sem_ir.insts().size(), nullptr)), + lowered_specifics_(sem_ir.generics(), {}), + lowered_specifics_type_fingerprint_(sem_ir.specifics(), {}), + lowered_specific_fingerprint_(sem_ir.specifics(), {}), + equivalent_specifics_(sem_ir.specifics(), SemIR::SpecificId::None) { // Initialization that relies on invariants of the class. cpp_code_generator_ = CreateCppCodeGenerator(); CARBON_CHECK(!sem_ir.has_errors(), @@ -59,32 +69,18 @@ auto FileContext::PrepareToLower() -> void { } // Lower all types that were required to be complete. - types_.resize(sem_ir_->insts().size()); for (auto type_id : sem_ir_->types().complete_types()) { if (type_id.index >= 0) { - types_[type_id.index] = BuildType(sem_ir_->types().GetInstId(type_id)); + types_.Set(type_id, BuildType(sem_ir_->types().GetInstId(type_id))); } } // Lower function declarations. - functions_.resize_for_overwrite(sem_ir_->functions().size()); for (auto [id, _] : sem_ir_->functions().enumerate()) { - functions_[id.index] = BuildFunctionDecl(id); + functions_.Set(id, BuildFunctionDecl(id)); } - // Specific functions are lowered when we emit a reference to them. - specific_functions_.resize(sem_ir_->specifics().size()); - // Additional data stored for specifics, for when attempting to coalesce. - // Indexed by `GenericId`. - lowered_specifics_.resize(sem_ir_->generics().size()); - // Indexed by `SpecificId`. - lowered_specifics_type_fingerprint_.resize(sem_ir_->specifics().size()); - lowered_specific_fingerprint_.resize(sem_ir_->specifics().size()); - equivalent_specifics_.resize(sem_ir_->specifics().size(), - SemIR::SpecificId::None); - // Lower constants. - constants_.resize(sem_ir_->insts().size()); LowerConstants(*this, constants_); } @@ -127,7 +123,7 @@ auto FileContext::LowerDefinitions() -> void { for (auto [id, fn_info] : sem_ir_->functions().enumerate()) { // If we created a declaration and the function definition is not imported, // build a definition. - if (functions_[id.index] && fn_info.definition_id.has_value() && + if (functions_.Get(id) && fn_info.definition_id.has_value() && !sem_ir().insts().GetImportSource(fn_info.definition_id).has_value()) { BuildFunctionDefinition(id); } @@ -187,13 +183,13 @@ auto FileContext::ContainsPair( } auto FileContext::CoalesceEquivalentSpecifics() -> void { - for (auto& specifics : lowered_specifics_) { + for (auto& specifics : lowered_specifics_.values()) { // i cannot be unsigned due to the comparison with a negative number when // the specifics vector is empty. for (int i = 0; i < static_cast(specifics.size()) - 1; ++i) { // This specific was already replaced, skip it. - if (equivalent_specifics_[specifics[i].index].has_value() && - equivalent_specifics_[specifics[i].index] != specifics[i]) { + if (equivalent_specifics_.Get(specifics[i]).has_value() && + equivalent_specifics_.Get(specifics[i]) != specifics[i]) { specifics[i] = specifics[specifics.size() - 1]; specifics.pop_back(); --i; @@ -203,8 +199,8 @@ auto FileContext::CoalesceEquivalentSpecifics() -> void { // `lowered_specifics_type_fingerprint_` and `common_fingerprint`. for (int j = i + 1; j < static_cast(specifics.size()); ++j) { // When the specific was already replaced, skip it. - if (equivalent_specifics_[specifics[j].index].has_value() && - equivalent_specifics_[specifics[j].index] != specifics[j]) { + if (equivalent_specifics_.Get(specifics[j]).has_value() && + equivalent_specifics_.Get(specifics[j]) != specifics[j]) { specifics[j] = specifics[specifics.size() - 1]; specifics.pop_back(); --j; @@ -243,10 +239,9 @@ auto FileContext::CoalesceEquivalentSpecifics() -> void { // Delete function bodies for already replaced functions. for (auto specific_id : specifics_to_delete) { - specific_functions_[specific_id.index]->eraseFromParent(); - specific_functions_[specific_id.index] = - specific_functions_[equivalent_specifics_[specific_id.index] - .index]; + specific_functions_.Get(specific_id)->eraseFromParent(); + specific_functions_.Get(specific_id) = + specific_functions_.Get(equivalent_specifics_.Get(specific_id)); } // Removed the replaced specific from the list of emitted specifics. @@ -272,10 +267,10 @@ auto FileContext::ProcessSpecificEquivalence( "Expected values in equivalence check"); auto get_canon = [&](SemIR::SpecificId specific_id) { - return equivalent_specifics_[specific_id.index].has_value() + return equivalent_specifics_.Get(specific_id).has_value() ? std::make_pair( - equivalent_specifics_[specific_id.index], - (equivalent_specifics_[specific_id.index] != specific_id)) + equivalent_specifics_.Get(specific_id), + (equivalent_specifics_.Get(specific_id) != specific_id)) : std::make_pair(specific_id, false); }; auto [canon_id1, replaced_before1] = get_canon(specific_id1); @@ -295,10 +290,10 @@ auto FileContext::ProcessSpecificEquivalence( // Update equivalent_specifics_ for all. This is used as an indicator that // this specific_id may be the canonical one when reducing the equivalence // chains in `IsKnownEquivalence`. - equivalent_specifics_[specific_id1.index] = canon_id1; - equivalent_specifics_[specific_id2.index] = canon_id1; - specific_functions_[canon_id2.index]->replaceAllUsesWith( - specific_functions_[canon_id1.index]); + equivalent_specifics_.Set(specific_id1, canon_id1); + equivalent_specifics_.Set(specific_id2, canon_id1); + specific_functions_.Get(canon_id2)->replaceAllUsesWith( + specific_functions_.Get(canon_id1)); if (!replaced_before2) { specifics_to_delete.push_back(canon_id2); } @@ -306,39 +301,40 @@ auto FileContext::ProcessSpecificEquivalence( auto FileContext::IsKnownEquivalence(SemIR::SpecificId specific_id1, SemIR::SpecificId specific_id2) -> bool { - if (!equivalent_specifics_[specific_id1.index].has_value() || - !equivalent_specifics_[specific_id2.index].has_value()) { + if (!equivalent_specifics_.Get(specific_id1).has_value() || + !equivalent_specifics_.Get(specific_id2).has_value()) { return false; } auto update_equivalent_specific = [&](SemIR::SpecificId specific_id) { llvm::SmallVector stack; SemIR::SpecificId specific_to_update = specific_id; - while (equivalent_specifics_[equivalent_specifics_[specific_to_update.index] - .index] != - equivalent_specifics_[specific_to_update.index]) { + while (equivalent_specifics_.Get( + equivalent_specifics_.Get(specific_to_update)) != + equivalent_specifics_.Get(specific_to_update)) { stack.push_back(specific_to_update); - specific_to_update = equivalent_specifics_[specific_to_update.index]; + specific_to_update = equivalent_specifics_.Get(specific_to_update); } for (auto specific : llvm::reverse(stack)) { - equivalent_specifics_[specific.index] = - equivalent_specifics_[equivalent_specifics_[specific.index].index]; + equivalent_specifics_.Set( + specific, + equivalent_specifics_.Get(equivalent_specifics_.Get(specific))); } }; update_equivalent_specific(specific_id1); update_equivalent_specific(specific_id2); - return equivalent_specifics_[specific_id1.index] == - equivalent_specifics_[specific_id2.index]; + return equivalent_specifics_.Get(specific_id1) == + equivalent_specifics_.Get(specific_id2); } auto FileContext::AreFunctionTypesEquivalent(SemIR::SpecificId specific_id1, SemIR::SpecificId specific_id2) -> bool { CARBON_CHECK(specific_id1.has_value() && specific_id2.has_value()); - return lowered_specifics_type_fingerprint_[specific_id1.index] == - lowered_specifics_type_fingerprint_[specific_id2.index]; + return lowered_specifics_type_fingerprint_.Get(specific_id1) == + lowered_specifics_type_fingerprint_.Get(specific_id2); } auto FileContext::AreFunctionBodiesEquivalent( @@ -352,8 +348,8 @@ auto FileContext::AreFunctionBodiesEquivalent( auto outer_pair = worklist.pop_back_val(); auto [specific_id1, specific_id2] = outer_pair; - auto state1 = lowered_specific_fingerprint_[specific_id1.index]; - auto state2 = lowered_specific_fingerprint_[specific_id2.index]; + auto state1 = lowered_specific_fingerprint_.Get(specific_id1); + auto state2 = lowered_specific_fingerprint_.Get(specific_id2); if (state1.common_fingerprint != state2.common_fingerprint) { InsertPair(specific_id1, specific_id2, non_equivalent_specifics_); return false; @@ -409,7 +405,7 @@ auto FileContext::CreateCppCodeGenerator() auto FileContext::GetConstant(SemIR::ConstantId const_id, SemIR::InstId use_inst_id) -> llvm::Value* { auto const_inst_id = sem_ir().constant_values().GetInstId(const_id); - auto* const_value = constants_[const_inst_id.index]; + auto* const_value = constants_.Get(const_inst_id); // For value expressions and initializing expressions, the value produced by // a constant instruction is a value representation of the constant. For @@ -626,7 +622,7 @@ auto FileContext::HandleReferencedSpecificFunction( llvm_type->print(os); function_type_fingerprint.update(os.TakeStr()); function_type_fingerprint.final( - lowered_specifics_type_fingerprint_[specific_id.index]); + lowered_specifics_type_fingerprint_.Get(specific_id)); } auto FileContext::BuildFunctionDecl(SemIR::FunctionId function_id, diff --git a/toolchain/lower/file_context.h b/toolchain/lower/file_context.h index fc7c8df8e00a..3c7e9ff7a8d5 100644 --- a/toolchain/lower/file_context.h +++ b/toolchain/lower/file_context.h @@ -20,6 +20,9 @@ namespace Carbon::Lower { // Context and shared functionality for lowering within a SemIR file. class FileContext { public: + using LoweredConstantStore = + FixedSizeValueStore; + // Describes a specific function's body fingerprint. struct SpecificFunctionFingerprint { // Fingerprint with all specific-dependent instructions, except specific @@ -72,9 +75,9 @@ class FileContext { CARBON_CHECK(type_id.has_value(), "Should not be called with `None`"); CARBON_CHECK(type_id.is_concrete(), "Lowering symbolic type {0}: {1}", type_id, sem_ir().types().GetAsInst(type_id)); - CARBON_CHECK(types_[type_id.index], "Missing type {0}: {1}", type_id, + CARBON_CHECK(types_.Get(type_id), "Missing type {0}: {1}", type_id, sem_ir().types().GetAsInst(type_id)); - return types_[type_id.index]; + return types_.Get(type_id); } // Returns location information for use with DebugInfo. @@ -145,8 +148,8 @@ class FileContext { // Gets the location in which a callable's function is stored. auto GetFunctionAddr(SemIR::FunctionId function_id, SemIR::SpecificId specific_id) -> llvm::Function** { - return specific_id.has_value() ? &specific_functions_[specific_id.index] - : &functions_[function_id.index]; + return specific_id.has_value() ? &specific_functions_.Get(specific_id) + : &functions_.Get(function_id); } // Notes that a C++ function has been referenced for the first time, so we @@ -195,7 +198,7 @@ class FileContext { // by one while lowering their definitions. auto AddLoweredSpecificForGeneric(SemIR::GenericId generic_id, SemIR::SpecificId specific_id) { - lowered_specifics_[generic_id.index].push_back(specific_id); + lowered_specifics_.Get(generic_id).push_back(specific_id); } // Initializes and returns a SpecificFunctionFingerprint* instance for a @@ -206,7 +209,7 @@ class FileContext { if (!specific_id.has_value()) { return nullptr; } - return &lowered_specific_fingerprint_[specific_id.index]; + return &lowered_specific_fingerprint_.Get(specific_id); } // Entry point for coalescing equivalent specifics. Two function definitions, @@ -282,44 +285,38 @@ class FileContext { // Maps callables to lowered functions. SemIR treats callables as the // canonical form of a function, so lowering needs to do the same. - // Vector indexes correspond to `FunctionId` indexes. We resize this directly - // to the correct size. - llvm::SmallVector functions_; + using LoweredFunctionStore = + FixedSizeValueStore; + LoweredFunctionStore functions_; - // Maps specific callables to lowered functions. Vector indexes correspond to - // `SpecificId` indexes. We resize this directly to the correct size. - llvm::SmallVector specific_functions_; + // Maps specific callables to lowered functions. + FixedSizeValueStore specific_functions_; - // Provides lowered versions of types. - // Vector indexes correspond to `TypeId` indexes for non-symbolic types. We - // resize this directly to the (often large) correct size. - llvm::SmallVector types_; + // Provides lowered versions of types. Entries are non-symbolic types. + using LoweredTypeStore = FixedSizeValueStore; + LoweredTypeStore types_; - // Maps constants to their lowered values. - // Vector indexes correspond to `InstId` indexes for constant instructions. We - // resize this directly to the (often large) correct size. - llvm::SmallVector constants_; + // Maps constants to their lowered values. Indexes are the `InstId` for + // constant instructions. + LoweredConstantStore constants_; // Maps global variables to their lowered variant. Map global_variables_; // For a generic function, keep track of the specifics for which LLVM // function declarations were created. Those can be retrieved then from - // `specific_functions_`. We resize this to the correct size. Vector indexes - // correspond to `GenericId` indexes. - llvm::SmallVector, 0> lowered_specifics_; + // `specific_functions_`. + FixedSizeValueStore> + lowered_specifics_; // For specifics that exist in lowered_specifics, a hash of their function - // type information: return and parameter types. We resize this to the - // correct size. Vector indexes correspond to `SpecificId` indexes. + // type information: return and parameter types. // TODO: Hashing all members of `FunctionTypeInfo` may not be necessary. - llvm::SmallVector, 0> + FixedSizeValueStore> lowered_specifics_type_fingerprint_; // This is initialized and populated while lowering a specific. - // We resize this to the correct size. Vector indexes correspond to - // `SpecificId` indexes. - llvm::SmallVector + FixedSizeValueStore lowered_specific_fingerprint_; // Equivalent specifics that have been found. For each specific, this points @@ -327,10 +324,10 @@ class FileContext { // define the canonical specific as the one with the lowest // `SpecificId.index`. // - // We resize this to the correct size and initialize to `SpecificId::None`, - // which defines that there is no other equivalent specific to this - // `SpecificId`. Vector indexes correspond to `SpecificId` indexes. - llvm::SmallVector equivalent_specifics_; + // Entries are initialized to `SpecificId::None`, which defines that there is + // no other equivalent specific to this `SpecificId`. + FixedSizeValueStore + equivalent_specifics_; // Non-equivalent specifics found. // TODO: Revisit this due to its quadratic space growth. diff --git a/toolchain/parse/tree_and_subtrees.cpp b/toolchain/parse/tree_and_subtrees.cpp index 134545e7c246..e55c230f216b 100644 --- a/toolchain/parse/tree_and_subtrees.cpp +++ b/toolchain/parse/tree_and_subtrees.cpp @@ -16,7 +16,8 @@ TreeAndSubtrees::TreeAndSubtrees(const Lex::TokenizedBuffer& tokens, const Tree& tree) : tokens_(&tokens), tree_(&tree), - subtree_sizes_(SubtreeSizeStore::MakeForOverwrite(tree_->size())) { + subtree_sizes_( + SubtreeSizeStore::MakeForOverwriteWithExplicitSize(tree_->size())) { // A stack of nodes which haven't yet been used as children. llvm::SmallVector size_stack; for (auto n : tree.postorder()) { diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index 3369a4df93f4..4378d565d272 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -44,7 +44,7 @@ Formatter::Formatter(const File* sem_ir, use_dump_sem_ir_ranges_(use_dump_sem_ir_ranges), // Create a placeholder visible chunk and assign it to all instructions // that don't have a chunk of their own. - tentative_inst_chunks_(sem_ir_->insts().size(), AddChunkNoFlush(true)) { + tentative_inst_chunks_(sem_ir_->insts(), AddChunkNoFlush(true)) { if (use_dump_sem_ir_ranges_) { ComputeNodeParents(); } @@ -103,8 +103,8 @@ auto Formatter::Format() -> void { auto Formatter::ComputeNodeParents() -> void { CARBON_CHECK(!node_parents_); - node_parents_ = - NodeParentStore(sem_ir_->parse_tree().size(), Parse::NodeId::None); + node_parents_ = NodeParentStore::MakeWithExplicitSize( + sem_ir_->parse_tree().size(), Parse::NodeId::None); for (auto n : sem_ir_->parse_tree().postorder()) { for (auto child : get_tree_and_subtrees_().children(n)) { node_parents_->Set(child, n); diff --git a/toolchain/sem_ir/generic.h b/toolchain/sem_ir/generic.h index 747bb92b5c74..dbfddfd32133 100644 --- a/toolchain/sem_ir/generic.h +++ b/toolchain/sem_ir/generic.h @@ -104,6 +104,8 @@ struct Specific : Printable { // their associated generic argument list. class SpecificStore : public Yaml::Printable { public: + using IdType = SpecificId; + // Adds a new specific, or gets the existing specific for a specified generic // and argument list. Returns the ID of the specific. The argument IDs must be // for instructions in the constant block, and must be a canonical instruction diff --git a/toolchain/sem_ir/inst.h b/toolchain/sem_ir/inst.h index eaff4e0cfcc0..83984427ddc6 100644 --- a/toolchain/sem_ir/inst.h +++ b/toolchain/sem_ir/inst.h @@ -389,6 +389,8 @@ struct LocIdAndInst { // Provides a ValueStore wrapper for an API specific to instructions. class InstStore { public: + using IdType = InstId; + explicit InstStore(File* file) : file_(file) {} // Adds an instruction to the instruction list, returning an ID to reference