Files
carbon-lang/toolchain/sem_ir/generic.cpp
T
Chandler Carruth d010d52f37 Switch the ValueStore-related templates to use explicit instantiation (#7116)
As part of this, move functions that seem reasonable to make out-of-line
to a separate `_impl.h` header file that is only included where the
explicit instantiation _definition_ is provided.

By using explicit instantiation we can make these templates behave more
like non-template classes in terms of supporting out-of-line definitions
that don't need to be compiled by every translation unit. The set of
eventual instantiations here is fundamentally known, and there tend to
be headers that define a canonical "leaf" type where it makes sense to
trigger the explicit instantiation.

Where we already had a `.cpp` file to put the explicit instantiation
definition, use it. But in some places we didn't have such a `.cpp` file
so this PR adds those.

This also requires that we have precise constraints on APIs that _can't_
be instantiated for specific argument types, as now we don't do this
lazily.

Combined, this appears to reduce the sum of object file sizes in the
`check` directory by almost 40% (122mb -> 74mb) in my measurement.

My actual goal was to improve compile times, but so far I don't have a
great methodology for measuring these... But the object file size
reduction seems to confirm this is a net win and likely represents a
non-trivial improvement in compile time.

Assisted-by: Antigravity with Gemini
2026-05-14 08:29:17 +00:00

148 lines
5.7 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
#include "toolchain/sem_ir/generic.h"
#include "toolchain/base/value_store_impl.h"
#include "toolchain/sem_ir/file.h"
#include "toolchain/sem_ir/typed_insts.h"
namespace Carbon::SemIR {
class SpecificStore::KeyContext : public TranslatingKeyContext<KeyContext> {
public:
// A lookup key for a specific.
struct Key {
GenericId generic_id;
InstBlockId args_id;
friend auto operator==(const Key&, const Key&) -> bool = default;
};
explicit KeyContext(const ValueStore* specifics) : specifics_(specifics) {}
auto TranslateKey(SpecificId id) const -> Key {
const auto& specific = specifics_->Get(id);
return {.generic_id = specific.generic_id, .args_id = specific.args_id};
}
private:
const ValueStore* specifics_;
};
auto SpecificStore::GetOrAdd(GenericId generic_id, InstBlockId args_id)
-> SpecificId {
CARBON_CHECK(generic_id.has_value());
return lookup_table_
.Insert(
KeyContext::Key{.generic_id = generic_id, .args_id = args_id},
[&] {
return specifics_.Add(
{.generic_id = generic_id, .args_id = args_id});
},
KeyContext(&specifics_))
.key();
}
auto SpecificStore::CollectMemUsage(MemUsage& mem_usage,
llvm::StringRef label) const -> void {
mem_usage.Collect(MemUsage::ConcatLabel(label, "specifics_"), specifics_);
mem_usage.Collect(MemUsage::ConcatLabel(label, "lookup_table_"),
lookup_table_, KeyContext(&specifics_));
}
static auto GetConstantInSpecific(const File& specific_ir,
SpecificId specific_id, const File& const_ir,
ConstantId const_id)
-> std::pair<const File*, ConstantId> {
if (!const_id.is_symbolic()) {
// The constant does not depend on a generic parameter.
return {&const_ir, const_id};
}
const auto& symbolic =
const_ir.constant_values().GetSymbolicConstant(const_id);
if (!symbolic.generic_id.has_value()) {
// The constant is an unattached symbolic constant, not associated with some
// particular generic.
return {&const_ir, const_id};
}
if (!specific_id.has_value()) {
// We have a generic constant but no specific. We treat this as a request
// for the value that should be used within the generic itself, which is the
// unattached constant.
return {&const_ir, const_ir.constant_values().Get(symbolic.inst_id)};
}
const auto& specific = specific_ir.specifics().Get(specific_id);
// TODO: Enforce this check even if the generic and specific are in different
// IRs.
CARBON_CHECK(
&specific_ir != &const_ir || specific.generic_id == symbolic.generic_id,
"Given a specific for the wrong generic");
auto value_block_id = specific.GetValueBlock(symbolic.index.region());
if (!value_block_id.has_value()) {
// For the self specific, we can see queries before the definition is
// resolved. Return the unattached constant value.
CARBON_CHECK(
specific_ir.generics().GetSelfSpecific(
specific_ir.specifics().Get(specific_id).generic_id) == specific_id,
"Queried {0} {1} in {2} for generic {3} before it was resolved.",
symbolic.index, const_ir.insts().Get(symbolic.inst_id), specific_id,
specific_ir.insts().Get(
specific_ir.generics().Get(specific.generic_id).decl_id));
// TODO: Make sure this is the same value that we put in the self specific
// when it's resolved. Consider not building value blocks for a self
// specific.
return {&const_ir, const_ir.constant_values().Get(symbolic.inst_id)};
}
return {&specific_ir,
specific_ir.constant_values().Get(specific_ir.inst_blocks().Get(
value_block_id)[symbolic.index.index()])};
}
auto GetConstantValueInSpecific(const File& sem_ir, SpecificId specific_id,
InstId inst_id) -> ConstantId {
return GetConstantInSpecific(sem_ir, specific_id, sem_ir,
sem_ir.constant_values().GetAttached(inst_id))
.second;
}
auto GetConstantValueInSpecific(const File& specific_ir, SpecificId specific_id,
const File& inst_ir, InstId inst_id)
-> std::pair<const File*, ConstantId> {
return GetConstantInSpecific(specific_ir, specific_id, inst_ir,
inst_ir.constant_values().GetAttached(inst_id));
}
auto GetTypeOfInstInSpecific(const File& sem_ir, SpecificId specific_id,
InstId inst_id) -> TypeId {
auto type_id = sem_ir.insts().GetAttachedType(inst_id);
auto const_id = sem_ir.types().GetConstantId(type_id);
auto [_, specific_const_id] =
GetConstantInSpecific(sem_ir, specific_id, sem_ir, const_id);
return TypeId::ForTypeConstant(specific_const_id);
}
auto GetTypeOfInstInSpecific(const File& specific_ir, SpecificId specific_id,
const File& inst_ir, InstId inst_id)
-> std::pair<const File*, TypeId> {
auto type_id = inst_ir.insts().GetAttachedType(inst_id);
auto const_id = inst_ir.types().GetConstantId(type_id);
auto [result_ir, result_const_id] =
GetConstantInSpecific(specific_ir, specific_id, inst_ir, const_id);
return {result_ir, TypeId::ForTypeConstant(result_const_id)};
}
} // namespace Carbon::SemIR
namespace Carbon {
template class ValueStore<SemIR::GenericId, SemIR::Generic,
Tag<SemIR::CheckIRId>>;
template class ValueStore<SemIR::SpecificId, SemIR::Specific,
Tag<SemIR::CheckIRId>>;
} // namespace Carbon