diff --git a/toolchain/check/BUILD b/toolchain/check/BUILD index 884183ac7f0d..8c55c261933a 100644 --- a/toolchain/check/BUILD +++ b/toolchain/check/BUILD @@ -103,6 +103,7 @@ cc_library( "//toolchain/lex:tokenized_buffer", "//toolchain/parse:dump", "//toolchain/parse:tree", + "//toolchain/sem_ir:dump", "//toolchain/sem_ir:file", ], # Always link dump methods. diff --git a/toolchain/check/dump.cpp b/toolchain/check/dump.cpp index 4823890d7770..719ee314e2ad 100644 --- a/toolchain/check/dump.cpp +++ b/toolchain/check/dump.cpp @@ -24,6 +24,7 @@ #include "toolchain/lex/tokenized_buffer.h" #include "toolchain/parse/dump.h" #include "toolchain/parse/tree.h" +#include "toolchain/sem_ir/dump.h" #include "toolchain/sem_ir/file.h" namespace Carbon::Check { @@ -67,12 +68,93 @@ LLVM_DUMP_METHOD static auto Dump(const Context& context, Parse::NodeId node_id) Parse::Dump(context.parse_tree(), node_id); } +LLVM_DUMP_METHOD static auto Dump(const Context& context, + SemIR::ClassId class_id) -> void { + SemIR::Dump(context.sem_ir(), class_id); +} + +LLVM_DUMP_METHOD static auto Dump(const Context& context, + SemIR::ConstantId const_id) -> void { + SemIR::Dump(context.sem_ir(), const_id); +} + +LLVM_DUMP_METHOD static auto Dump(const Context& context, + SemIR::EntityNameId entity_name_id) -> void { + SemIR::Dump(context.sem_ir(), entity_name_id); +} + +LLVM_DUMP_METHOD static auto Dump(const Context& context, + SemIR::FacetTypeId facet_type_id) -> void { + SemIR::Dump(context.sem_ir(), facet_type_id); +} + +LLVM_DUMP_METHOD static auto Dump(const Context& context, + SemIR::FunctionId function_id) -> void { + SemIR::Dump(context.sem_ir(), function_id); +} + +LLVM_DUMP_METHOD static auto Dump(const Context& context, + SemIR::GenericId generic_id) -> void { + SemIR::Dump(context.sem_ir(), generic_id); +} + +LLVM_DUMP_METHOD static auto Dump(const Context& context, SemIR::ImplId impl_id) + -> void { + SemIR::Dump(context.sem_ir(), impl_id); +} + +LLVM_DUMP_METHOD static auto Dump(const Context& context, + SemIR::InstBlockId inst_block_id) -> void { + SemIR::Dump(context.sem_ir(), inst_block_id); +} + +LLVM_DUMP_METHOD static auto Dump(const Context& context, SemIR::InstId inst_id) + -> void { + SemIR::Dump(context.sem_ir(), inst_id); +} + +LLVM_DUMP_METHOD static auto Dump(const Context& context, + SemIR::InterfaceId interface_id) -> void { + SemIR::Dump(context.sem_ir(), interface_id); +} + LLVM_DUMP_METHOD static auto Dump(const Context& context, SemIR::LocId loc_id) -> void { DumpNoNewline(context, loc_id); llvm::errs() << '\n'; } +LLVM_DUMP_METHOD static auto Dump(const Context& context, SemIR::NameId name_id) + -> void { + SemIR::Dump(context.sem_ir(), name_id); +} + +LLVM_DUMP_METHOD static auto Dump(const Context& context, + SemIR::NameScopeId name_scope_id) -> void { + SemIR::Dump(context.sem_ir(), name_scope_id); +} + +LLVM_DUMP_METHOD static auto Dump(const Context& context, + SemIR::SpecificId specific_id) -> void { + SemIR::Dump(context.sem_ir(), specific_id); +} + +LLVM_DUMP_METHOD static auto Dump( + const Context& context, SemIR::StructTypeFieldsId struct_type_fields_id) + -> void { + SemIR::Dump(context.sem_ir(), struct_type_fields_id); +} + +LLVM_DUMP_METHOD static auto Dump(const Context& context, + SemIR::TypeBlockId type_block_id) -> void { + SemIR::Dump(context.sem_ir(), type_block_id); +} + +LLVM_DUMP_METHOD static auto Dump(const Context& context, SemIR::TypeId type_id) + -> void { + SemIR::Dump(context.sem_ir(), type_id); +} + } // namespace Carbon::Check #endif // NDEBUG diff --git a/toolchain/driver/BUILD b/toolchain/driver/BUILD index 4842aaaf047a..d8ac6767b014 100644 --- a/toolchain/driver/BUILD +++ b/toolchain/driver/BUILD @@ -129,6 +129,7 @@ cc_library( "//toolchain/lower", "//toolchain/parse", "//toolchain/parse:tree", + "//toolchain/sem_ir:dump", "//toolchain/sem_ir:file", "//toolchain/sem_ir:formatter", "//toolchain/sem_ir:inst_namer", diff --git a/toolchain/sem_ir/BUILD b/toolchain/sem_ir/BUILD index 0d102074dcd8..f2b7af3fab30 100644 --- a/toolchain/sem_ir/BUILD +++ b/toolchain/sem_ir/BUILD @@ -182,6 +182,20 @@ cc_library( ], ) +cc_library( + name = "dump", + srcs = ["dump.cpp"], + hdrs = ["dump.h"], + deps = [ + ":file", + ":stringify_type", + "//common:ostream", + ], + # Always link dump methods so they are callable from a debugger + # even though they are never called. + alwayslink = 1, +) + cc_test( name = "name_scope_test", size = "small", diff --git a/toolchain/sem_ir/dump.cpp b/toolchain/sem_ir/dump.cpp new file mode 100644 index 000000000000..c8434ab2b16f --- /dev/null +++ b/toolchain/sem_ir/dump.cpp @@ -0,0 +1,243 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef NDEBUG + +#include "toolchain/sem_ir/dump.h" + +#include "common/ostream.h" +#include "toolchain/sem_ir/stringify_type.h" + +namespace Carbon::SemIR { + +static auto DumpNameIfValid(const File& file, NameId name_id) -> void { + if (name_id.is_valid()) { + llvm::errs() << " `" << file.names().GetFormatted(name_id) << "`"; + } +} + +static auto DumpNoNewline(const File& file, ConstantId const_id) -> void { + llvm::errs() << const_id; + if (const_id.is_symbolic()) { + llvm::errs() << ": " + << file.constant_values().GetSymbolicConstant(const_id); + } else if (const_id.is_template()) { + llvm::errs() << ": " + << file.insts().Get( + file.constant_values().GetInstId(const_id)); + } +} + +static auto DumpNoNewline(const File& file, InstId inst_id) -> void { + llvm::errs() << inst_id; + if (inst_id.is_valid()) { + llvm::errs() << ": " << file.insts().Get(inst_id); + } +} + +static auto DumpNoNewline(const File& file, InterfaceId interface_id) -> void { + llvm::errs() << interface_id; + if (interface_id.is_valid()) { + auto interface = file.interfaces().Get(interface_id); + llvm::errs() << ": " << interface; + DumpNameIfValid(file, interface.name_id); + } +} + +static auto DumpNoNewline(const File& file, SpecificId specific_id) -> void { + llvm::errs() << specific_id; + if (specific_id.is_valid()) { + llvm::errs() << ": " << file.specifics().Get(specific_id); + } +} + +LLVM_DUMP_METHOD auto Dump(const File& file, ClassId class_id) -> void { + llvm::errs() << class_id; + if (class_id.is_valid()) { + auto class_obj = file.classes().Get(class_id); + llvm::errs() << ": " << class_obj; + DumpNameIfValid(file, class_obj.name_id); + } + llvm::errs() << '\n'; +} + +LLVM_DUMP_METHOD auto Dump(const File& file, ConstantId const_id) -> void { + DumpNoNewline(file, const_id); + llvm::errs() << '\n'; +} + +LLVM_DUMP_METHOD auto Dump(const File& file, EntityNameId entity_name_id) + -> void { + llvm::errs() << entity_name_id; + if (entity_name_id.is_valid()) { + auto entity_name = file.entity_names().Get(entity_name_id); + llvm::errs() << ": " << entity_name; + DumpNameIfValid(file, entity_name.name_id); + } + llvm::errs() << '\n'; +} + +LLVM_DUMP_METHOD auto Dump(const File& file, FacetTypeId facet_type_id) + -> void { + llvm::errs() << facet_type_id; + if (facet_type_id.is_valid()) { + auto facet_type = file.facet_types().Get(facet_type_id); + llvm::errs() << ": " << facet_type; + for (auto impls : facet_type.impls_constraints) { + llvm::errs() << "\n - "; + DumpNoNewline(file, impls.interface_id); + if (impls.specific_id.is_valid()) { + llvm::errs() << "; "; + DumpNoNewline(file, impls.specific_id); + } + } + for (auto rewrite : facet_type.rewrite_constraints) { + llvm::errs() << "\n - "; + DumpNoNewline(file, rewrite.lhs_const_id); + llvm::errs() << "\n - "; + DumpNoNewline(file, rewrite.rhs_const_id); + } + } + llvm::errs() << '\n'; +} + +LLVM_DUMP_METHOD auto Dump(const File& file, FunctionId function_id) -> void { + llvm::errs() << function_id; + if (function_id.is_valid()) { + auto function = file.functions().Get(function_id); + llvm::errs() << ": " << function; + DumpNameIfValid(file, function.name_id); + } + llvm::errs() << '\n'; +} + +LLVM_DUMP_METHOD auto Dump(const File& file, GenericId generic_id) -> void { + llvm::errs() << generic_id; + if (generic_id.is_valid()) { + llvm::errs() << ": " << file.generics().Get(generic_id); + } + llvm::errs() << '\n'; +} + +LLVM_DUMP_METHOD auto Dump(const File& file, ImplId impl_id) -> void { + llvm::errs() << impl_id; + if (impl_id.is_valid()) { + llvm::errs() << ": " << file.impls().Get(impl_id); + } + llvm::errs() << '\n'; +} + +LLVM_DUMP_METHOD auto Dump(const File& file, InstBlockId inst_block_id) + -> void { + llvm::errs() << inst_block_id; + if (inst_block_id.is_valid()) { + llvm::errs() << ":"; + auto inst_block = file.inst_blocks().Get(inst_block_id); + for (auto inst_id : inst_block) { + llvm::errs() << "\n - "; + DumpNoNewline(file, inst_id); + } + } + llvm::errs() << '\n'; +} + +LLVM_DUMP_METHOD auto Dump(const File& file, InstId inst_id) -> void { + DumpNoNewline(file, inst_id); + llvm::errs() << '\n'; + if (inst_id.is_valid()) { + Inst inst = file.insts().Get(inst_id); + if (inst.type_id().is_valid()) { + llvm::errs() << " - type "; + Dump(file, inst.type_id()); + } + ConstantId const_id = file.constant_values().Get(inst_id); + if (const_id.is_valid()) { + InstId const_inst_id = file.constant_values().GetInstId(const_id); + llvm::errs() << " - value "; + if (const_inst_id == inst_id) { + llvm::errs() << const_id << '\n'; + } else { + Dump(file, const_id); + } + } + } +} + +LLVM_DUMP_METHOD auto Dump(const File& file, InterfaceId interface_id) -> void { + DumpNoNewline(file, interface_id); + llvm::errs() << '\n'; +} + +LLVM_DUMP_METHOD auto Dump(const File& file, NameId name_id) -> void { + llvm::errs() << name_id; + DumpNameIfValid(file, name_id); + llvm::errs() << '\n'; +} + +LLVM_DUMP_METHOD auto Dump(const File& file, NameScopeId name_scope_id) + -> void { + llvm::errs() << name_scope_id; + if (name_scope_id.is_valid()) { + auto name_scope = file.name_scopes().Get(name_scope_id); + llvm::errs() << ": " << name_scope; + if (name_scope.inst_id().is_valid()) { + llvm::errs() << " " << file.insts().Get(name_scope.inst_id()); + } + DumpNameIfValid(file, name_scope.name_id()); + } + llvm::errs() << '\n'; +} + +LLVM_DUMP_METHOD auto Dump(const File& file, SpecificId specific_id) -> void { + DumpNoNewline(file, specific_id); + llvm::errs() << '\n'; +} + +LLVM_DUMP_METHOD auto Dump(const File& file, + StructTypeFieldsId struct_type_fields_id) -> void { + llvm::errs() << struct_type_fields_id; + if (struct_type_fields_id.is_valid()) { + llvm::errs() << ":"; + auto block = file.struct_type_fields().Get(struct_type_fields_id); + for (auto field : block) { + llvm::errs() << "\n - " << field; + DumpNameIfValid(file, field.name_id); + if (field.type_id.is_valid()) { + InstId inst_id = + file.constant_values().GetInstId(field.type_id.AsConstantId()); + llvm::errs() << ": " << StringifyTypeExpr(file, inst_id); + } + } + } + llvm::errs() << '\n'; +} + +LLVM_DUMP_METHOD auto Dump(const File& file, TypeBlockId type_block_id) + -> void { + llvm::errs() << type_block_id; + if (type_block_id.is_valid()) { + llvm::errs() << ":\n"; + auto type_block = file.type_blocks().Get(type_block_id); + for (auto type_id : type_block) { + llvm::errs() << " - "; + Dump(file, type_id); + } + } else { + llvm::errs() << '\n'; + } +} + +LLVM_DUMP_METHOD auto Dump(const File& file, TypeId type_id) -> void { + llvm::errs() << type_id; + if (type_id.is_valid()) { + InstId inst_id = file.constant_values().GetInstId(type_id.AsConstantId()); + llvm::errs() << ": " << StringifyTypeExpr(file, inst_id) << "; " + << file.insts().Get(inst_id); + } + llvm::errs() << '\n'; +} + +} // namespace Carbon::SemIR + +#endif // NDEBUG diff --git a/toolchain/sem_ir/dump.h b/toolchain/sem_ir/dump.h new file mode 100644 index 000000000000..2606bce42471 --- /dev/null +++ b/toolchain/sem_ir/dump.h @@ -0,0 +1,44 @@ +// 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 + +// This library contains functions to assist dumping objects to stderr during +// interactive debugging. Functions named `Dump` are intended for direct use by +// developers, and should use overload resolution to determine which will be +// invoked. The debugger should do namespace resolution automatically. For +// example: +// +// - lldb: `expr Dump(tokens, id)` +// - gdb: `call Dump(tokens, id)` + +#ifndef CARBON_TOOLCHAIN_SEM_IR_DUMP_H_ +#define CARBON_TOOLCHAIN_SEM_IR_DUMP_H_ + +#ifndef NDEBUG + +#include "toolchain/sem_ir/file.h" + +namespace Carbon::SemIR { + +auto Dump(const File& file, ClassId class_id) -> void; +auto Dump(const File& file, ConstantId const_id) -> void; +auto Dump(const File& file, EntityNameId entity_name_id) -> void; +auto Dump(const File& file, FacetTypeId facet_type_id) -> void; +auto Dump(const File& file, FunctionId function_id) -> void; +auto Dump(const File& file, GenericId generic_id) -> void; +auto Dump(const File& file, ImplId impl_id) -> void; +auto Dump(const File& file, InstBlockId inst_block_id) -> void; +auto Dump(const File& file, InstId inst_id) -> void; +auto Dump(const File& file, InterfaceId interface_id) -> void; +auto Dump(const File& file, NameId name_id) -> void; +auto Dump(const File& file, NameScopeId name_scope_id) -> void; +auto Dump(const File& file, SpecificId specific_id) -> void; +auto Dump(const File& file, StructTypeFieldsId struct_type_fields_id) -> void; +auto Dump(const File& file, TypeBlockId type_block_id) -> void; +auto Dump(const File& file, TypeId type_id) -> void; + +} // namespace Carbon::SemIR + +#endif // NDEBUG + +#endif // CARBON_TOOLCHAIN_SEM_IR_DUMP_H_