// 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/inst_namer.h" #include #include #include #include "common/ostream.h" #include "common/raw_string_ostream.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StableHashing.h" #include "toolchain/base/kind_switch.h" #include "toolchain/base/shared_value_stores.h" #include "toolchain/base/value_ids.h" #include "toolchain/lex/tokenized_buffer.h" #include "toolchain/parse/tree.h" #include "toolchain/sem_ir/cpp_overload_set.h" #include "toolchain/sem_ir/entity_with_params_base.h" #include "toolchain/sem_ir/function.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst_kind.h" #include "toolchain/sem_ir/pattern.h" #include "toolchain/sem_ir/singleton_insts.h" #include "toolchain/sem_ir/specific_interface.h" #include "toolchain/sem_ir/specific_named_constraint.h" #include "toolchain/sem_ir/type_info.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::SemIR { class InstNamer::NamingContext { public: explicit NamingContext(InstNamer* inst_namer, InstNamer::ScopeId scope_id, InstId inst_id); // Names the single instruction. Use bound names where available. Otherwise, // assign a backup name. // // Insts with a type_id are required to add names; other insts may // optionally set a name. All insts may push other insts to be named. auto NameInst() -> void; private: // Adds the instruction's name. auto AddInstName(std::string name) -> void; // Adds the instruction's name for a StructType, which contains the names of // its fields. auto AddStructTypeInstName(StructType struct_ty, llvm::StringRef type_suffix, llvm::StringRef name_suffix) -> void; // Adds the instruction's name by `NameId`. auto AddInstNameId(NameId name_id, llvm::StringRef suffix = "") -> void { AddInstName((sem_ir().names().GetIRBaseName(name_id) + suffix).str()); } // Names an `IntType` or `FloatType`. auto AddIntOrFloatTypeName(char type_literal_prefix, InstId bit_width_id, llvm::StringRef suffix = "") -> void; // Names an `ImplWitnessTable` instruction. auto AddWitnessTableName(InstId witness_table_inst_id, std::string name) -> void; auto AddBlockLabel(ScopeId scope_id, InstBlockId block_id, std::string name, LocId loc_id) -> void { inst_namer_->AddBlockLabel(scope_id, block_id, name, loc_id); } // Pushes all instructions in a generic, by ID. auto PushGeneric(ScopeId scope_id, GenericId generic_id) -> void { inst_namer_->PushGeneric(scope_id, generic_id); } // Pushes all instructions in a block, by ID. auto PushBlockId(ScopeId scope_id, InstBlockId block_id) -> void { inst_namer_->PushBlockId(scope_id, block_id); } // Names the instruction as an entity. May push processing of the entity. template auto AddEntityNameAndMaybePush(EntityIdT id, llvm::StringRef suffix = "") -> void { AddInstName((inst_namer_->MaybePushEntity(id) + suffix).str()); } auto sem_ir() -> const File& { return *inst_namer_->sem_ir_; } InstNamer* inst_namer_; ScopeId scope_id_; InstId inst_id_; Inst inst_; }; 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)); generic_scopes_.resize(sem_ir->generics().size(), ScopeId::None); // We process the stack between each large block in order to reduce the // temporary size of the stack. auto process_stack = [&] { while (!inst_stack_.empty() || !inst_block_stack_.empty()) { if (inst_stack_.empty()) { auto [scope_id, block_id] = inst_block_stack_.pop_back_val(); if (block_id != SemIR::InstBlockId::Empty) { PushBlockInsts(scope_id, sem_ir_->inst_blocks().Get(block_id)); } } while (!inst_stack_.empty()) { auto [scope_id, inst_id] = inst_stack_.pop_back_val(); NamingContext context(this, scope_id, inst_id); context.NameInst(); } } }; // Name each of the top-level scopes, in order. We use these as the roots of // walking the IR. PushBlockInsts(ScopeId::Constants, sem_ir->constants().array_ref()); process_stack(); PushBlockId(ScopeId::Imports, InstBlockId::Imports); process_stack(); PushBlockId(ScopeId::Generated, InstBlockId::Generated); process_stack(); PushBlockId(ScopeId::File, sem_ir->top_inst_block_id()); process_stack(); // Global init won't have any other references, so we add it directly. if (sem_ir_->global_ctor_id().has_value()) { MaybePushEntity(sem_ir_->global_ctor_id()); process_stack(); } } auto InstNamer::GetScopeIdOffset(ScopeIdTypeEnum id_enum) const -> int { int offset = 0; // For each Id type, add the number of entities *above* its case; for example, // the offset for functions excludes the functions themselves. The fallthrough // handles summing to get uniqueness; order isn't special. switch (id_enum) { case ScopeIdTypeEnum::None: // `None` will be getting a full count of scopes. offset += sem_ir_->associated_constants().size(); [[fallthrough]]; case ScopeIdTypeEnum::For: offset += sem_ir_->classes().size(); [[fallthrough]]; case ScopeIdTypeEnum::For: offset += sem_ir_->cpp_overload_sets().size(); [[fallthrough]]; case ScopeIdTypeEnum::For: offset += sem_ir_->functions().size(); [[fallthrough]]; case ScopeIdTypeEnum::For: offset += sem_ir_->impls().size(); [[fallthrough]]; case ScopeIdTypeEnum::For: offset += sem_ir_->interfaces().size(); [[fallthrough]]; case ScopeIdTypeEnum::For: offset += sem_ir_->interfaces().size(); [[fallthrough]]; case ScopeIdTypeEnum::For: offset += sem_ir_->named_constraints().size(); [[fallthrough]]; case ScopeIdTypeEnum::For: offset += sem_ir_->named_constraints().size(); [[fallthrough]]; case ScopeIdTypeEnum::For: offset += sem_ir_->require_impls().size(); [[fallthrough]]; case ScopeIdTypeEnum::For: offset += sem_ir_->specific_interfaces().size(); [[fallthrough]]; case ScopeIdTypeEnum::For: offset += sem_ir_->vtables().size(); [[fallthrough]]; case ScopeIdTypeEnum::For: // All type-specific scopes are offset by `FirstEntityScope`. offset += static_cast(ScopeId::FirstEntityScope); return offset; default: CARBON_FATAL("Unexpected ScopeIdTypeEnum: {0}", id_enum); } } auto InstNamer::GetScopeName(ScopeId scope) const -> std::string { switch (scope) { case ScopeId::None: return ""; // These are treated as SemIR keywords. case ScopeId::File: return "file"; case ScopeId::Generated: return "generated"; case ScopeId::Imports: return "imports"; case ScopeId::Constants: return "constants"; // For everything else, use an @ prefix. default: return ("@" + GetScopeInfo(scope).name.GetFullName()).str(); } } auto InstNamer::GetUnscopedNameFor(InstId inst_id) const -> llvm::StringRef { if (!inst_id.has_value()) { return ""; } if (IsSingletonInstId(inst_id)) { return sem_ir_->insts().Get(inst_id).kind().ir_name(); } auto index = sem_ir_->insts().GetRawIndex(inst_id); const auto& inst_name = insts_[index].second; return inst_name ? inst_name.GetFullName() : ""; } auto InstNamer::GetNameFor(ScopeId scope_id, InstId inst_id) const -> std::string { if (!inst_id.has_value()) { return "invalid"; } // Check for a builtin. if (IsSingletonInstId(inst_id)) { return sem_ir_->insts().Get(inst_id).kind().ir_name().str(); } if (inst_id == SemIR::Namespace::PackageInstId) { return "package"; } auto index = sem_ir_->insts().GetRawIndex(inst_id); const auto& [inst_scope, inst_name] = insts_[index]; if (!inst_name) { // This should not happen in valid IR. RawStringOstream out; out << "." << inst_id; auto loc_id = sem_ir_->insts().GetCanonicalLocId(inst_id); // TODO: Consider handling other kinds. if (loc_id.kind() == LocId::Kind::NodeId) { const auto& tree = sem_ir_->parse_tree(); auto token = tree.node_token(loc_id.node_id()); out << ".loc" << tree.tokens().GetLineNumber(token) << "_" << tree.tokens().GetColumnNumber(token); } return out.TakeStr(); } if (inst_scope == scope_id) { return ("%" + inst_name.GetFullName()).str(); } return (GetScopeName(inst_scope) + ".%" + inst_name.GetFullName()).str(); } auto InstNamer::GetUnscopedLabelFor(InstBlockId block_id) const -> llvm::StringRef { if (!block_id.has_value()) { return ""; } const auto& label_name = labels_[sem_ir_->inst_blocks().GetRawIndex(block_id)].second; return label_name ? label_name.GetFullName() : ""; } // Returns the IR name to use for a label, when referenced from a given scope. auto InstNamer::GetLabelFor(ScopeId scope_id, InstBlockId block_id) const -> std::string { if (!block_id.has_value()) { return "!invalid"; } const auto& [label_scope, label_name] = labels_[sem_ir_->inst_blocks().GetRawIndex(block_id)]; if (!label_name) { // This should not happen in valid IR. RawStringOstream out; out << ""; return out.TakeStr(); } if (label_scope == scope_id) { return ("!" + label_name.GetFullName()).str(); } return (GetScopeName(label_scope) + ".!" + label_name.GetFullName()).str(); } auto InstNamer::Namespace::Name::GetFullName() const -> llvm::StringRef { if (!value_) { return ""; } llvm::StringMapEntry* value = value_; while (value->second.ambiguous && value->second.fallback) { value = value->second.fallback.value_; } return value->first(); } auto InstNamer::Namespace::Name::GetBaseName() const -> llvm::StringRef { if (!value_) { return ""; } return value_->first().take_front(base_name_size_); } auto InstNamer::Namespace::AllocateName( const InstNamer& inst_namer, std::variant loc_id_or_fingerprint, std::string name) -> Name { // The best (shortest) name for this instruction so far, and the current // name for it. Name best; Name current; const size_t base_name_size = name.size(); // Add `name` as a name for this entity. auto add_name = [&](bool mark_ambiguous = true) { auto [it, added] = allocated_.insert({name, NameResult()}); Name new_name = Name(it, base_name_size); if (!added) { if (mark_ambiguous) { // This name was allocated for a different instruction. Mark it as // ambiguous and keep looking for a name for this instruction. new_name.SetAmbiguous(); } } else { if (!best) { best = new_name; } else { CARBON_CHECK(current); current.SetFallback(new_name); } current = new_name; } return added; }; // Use the given name if it's available. if (!name.empty()) { add_name(); } // Append location information to try to disambiguate. if (auto* loc_id = std::get_if(&loc_id_or_fingerprint)) { *loc_id = inst_namer.sem_ir_->insts().GetCanonicalLocId(*loc_id); // TODO: Consider handling other kinds. if (loc_id->kind() == LocId::Kind::NodeId) { const auto& tree = inst_namer.sem_ir_->parse_tree(); auto token = tree.node_token(loc_id->node_id()); llvm::raw_string_ostream(name) << ".loc" << tree.tokens().GetLineNumber(token); add_name(); llvm::raw_string_ostream(name) << "_" << tree.tokens().GetColumnNumber(token); add_name(); } } else { uint64_t fingerprint = std::get(loc_id_or_fingerprint); llvm::raw_string_ostream out(name); out << "."; // Include names with 3-6 characters from the fingerprint. Then fall back to // sequential numbering. for (int n : llvm::seq(1, 7)) { out.write_hex((fingerprint >> (64 - 4 * n)) & 0xF); if (n >= 3) { add_name(); } } } // Append numbers until we find an available name. name += "."; auto name_size_without_counter = name.size(); for (int counter = 1;; ++counter) { name.resize(name_size_without_counter); llvm::raw_string_ostream(name) << counter; if (add_name(/*mark_ambiguous=*/false)) { return best; } } } auto InstNamer::AddBlockLabel( ScopeId scope_id, InstBlockId block_id, std::string name, std::variant loc_id_or_fingerprint) -> void { if (!block_id.has_value() || labels_[sem_ir_->inst_blocks().GetRawIndex(block_id)].second) { return; } if (auto* loc_id = std::get_if(&loc_id_or_fingerprint); loc_id && !loc_id->has_value()) { if (const auto& block = sem_ir_->inst_blocks().Get(block_id); !block.empty()) { loc_id_or_fingerprint = LocId(block.front()); } } labels_[sem_ir_->inst_blocks().GetRawIndex(block_id)] = { scope_id, GetScopeInfo(scope_id).labels.AllocateName( *this, loc_id_or_fingerprint, std::move(name))}; } // Provides names for `AddBlockLabel`. struct BranchNames { // Returns names for a branching parse node, or nullopt if not a branch. static auto For(Parse::NodeKind node_kind) -> std::optional { switch (node_kind) { case Parse::NodeKind::ForHeaderStart: return {{.prefix = "for", .branch = "next"}}; case Parse::NodeKind::ForHeader: return {{.prefix = "for", .branch_if = "body", .branch = "done"}}; case Parse::NodeKind::IfExprIf: return {{.prefix = "if.expr", .branch_if = "then", .branch = "else", .branch_with_arg = "result"}}; case Parse::NodeKind::IfCondition: return {{.prefix = "if", .branch_if = "then", .branch = "else"}}; case Parse::NodeKind::IfStatement: return {{.prefix = "if", .branch = "done"}}; case Parse::NodeKind::ShortCircuitOperandAnd: return { {.prefix = "and", .branch_if = "rhs", .branch_with_arg = "result"}}; case Parse::NodeKind::ShortCircuitOperandOr: return { {.prefix = "or", .branch_if = "rhs", .branch_with_arg = "result"}}; case Parse::NodeKind::WhileConditionStart: return {{.prefix = "while", .branch = "cond"}}; case Parse::NodeKind::WhileCondition: return {{.prefix = "while", .branch_if = "body", .branch = "done"}}; default: return std::nullopt; } } // Returns the provided suffix for the instruction kind, or an empty string. auto GetSuffix(InstKind inst_kind) -> llvm::StringLiteral { switch (inst_kind) { case BranchIf::Kind: return branch_if; case Branch::Kind: return branch; case BranchWithArg::Kind: return branch_with_arg; default: return ""; } } // The kind of branch, based on the node kind. llvm::StringLiteral prefix; // For labeling branch instruction kinds. Only expected kinds need a value; // the empty string is for unexpected kinds. llvm::StringLiteral branch_if = ""; llvm::StringLiteral branch = ""; llvm::StringLiteral branch_with_arg = ""; }; // Finds and adds a suitable block label for the given SemIR instruction that // represents some kind of branch. auto InstNamer::AddBlockLabel(ScopeId scope_id, LocId loc_id, AnyBranch branch) -> void { std::string label; loc_id = sem_ir_->insts().GetCanonicalLocId(loc_id); if (loc_id.node_id().has_value()) { if (auto names = BranchNames::For( sem_ir_->parse_tree().node_kind(loc_id.node_id()))) { if (auto suffix = names->GetSuffix(branch.kind); !suffix.empty()) { label = llvm::formatv("{0}.{1}", names->prefix, suffix); } else { label = llvm::formatv("{0}.", names->prefix, branch.kind); } } } AddBlockLabel(scope_id, branch.target_id, label, loc_id); } auto InstNamer::PushBlockId(ScopeId scope_id, InstBlockId block_id) -> void { if (block_id.has_value()) { inst_block_stack_.push_back({scope_id, block_id}); } } auto InstNamer::PushBlockInsts(ScopeId scope_id, llvm::ArrayRef inst_ids) -> void { for (auto inst_id : llvm::reverse(inst_ids)) { if (inst_id.has_value() && !IsSingletonInstId(inst_id)) { inst_stack_.push_back(std::make_pair(scope_id, inst_id)); } } } auto InstNamer::PushGeneric(ScopeId scope_id, GenericId generic_id) -> void { if (!generic_id.has_value()) { return; } generic_scopes_[sem_ir_->generics().GetRawIndex(generic_id)] = scope_id; const auto& generic = sem_ir_->generics().Get(generic_id); // Push blocks in reverse order. PushBlockId(scope_id, generic.definition_block_id); PushBlockId(scope_id, generic.decl_block_id); } auto InstNamer::PushEntity(AssociatedConstantId associated_constant_id, [[maybe_unused]] ScopeId scope_id, Scope& scope) -> void { const auto& assoc_const = sem_ir_->associated_constants().Get(associated_constant_id); scope.name = globals_.AllocateName( *this, LocId(assoc_const.decl_id), sem_ir_->names().GetIRBaseName(assoc_const.name_id).str()); // The `decl_block_id` is associated with this `scope_id` from the // AssociatedConstantDecl instruction handler. } auto InstNamer::PushEntity(ClassId class_id, ScopeId scope_id, Scope& scope) -> void { const auto& class_info = sem_ir_->classes().Get(class_id); LocId class_loc(class_info.latest_decl_id()); scope.name = globals_.AllocateName( *this, class_loc, sem_ir_->names().GetIRBaseName(class_info.name_id).str()); AddBlockLabel(scope_id, class_info.body_block_id, "class", class_loc); PushGeneric(scope_id, class_info.generic_id); // Push blocks in reverse order. PushBlockId(scope_id, class_info.body_block_id); PushBlockId(scope_id, class_info.pattern_block_id); } auto InstNamer::GetNameForParentNameScope(NameScopeId name_scope_id) -> llvm::StringRef { if (!name_scope_id.has_value()) { return ""; } auto scope_inst = sem_ir_->insts().Get(sem_ir_->name_scopes().Get(name_scope_id).inst_id()); CARBON_KIND_SWITCH(scope_inst) { case CARBON_KIND(ClassDecl class_decl): { return MaybePushEntity(class_decl.class_id); } case CARBON_KIND(ImplDecl impl): { return MaybePushEntity(impl.impl_id); } case CARBON_KIND(InterfaceDecl interface): { return MaybePushEntity(interface.interface_id); } case CARBON_KIND(InterfaceWithSelfDecl interface_with_self): { return MaybePushEntity( InterfaceWithSelfId{interface_with_self.interface_id}); } case CARBON_KIND(NamedConstraintDecl named_constraint): { return MaybePushEntity(named_constraint.named_constraint_id); } case CARBON_KIND(NamedConstraintWithSelfDecl named_constraint_with_self): { return MaybePushEntity(NamedConstraintWithSelfId{ named_constraint_with_self.named_constraint_id}); } case SemIR::Namespace::Kind: { // Only prefix type scopes. return ""; } default: { return ""; } } } auto InstNamer::PushEntity(FunctionId function_id, ScopeId scope_id, Scope& scope) -> void { const auto& fn = sem_ir_->functions().Get(function_id); LocId fn_loc(fn.latest_decl_id()); auto scope_prefix = GetNameForParentNameScope(fn.parent_scope_id); scope.name = globals_.AllocateName( *this, fn_loc, llvm::formatv("{0}{1}{2}", scope_prefix, scope_prefix.empty() ? "" : ".", sem_ir_->names().GetIRBaseName(fn.name_id))); if (!fn.body_block_ids.empty()) { AddBlockLabel(scope_id, fn.body_block_ids.front(), "entry", fn_loc); } // Push blocks in reverse order. PushGeneric(scope_id, fn.generic_id); for (auto block_id : llvm::reverse(fn.body_block_ids)) { PushBlockId(scope_id, block_id); } PushBlockId(scope_id, fn.pattern_block_id); PushBlockId(scope_id, fn.call_params_id); } auto InstNamer::PushEntity(RequireImplsId require_impls_id, ScopeId scope_id, Scope& scope) -> void { const auto& require = sem_ir_->require_impls().Get(require_impls_id); LocId require_loc(require.decl_id); auto scope_prefix = GetNameForParentNameScope(require.parent_scope_id); llvm::StringRef self_name; auto self_const_id = sem_ir_->constant_values().GetConstantInstId(require.self_id); auto self_index = sem_ir_->insts().GetRawIndex(self_const_id); if (IsSingletonInstId(self_const_id)) { self_name = sem_ir_->insts().Get(self_const_id).kind().ir_name(); } else if (const auto& inst_name = insts_[self_index].second) { self_name = inst_name.GetBaseName(); } else { self_name = ""; } llvm::StringRef facet_type_name; auto facet_type_const_id = sem_ir_->constant_values().GetConstantInstId(require.facet_type_inst_id); auto facet_type_index = sem_ir_->insts().GetRawIndex(facet_type_const_id); if (IsSingletonInstId(facet_type_const_id)) { facet_type_name = sem_ir_->insts().Get(facet_type_const_id).kind().ir_name(); } else if (const auto& inst_name = insts_[facet_type_index].second) { facet_type_name = inst_name.GetBaseName(); } else { facet_type_name = ""; } scope.name = globals_.AllocateName( *this, require_loc, llvm::formatv("{0}{1}{2}.impls.{3}.require", scope_prefix, scope_prefix.empty() ? "" : ".", self_name, facet_type_name)); auto decl = sem_ir_->insts().GetAs(require.decl_id); AddBlockLabel(scope_id, decl.decl_block_id, "require", require_loc); // Push blocks in reverse order. PushGeneric(scope_id, require.generic_id); } auto InstNamer::PushEntity(CppOverloadSetId cpp_overload_set_id, ScopeId /*scope_id*/, Scope& scope) -> void { const CppOverloadSet& overload_set = sem_ir_->cpp_overload_sets().Get(cpp_overload_set_id); uint64_t fingerprint = fingerprinter_.GetOrCompute(sem_ir_, cpp_overload_set_id); auto scope_prefix = GetNameForParentNameScope(overload_set.parent_scope_id); scope.name = globals_.AllocateName( *this, fingerprint, llvm::formatv("{0}{1}{2}.cpp_overload_set", scope_prefix, scope_prefix.empty() ? "" : ".", sem_ir_->names().GetIRBaseName(overload_set.name_id))); } auto InstNamer::PushEntity(ImplId impl_id, ScopeId scope_id, Scope& scope) -> void { const auto& impl = sem_ir_->impls().Get(impl_id); auto impl_fingerprint = fingerprinter_.GetOrCompute(sem_ir_, impl_id); llvm::StringRef self_name; auto self_const_id = sem_ir_->constant_values().GetConstantInstId(impl.self_id); auto index = sem_ir_->insts().GetRawIndex(self_const_id); if (IsSingletonInstId(self_const_id)) { self_name = sem_ir_->insts().Get(self_const_id).kind().ir_name(); } else if (const auto& inst_name = insts_[index].second) { self_name = inst_name.GetBaseName(); } else { self_name = ""; } llvm::StringRef interface_name; if (impl.interface.interface_id.has_value()) { interface_name = MaybePushEntity(impl.interface.interface_id); } else { interface_name = ""; } scope.name = globals_.AllocateName( *this, impl_fingerprint, llvm::formatv("{0}.as.{1}.impl", self_name, interface_name)); AddBlockLabel(scope_id, impl.body_block_id, "impl", impl_fingerprint); // Push blocks in reverse order. PushGeneric(scope_id, impl.generic_id); PushBlockId(scope_id, impl.witness_block_id); PushBlockId(scope_id, impl.body_block_id); PushBlockId(scope_id, impl.pattern_block_id); } auto InstNamer::PushEntity(InterfaceId interface_id, ScopeId scope_id, Scope& scope) -> void { const auto& interface = sem_ir_->interfaces().Get(interface_id); LocId interface_loc(interface.latest_decl_id()); auto name = sem_ir_->names().GetIRBaseName(interface.name_id).str(); scope.name = globals_.AllocateName(*this, interface_loc, name); AddBlockLabel(scope_id, interface.body_block_without_self_id, "interface", interface_loc); // Push blocks in reverse order. PushGeneric(scope_id, interface.generic_id); PushBlockId(scope_id, interface.body_block_without_self_id); PushBlockId(scope_id, interface.pattern_block_id); } auto InstNamer::PushEntity(InterfaceWithSelfId interface_id, ScopeId scope_id, Scope& scope) -> void { const auto& interface = sem_ir_->interfaces().Get(interface_id.id); LocId interface_loc(interface.latest_decl_id()); auto name = sem_ir_->names().GetIRBaseName(interface.name_id).str(); name += ".WithSelf"; scope.name = globals_.AllocateName(*this, interface_loc, name); AddBlockLabel(scope_id, interface.body_block_with_self_id, "interface_with_self", interface_loc); // Push blocks in reverse order. PushGeneric(scope_id, interface.generic_with_self_id); PushBlockId(scope_id, interface.body_block_with_self_id); } auto InstNamer::PushEntity(NamedConstraintId named_constraint_id, ScopeId scope_id, Scope& scope) -> void { const auto& constraint = sem_ir_->named_constraints().Get(named_constraint_id); LocId constraint_loc(constraint.latest_decl_id()); auto name = sem_ir_->names().GetIRBaseName(constraint.name_id).str(); scope.name = globals_.AllocateName(*this, constraint_loc, name); AddBlockLabel(scope_id, constraint.body_block_without_self_id, "constraint", constraint_loc); // Push blocks in reverse order. PushGeneric(scope_id, constraint.generic_id); PushBlockId(scope_id, constraint.body_block_without_self_id); PushBlockId(scope_id, constraint.pattern_block_id); } auto InstNamer::PushEntity(NamedConstraintWithSelfId named_constraint_id, ScopeId scope_id, Scope& scope) -> void { const auto& constraint = sem_ir_->named_constraints().Get(named_constraint_id.id); LocId constraint_loc(constraint.latest_decl_id()); auto name = sem_ir_->names().GetIRBaseName(constraint.name_id).str(); name += ".WithSelf"; scope.name = globals_.AllocateName(*this, constraint_loc, name); AddBlockLabel(scope_id, constraint.body_block_with_self_id, "constraint_with_self", constraint_loc); // Push blocks in reverse order. PushGeneric(scope_id, constraint.generic_with_self_id); PushBlockId(scope_id, constraint.body_block_with_self_id); } auto InstNamer::PushEntity(VtableId vtable_id, ScopeId /*scope_id*/, Scope& scope) -> void { const auto& vtable = sem_ir_->vtables().Get(vtable_id); const auto& class_info = sem_ir_->classes().Get(vtable.class_id); LocId vtable_loc(class_info.latest_decl_id()); scope.name = globals_.AllocateName( *this, vtable_loc, sem_ir_->names().GetIRBaseName(class_info.name_id).str() + ".vtable"); // TODO: Add support for generic vtables here and elsewhere. // PushGeneric(scope_id, vtable_info.generic_id); } InstNamer::NamingContext::NamingContext(InstNamer* inst_namer, InstNamer::ScopeId scope_id, InstId inst_id) : inst_namer_(inst_namer), scope_id_(scope_id), inst_id_(inst_id), inst_(sem_ir().insts().Get(inst_id)) {} auto InstNamer::NamingContext::AddInstName(std::string name) -> void { auto index = sem_ir().insts().GetRawIndex(inst_id_); ScopeId old_scope_id = inst_namer_->insts_[index].first; if (old_scope_id == ScopeId::None) { std::variant loc_id_or_fingerprint = LocId::None; if (scope_id_ == ScopeId::Constants || scope_id_ == ScopeId::Generated || scope_id_ == ScopeId::Imports) { loc_id_or_fingerprint = inst_namer_->fingerprinter_.GetOrCompute(&sem_ir(), inst_id_); } else { loc_id_or_fingerprint = LocId(inst_id_); } auto scoped_name = inst_namer_->GetScopeInfo(scope_id_).insts.AllocateName( *inst_namer_, loc_id_or_fingerprint, std::move(name)); inst_namer_->insts_[index] = {scope_id_, scoped_name}; } else { CARBON_CHECK(old_scope_id == scope_id_, "Attempting to name inst in multiple scopes"); } } auto InstNamer::NamingContext::AddStructTypeInstName( StructType struct_ty, llvm::StringRef type_suffix, llvm::StringRef name_suffix) -> void { RawStringOstream out; const auto& fields = sem_ir().struct_type_fields().Get(struct_ty.fields_id); if (fields.empty()) { out << "empty_struct" << type_suffix; } else { RawStringOstream name; name << "struct" << type_suffix; for (auto field : fields) { name << "."; name << sem_ir().names().GetIRBaseName(field.name_id); } out << name.TakeStr(); } out << name_suffix; AddInstName(out.TakeStr()); } auto InstNamer::NamingContext::AddIntOrFloatTypeName(char type_literal_prefix, InstId bit_width_id, llvm::StringRef suffix) -> void { RawStringOstream out; out << type_literal_prefix; if (auto bit_width = sem_ir().insts().TryGetAs(bit_width_id)) { out << sem_ir().ints().Get(bit_width->int_id); } else { out << "N"; } out << suffix; AddInstName(out.TakeStr()); } auto InstNamer::NamingContext::AddWitnessTableName(InstId witness_table_inst_id, std::string name) -> void { auto witness_table = sem_ir().insts().TryGetAs(witness_table_inst_id); if (!witness_table || !witness_table->impl_id.has_value()) { // TODO: If `impl_id` is None, the witness comes from a facet value. Can we // get the interface names from it? Store the facet value instruction in the // table? AddInstName(name); return; } const auto& impl = sem_ir().impls().Get(witness_table->impl_id); auto name_id = sem_ir().interfaces().Get(impl.interface.interface_id).name_id; std::string suffix = llvm::formatv(".{}", name); AddInstNameId(name_id, suffix); } auto InstNamer::NamingContext::NameInst() -> void { CARBON_KIND_SWITCH(inst_) { case AddrOf::Kind: { AddInstName("addr"); return; } case ArrayType::Kind: { // TODO: Can we figure out the name of the type this is an array of? AddInstName("array_type"); return; } case CARBON_KIND(AssociatedConstantDecl inst): { AddEntityNameAndMaybePush(inst.assoc_const_id); PushBlockId(inst_namer_->GetScopeFor(inst.assoc_const_id), inst.decl_block_id); return; } case CARBON_KIND(AssociatedEntity inst): { RawStringOstream out; out << "assoc" << inst.index.index; AddInstName(out.TakeStr()); return; } case CARBON_KIND(AssociatedEntityType inst): { AddEntityNameAndMaybePush(inst.interface_id, ".assoc_type"); return; } case CARBON_KIND_ANY(AnyBindingOrExportDecl, inst): { AddInstNameId(sem_ir().entity_names().Get(inst.entity_name_id).name_id); return; } case CARBON_KIND_ANY(AnyBindingPattern, inst): { auto name_id = NameId::Underscore; if (inst.entity_name_id.has_value()) { name_id = sem_ir().entity_names().Get(inst.entity_name_id).name_id; } AddInstNameId(name_id, ".patt"); return; } case CARBON_KIND(BoolLiteral inst): { if (inst.value.ToBool()) { AddInstName("true"); } else { AddInstName("false"); } return; } case CARBON_KIND(BoundMethod inst): { auto type_id = sem_ir().insts().Get(inst.function_decl_id).type_id(); if (auto fn_ty = sem_ir().types().TryGetAs(type_id)) { AddEntityNameAndMaybePush(fn_ty->function_id, ".bound"); } else { AddInstName("bound_method"); } return; } case CARBON_KIND_ANY(AnyBranch, branch): { inst_namer_->AddBlockLabel(scope_id_, LocId(inst_id_), branch); return; } case CARBON_KIND(Call inst): { auto callee = GetCallee(sem_ir(), inst.callee_id); if (auto* fn = std::get_if(&callee)) { AddEntityNameAndMaybePush(fn->function_id, ".call"); return; } AddInstName(""); return; } case CARBON_KIND(ClassDecl inst): { AddEntityNameAndMaybePush(inst.class_id, ".decl"); auto class_scope_id = inst_namer_->GetScopeFor(inst.class_id); PushBlockId(class_scope_id, inst.decl_block_id); return; } case CARBON_KIND(ClassType inst): { if (auto type_info = RecognizedTypeInfo::ForType(sem_ir(), inst); type_info.is_valid()) { RawStringOstream out; if (type_info.PrintLiteral(sem_ir(), out)) { AddInstName(out.TakeStr()); return; } } AddEntityNameAndMaybePush(inst.class_id); return; } case CompleteTypeWitness::Kind: { // TODO: Can we figure out the name of the type this is a witness for? AddInstName("complete_type"); return; } case CARBON_KIND(VtableDecl inst): { const auto& vtable = sem_ir().vtables().Get(inst.vtable_id); inst_namer_->MaybePushEntity(inst.vtable_id); if (inst_namer_->GetScopeFor(vtable.class_id) == scope_id_) { inst_namer_->MaybePushEntity(vtable.class_id); AddInstName("vtable_decl"); } else { AddEntityNameAndMaybePush(vtable.class_id, ".vtable_decl"); } return; } case CARBON_KIND(VtablePtr inst): { const auto& vtable = sem_ir().vtables().Get(inst.vtable_id); inst_namer_->MaybePushEntity(inst.vtable_id); AddEntityNameAndMaybePush(vtable.class_id, ".vtable_ptr"); return; } case ConstType::Kind: { // TODO: Can we figure out the name of the type argument? AddInstName("const"); return; } case CARBON_KIND(CppTemplateNameType inst): { AddInstNameId(sem_ir().entity_names().Get(inst.name_id).name_id, ".type"); return; } case CustomWitness::Kind: { AddInstName("custom_witness"); return; } case CARBON_KIND(ExprPattern inst): { for (auto block_id : sem_ir().expr_regions().Get(inst.expr_region_id).block_ids) { PushBlockId(scope_id_, block_id); } AddInstName("expr_patt"); return; } case CARBON_KIND(FacetAccessType inst): { auto name_id = SemIR::NameId::None; if (auto name = sem_ir().insts().TryGetAs(inst.facet_value_inst_id)) { name_id = name->name_id; } else if (auto bind = sem_ir().insts().TryGetAs( inst.facet_value_inst_id)) { name_id = sem_ir().entity_names().Get(bind->entity_name_id).name_id; } if (name_id.has_value()) { AddInstNameId(name_id, ".as_type"); } else { AddInstName("as_type"); } return; } case CARBON_KIND(SymbolicBindingType inst): { auto bind = sem_ir().insts().GetAs(inst.facet_value_inst_id); auto name_id = sem_ir().entity_names().Get(bind.entity_name_id).name_id; if (name_id.has_value()) { AddInstNameId(name_id, ".binding.as_type"); } else { AddInstName("binding.as_type"); } return; } case CARBON_KIND(FacetType inst): { const auto& facet_type_info = sem_ir().facet_types().Get(inst.facet_type_id); bool has_where = facet_type_info.other_requirements || !facet_type_info.self_impls_constraints.empty() || !facet_type_info.self_impls_named_constraints.empty() || !facet_type_info.type_impls_interfaces.empty() || !facet_type_info.type_impls_named_constraints.empty() || !facet_type_info.rewrite_constraints.empty(); if (facet_type_info.extend_constraints.size() == 1 && facet_type_info.extend_named_constraints.empty()) { AddEntityNameAndMaybePush( facet_type_info.extend_constraints.front().interface_id, has_where ? "_where.type" : ".type"); } else if (facet_type_info.extend_named_constraints.size() == 1 && facet_type_info.extend_constraints.empty()) { AddEntityNameAndMaybePush( facet_type_info.extend_named_constraints.front() .named_constraint_id, has_where ? "_where.type" : ".type"); } else if (facet_type_info.extend_constraints.empty() && facet_type_info.extend_named_constraints.empty()) { AddInstName(has_where ? "type_where" : "type"); } else { AddInstName("facet_type"); } return; } case CARBON_KIND(FacetValue inst): { if (auto facet_type = sem_ir().types().TryGetAs(inst.type_id)) { const auto& facet_type_info = sem_ir().facet_types().Get(facet_type->facet_type_id); if (auto single = facet_type_info.TryAsSingleExtend()) { CARBON_KIND_SWITCH(*single) { case CARBON_KIND(SemIR::SpecificInterface interface): { AddEntityNameAndMaybePush(interface.interface_id, ".facet"); break; } case CARBON_KIND(SemIR::SpecificNamedConstraint constraint): { AddEntityNameAndMaybePush(constraint.named_constraint_id, ".facet"); break; } } return; } if (facet_type_info.HasNoConstraints()) { if (auto class_ty = sem_ir().insts().TryGetAs(inst.type_inst_id)) { AddEntityNameAndMaybePush(class_ty->class_id, ".type.facet"); return; } if (auto tuple_ty = sem_ir().insts().TryGetAs( inst.type_inst_id)) { if (tuple_ty->type_elements_id == InstBlockId::Empty) { AddInstName("empty_tuple.type.facet"); } else { AddInstName("tuple.type.facet"); } return; } if (auto struct_ty = sem_ir().insts().TryGetAs( inst.type_inst_id)) { AddStructTypeInstName(*struct_ty, "", ".type.facet"); return; } } } AddInstName("facet_value"); return; } case CARBON_KIND(FloatType inst): { AddIntOrFloatTypeName('f', inst.bit_width_id); return; } case FloatLiteralValue::Kind: case FloatValue::Kind: { AddInstName("float"); return; } case CARBON_KIND(FunctionDecl inst): { AddEntityNameAndMaybePush(inst.function_id, ".decl"); auto function_scope_id = inst_namer_->GetScopeFor(inst.function_id); PushBlockId(function_scope_id, inst.decl_block_id); return; } case CARBON_KIND(FunctionType inst): { AddEntityNameAndMaybePush(inst.function_id, ".type"); return; } case CARBON_KIND(CppOverloadSetValue inst): { AddEntityNameAndMaybePush(inst.overload_set_id, ".value"); return; } case CARBON_KIND(CppOverloadSetType inst): { AddEntityNameAndMaybePush(inst.overload_set_id, ".type"); return; } case CARBON_KIND(GenericClassType inst): { AddEntityNameAndMaybePush(inst.class_id, ".type"); return; } case CARBON_KIND(GenericInterfaceType inst): { AddEntityNameAndMaybePush(inst.interface_id, ".type"); return; } case CARBON_KIND(GenericNamedConstraintType inst): { AddEntityNameAndMaybePush(inst.named_constraint_id, ".type"); return; } case CARBON_KIND(ImplDecl inst): { // `impl` declarations aren't named because they aren't added to any // namespace, and so aren't referenced directly. inst_namer_->MaybePushEntity(inst.impl_id); PushBlockId(inst_namer_->GetScopeFor(inst.impl_id), inst.decl_block_id); return; } case CARBON_KIND(LookupImplWitness inst): { const auto& interface = sem_ir().specific_interfaces().Get(inst.query_specific_interface_id); AddEntityNameAndMaybePush(interface.interface_id, ".lookup_impl_witness"); return; } case CARBON_KIND(ImplWitness inst): { AddWitnessTableName(inst.witness_table_id, "impl_witness"); return; } case CARBON_KIND(ImplWitnessAccess inst): { // TODO: Include information about the impl? RawStringOstream out; out << "impl.elem" << inst.index.index; AddInstName(out.TakeStr()); return; } case CARBON_KIND(ImplWitnessAccessSubstituted inst): { // TODO: Include information about the impl? RawStringOstream out; auto access = sem_ir().insts().GetAs( inst.impl_witness_access_id); out << "impl.elem" << access.index.index << ".subst"; AddInstName(out.TakeStr()); return; } case ImplWitnessAssociatedConstant::Kind: { AddInstName("impl_witness_assoc_constant"); return; } case ImplWitnessTable::Kind: { AddWitnessTableName(inst_id_, "impl_witness_table"); return; } case ImportCppDecl::Kind: { AddInstName("Cpp.import_cpp"); return; } case CARBON_KIND(ImportDecl inst): { if (inst.package_id.has_value()) { AddInstNameId(inst.package_id, ".import"); } else { AddInstName("default.import"); } return; } case CARBON_KIND_ANY(AnyImportRef, inst): { // Build the base import name: . RawStringOstream out; auto import_ir_inst = sem_ir().import_ir_insts().Get(inst.import_ir_inst_id); const auto& import_ir = *sem_ir().import_irs().Get(import_ir_inst.ir_id()).sem_ir; auto package_id = import_ir.package_id(); if (auto ident_id = package_id.AsIdentifierId(); ident_id.has_value()) { out << import_ir.identifiers().Get(ident_id); } else { out << package_id.AsSpecialName(); } out << "."; // Add entity name if available. if (inst.entity_name_id.has_value()) { auto name_id = sem_ir().entity_names().Get(inst.entity_name_id).name_id; out << sem_ir().names().GetIRBaseName(name_id); } else { out << "import_ref"; } AddInstName(out.TakeStr()); // When building import refs, we frequently add instructions without // a block. Constants that refer to them need to be separately // named. auto const_id = sem_ir().constant_values().Get(inst_id_); if (const_id.has_value() && const_id.is_concrete()) { auto const_inst_id = sem_ir().constant_values().GetInstId(const_id); auto index = sem_ir().insts().GetRawIndex(const_inst_id); if (!inst_namer_->insts_[index].second) { inst_namer_->PushBlockInsts(ScopeId::Imports, const_inst_id); } } return; } case CARBON_KIND(InstValue inst): { inst_namer_->PushBlockInsts(scope_id_, inst.inst_id); AddInstName( ("inst." + sem_ir().insts().Get(inst.inst_id).kind().ir_name()) .str()); return; } case CARBON_KIND(InterfaceDecl inst): { AddEntityNameAndMaybePush(inst.interface_id, ".decl"); auto interface_scope_id = inst_namer_->GetScopeFor(inst.interface_id); PushBlockId(interface_scope_id, inst.decl_block_id); return; } case CARBON_KIND(InterfaceWithSelfDecl inst): { AddEntityNameAndMaybePush(InterfaceWithSelfId{inst.interface_id}, ".decl"); return; } case CARBON_KIND(IntType inst): { AddIntOrFloatTypeName(inst.int_kind == IntKind::Signed ? 'i' : 'u', inst.bit_width_id, ".builtin"); return; } case CARBON_KIND(IntValue inst): { RawStringOstream out; out << "int_" << sem_ir().ints().Get(inst.int_id); AddInstName(out.TakeStr()); return; } case CARBON_KIND(NameBindingDecl inst): { PushBlockId(scope_id_, inst.pattern_block_id); return; } case CARBON_KIND(NamedConstraintDecl inst): { AddEntityNameAndMaybePush(inst.named_constraint_id, ".decl"); auto interface_scope_id = inst_namer_->GetScopeFor(inst.named_constraint_id); PushBlockId(interface_scope_id, inst.decl_block_id); return; } case CARBON_KIND(NamedConstraintWithSelfDecl inst): { AddEntityNameAndMaybePush( NamedConstraintWithSelfId{inst.named_constraint_id}, ".decl"); return; } case CARBON_KIND(NameRef inst): { AddInstNameId(inst.name_id, ".ref"); return; } // The namespace is specified here due to the name conflict. case CARBON_KIND(SemIR::Namespace inst): { AddInstNameId(sem_ir().name_scopes().Get(inst.name_scope_id).name_id()); return; } case CARBON_KIND_ANY(AnyParam, inst): { AddInstNameId(inst.pretty_name_id, ".param"); return; } case OutParamPattern::Kind: case RefParamPattern::Kind: case ValueParamPattern::Kind: case VarParamPattern::Kind: { AddInstNameId(GetPrettyNameFromPatternId(sem_ir(), inst_id_), ".param_patt"); return; } case PatternType::Kind: { AddInstName("pattern_type"); return; } case PointerType::Kind: { AddInstName("ptr"); return; } case RequireCompleteType::Kind: { AddInstName("require_complete"); return; } case CARBON_KIND(RequireImplsDecl inst): { AddEntityNameAndMaybePush(inst.require_impls_id, ".decl"); auto require_scope_id = inst_namer_->GetScopeFor(inst.require_impls_id); PushBlockId(require_scope_id, inst.decl_block_id); return; } case ReturnSlotPattern::Kind: { AddInstNameId(NameId::ReturnSlot, ".patt"); return; } case CARBON_KIND(SpecificFunction inst): { auto type_id = sem_ir().insts().Get(inst.callee_id).type_id(); if (auto fn_ty = sem_ir().types().TryGetAs(type_id)) { AddEntityNameAndMaybePush(fn_ty->function_id, ".specific_fn"); } else { AddInstName("specific_fn"); } return; } case CARBON_KIND(SpecificImplFunction inst): { auto type_id = sem_ir().insts().Get(inst.callee_id).type_id(); if (auto fn_ty = sem_ir().types().TryGetAs(type_id)) { AddEntityNameAndMaybePush(fn_ty->function_id, ".specific_impl_fn"); } else { AddInstName("specific_impl_fn"); } return; } case ReturnSlot::Kind: { AddInstNameId(NameId::ReturnSlot); return; } case CARBON_KIND(SpliceBlock inst): { PushBlockId(scope_id_, inst.block_id); AddInstName(""); return; } case StringLiteral::Kind: { AddInstName("str"); return; } case CARBON_KIND(StructType inst): { AddStructTypeInstName(inst, "_type", ""); return; } case CARBON_KIND(StructValue inst): { if (auto fn_ty = sem_ir().types().TryGetAs(inst.type_id)) { AddEntityNameAndMaybePush(fn_ty->function_id); } else if (auto class_ty = sem_ir().types().TryGetAs(inst.type_id)) { AddEntityNameAndMaybePush(class_ty->class_id, ".val"); } else if (auto generic_class_ty = sem_ir().types().TryGetAs( inst.type_id)) { AddEntityNameAndMaybePush(generic_class_ty->class_id, ".generic"); } else if (auto generic_interface_ty = sem_ir().types().TryGetAs( inst.type_id)) { AddInstNameId(sem_ir() .interfaces() .Get(generic_interface_ty->interface_id) .name_id, ".generic"); } else if (auto template_name_ty = sem_ir().types().TryGetAs( inst.type_id)) { AddInstNameId( sem_ir().entity_names().Get(template_name_ty->name_id).name_id, ".template"); } else { if (sem_ir().inst_blocks().Get(inst.elements_id).empty()) { AddInstName("empty_struct"); } else { AddInstName("struct"); } } return; } case CARBON_KIND(TupleAccess inst): { RawStringOstream out; out << "tuple.elem" << inst.index.index; AddInstName(out.TakeStr()); return; } case CARBON_KIND(TupleType inst): { if (inst.type_elements_id == InstBlockId::Empty) { AddInstName("empty_tuple.type"); } else { AddInstName("tuple.type"); } return; } case CARBON_KIND(TupleValue inst): { if (sem_ir().types().Is(inst.type_id)) { AddInstName("array"); } else if (inst.elements_id == InstBlockId::Empty) { AddInstName("empty_tuple"); } else { AddInstName("tuple"); } return; } case CARBON_KIND(TypeLiteral inst): { if (auto value_id = sem_ir().constant_values().GetConstantInstId(inst.value_id); value_id.has_value()) { if (auto class_type = sem_ir().insts().TryGetAs(value_id)) { if (auto type_info = RecognizedTypeInfo::ForType(sem_ir(), *class_type); type_info.is_valid()) { RawStringOstream out; if (type_info.PrintLiteral(sem_ir(), out)) { AddInstName(out.TakeStr()); return; } } } } AddInstName(""); return; } case CARBON_KIND(UnboundElementType inst): { if (auto class_ty = sem_ir().insts().TryGetAs(inst.class_type_inst_id)) { AddEntityNameAndMaybePush(class_ty->class_id, ".elem"); } else { AddInstName("elem_type"); } return; } case UninitializedValue::Kind: { AddInstName("uninit"); return; } case VarPattern::Kind: { AddInstNameId(GetPrettyNameFromPatternId(sem_ir(), inst_id_), ".var_patt"); return; } case CARBON_KIND(VarStorage inst): { if (inst.pattern_id.has_value()) { AddInstNameId(GetPrettyNameFromPatternId(sem_ir(), inst.pattern_id), ".var"); } else { AddInstName("var"); } return; } default: { // Sequentially number all remaining values. if (inst_.kind().has_type()) { AddInstName(""); } return; } } } auto InstNamer::has_name(InstId inst_id) const -> bool { return static_cast( insts_[sem_ir_->insts().GetRawIndex(inst_id)].second); } } // namespace Carbon::SemIR