mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 20:50:13 +01:00
Add a text mode for fingerprinting. (#7231)
The intent is to add visibility into how the fingerprint is computed, so that fingerprinting issues and mangling collisions can be more readily understood and fixed. Assisted-by: Gemini via Antigravity
This commit is contained in:
@@ -487,7 +487,7 @@ auto CheckParseTrees(
|
||||
auto* unit_info = ready_to_check[check_index];
|
||||
CheckUnit(unit_info, &tree_and_subtrees_getters, fs,
|
||||
unit_info->unit->llvm_context, clang_invocation,
|
||||
options.vlog_stream)
|
||||
options.vlog_stream, options.mangle_string_fingerprint)
|
||||
.Run();
|
||||
for (auto* incoming_import : unit_info->incoming_imports) {
|
||||
--incoming_import->imports_remaining;
|
||||
@@ -537,7 +537,7 @@ auto CheckParseTrees(
|
||||
if (unit_info.imports_remaining > 0) {
|
||||
CheckUnit(&unit_info, &tree_and_subtrees_getters, fs,
|
||||
unit_info.unit->llvm_context, clang_invocation,
|
||||
options.vlog_stream)
|
||||
options.vlog_stream, options.mangle_string_fingerprint)
|
||||
.Run();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,10 @@ struct CheckParseTreesOptions {
|
||||
// If not empty, a raw SemIR dump should be written to this path in the event
|
||||
// of a crash.
|
||||
llvm::StringRef sem_ir_crash_dump;
|
||||
|
||||
// Whether to use the string form of the fingerprint from mangling instead of
|
||||
// the hash form.
|
||||
bool mangle_string_fingerprint = false;
|
||||
};
|
||||
|
||||
// Checks a group of parse trees. This will use imports to decide the order of
|
||||
|
||||
@@ -63,7 +63,7 @@ CheckUnit::CheckUnit(
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
|
||||
llvm::LLVMContext* llvm_context,
|
||||
std::shared_ptr<clang::CompilerInvocation> clang_invocation,
|
||||
llvm::raw_ostream* vlog_stream)
|
||||
llvm::raw_ostream* vlog_stream, bool mangle_string_fingerprint)
|
||||
: unit_and_imports_(unit_and_imports),
|
||||
tree_and_subtrees_getter_(tree_and_subtrees_getters->Get(
|
||||
unit_and_imports->unit->sem_ir->check_ir_id())),
|
||||
@@ -75,7 +75,8 @@ CheckUnit::CheckUnit(
|
||||
context_(&emitter_, tree_and_subtrees_getter_,
|
||||
unit_and_imports_->unit->sem_ir,
|
||||
GetImportedIRCount(unit_and_imports),
|
||||
unit_and_imports_->unit->total_ir_count, vlog_stream) {}
|
||||
unit_and_imports_->unit->total_ir_count, vlog_stream,
|
||||
mangle_string_fingerprint) {}
|
||||
|
||||
auto CheckUnit::Run() -> void {
|
||||
Timings::ScopedTiming timing(unit_and_imports_->unit->timings, "check");
|
||||
|
||||
@@ -129,7 +129,7 @@ class CheckUnit {
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
|
||||
llvm::LLVMContext* llvm_context,
|
||||
std::shared_ptr<clang::CompilerInvocation> clang_invocation,
|
||||
llvm::raw_ostream* vlog_stream);
|
||||
llvm::raw_ostream* vlog_stream, bool mangle_string_fingerprint = false);
|
||||
|
||||
// Produces and checks the IR for the provided unit.
|
||||
auto Run() -> void;
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Carbon::Check {
|
||||
Context::Context(DiagnosticEmitterBase* emitter,
|
||||
Parse::GetTreeAndSubtreesFn tree_and_subtrees_getter,
|
||||
SemIR::File* sem_ir, int imported_ir_count, int total_ir_count,
|
||||
llvm::raw_ostream* vlog_stream)
|
||||
llvm::raw_ostream* vlog_stream, bool mangle_string_fingerprint)
|
||||
: emitter_(emitter),
|
||||
tree_and_subtrees_getter_(tree_and_subtrees_getter),
|
||||
sem_ir_(sem_ir),
|
||||
@@ -39,7 +39,8 @@ Context::Context(DiagnosticEmitterBase* emitter,
|
||||
region_stack_([this](SemIR::LocId loc_id, std::string label) {
|
||||
TODO(loc_id, label);
|
||||
}),
|
||||
core_identifiers_(&identifiers()) {
|
||||
core_identifiers_(&identifiers()),
|
||||
mangle_string_fingerprint_(mangle_string_fingerprint) {
|
||||
// Prepare fields which relate to the number of IRs available for import.
|
||||
import_irs().Reserve(imported_ir_count);
|
||||
import_ir_constant_values_.reserve(imported_ir_count);
|
||||
|
||||
@@ -61,7 +61,8 @@ class Context {
|
||||
explicit Context(DiagnosticEmitterBase* emitter,
|
||||
Parse::GetTreeAndSubtreesFn tree_and_subtrees_getter,
|
||||
SemIR::File* sem_ir, int imported_ir_count,
|
||||
int total_ir_count, llvm::raw_ostream* vlog_stream);
|
||||
int total_ir_count, llvm::raw_ostream* vlog_stream,
|
||||
bool mangle_string_fingerprint = false);
|
||||
|
||||
// Marks an implementation TODO. Always returns false.
|
||||
auto TODO(SemIR::LocId loc_id, std::string label) -> bool;
|
||||
@@ -398,6 +399,9 @@ class Context {
|
||||
auto constants() -> SemIR::ConstantStore& { return sem_ir().constants(); }
|
||||
auto bundles() -> SemIR::BundleStore& { return sem_ir().bundles(); }
|
||||
auto total_ir_count() const -> int { return total_ir_count_; }
|
||||
auto mangle_string_fingerprint() const -> bool {
|
||||
return mangle_string_fingerprint_;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// End of SemIR::File members.
|
||||
@@ -557,6 +561,8 @@ class Context {
|
||||
|
||||
// See `CoreIdentifierCache` for details.
|
||||
CoreIdentifierCache core_identifiers_;
|
||||
|
||||
bool mangle_string_fingerprint_;
|
||||
};
|
||||
|
||||
inline constexpr Context::FormExpr Context::FormExpr::Error = {
|
||||
|
||||
@@ -480,7 +480,8 @@ static auto BuildCppFunctionDeclForCarbonFn(Context& context,
|
||||
function_decl->setParams(param_var_decls);
|
||||
|
||||
// Mangle the function name and attach it to the `FunctionDecl`.
|
||||
SemIR::Mangler m(context.sem_ir(), context.total_ir_count());
|
||||
SemIR::Mangler m(context.sem_ir(), context.total_ir_count(),
|
||||
context.mangle_string_fingerprint());
|
||||
std::string mangled_name = m.Mangle(function_id, SemIR::SpecificId::None);
|
||||
function_decl->addAttr(
|
||||
clang::AsmLabelAttr::Create(context.ast_context(), mangled_name));
|
||||
|
||||
@@ -391,6 +391,14 @@ in the check phase. If empty, the dump is not written.
|
||||
)""",
|
||||
},
|
||||
[&](auto& arg_b) { arg_b.Set(&sem_ir_crash_dump); });
|
||||
b.AddFlag(
|
||||
{
|
||||
.name = "mangle-string-fingerprint",
|
||||
.help = R"""(
|
||||
Use the string form of the fingerprint from mangling instead of the hash form.
|
||||
)""",
|
||||
},
|
||||
[&](auto& arg_b) { arg_b.Set(&mangle_string_fingerprint); });
|
||||
}
|
||||
|
||||
static constexpr CommandLine::CommandInfo SubcommandInfo = {
|
||||
@@ -821,6 +829,7 @@ auto CompilationUnit::RunLower() -> void {
|
||||
options.want_debug_info = options_->include_debug_info;
|
||||
options.vlog_stream = vlog_stream_;
|
||||
options.opt_level = options_->opt_level;
|
||||
options.mangle_string_fingerprint = options_->mangle_string_fingerprint;
|
||||
module_ = Lower::LowerToLLVM(*llvm_context_, driver_env_->fs,
|
||||
cache_->tree_and_subtrees_getters(), *sem_ir_,
|
||||
total_ir_count_, options);
|
||||
@@ -1260,6 +1269,7 @@ auto CompileSubcommand::Run(DriverEnv& driver_env) -> DriverResult {
|
||||
options.prelude_import = options_.prelude_import;
|
||||
options.vlog_stream = driver_env.vlog_stream;
|
||||
options.fuzzing = driver_env.fuzzing;
|
||||
options.mangle_string_fingerprint = options_.mangle_string_fingerprint;
|
||||
if (options.vlog_stream || options_.dump_sem_ir || options_.dump_cpp_ast ||
|
||||
options_.dump_raw_sem_ir) {
|
||||
options.include_in_dumps = &cache.include_in_dumps();
|
||||
|
||||
@@ -71,6 +71,8 @@ struct CompileOptions {
|
||||
llvm::SmallVector<llvm::StringRef> exclude_dump_file_prefixes;
|
||||
|
||||
llvm::StringRef sem_ir_crash_dump;
|
||||
|
||||
bool mangle_string_fingerprint = false;
|
||||
};
|
||||
|
||||
// Implements the compile subcommand of the driver.
|
||||
|
||||
@@ -20,7 +20,7 @@ Context::Context(
|
||||
const Parse::GetTreeAndSubtreesStore* tree_and_subtrees_getters,
|
||||
clang::CodeGenerator* clang_code_generator, llvm::StringRef module_name,
|
||||
int total_ir_count, Lower::OptimizationLevel opt_level,
|
||||
llvm::raw_ostream* vlog_stream)
|
||||
bool mangle_string_fingerprint, llvm::raw_ostream* vlog_stream)
|
||||
: llvm_context_(llvm_context),
|
||||
clang_code_generator_(clang_code_generator),
|
||||
llvm_module_owner_(
|
||||
@@ -39,6 +39,7 @@ Context::Context(
|
||||
tree_and_subtrees_getters_(tree_and_subtrees_getters),
|
||||
vlog_stream_(vlog_stream),
|
||||
total_ir_count_(total_ir_count),
|
||||
mangle_string_fingerprint_(mangle_string_fingerprint),
|
||||
file_contexts_(
|
||||
FileContextStore::MakeForOverwriteWithExplicitSize(total_ir_count_)) {
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ class Context {
|
||||
const Parse::GetTreeAndSubtreesStore* tree_and_subtrees_getters,
|
||||
clang::CodeGenerator* code_generator, llvm::StringRef module_name,
|
||||
int total_ir_count, Lower::OptimizationLevel opt_level,
|
||||
llvm::raw_ostream* vlog_stream);
|
||||
bool mangle_string_fingerprint, 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
|
||||
@@ -124,6 +124,9 @@ class Context {
|
||||
return *tree_and_subtrees_getters_;
|
||||
}
|
||||
auto total_ir_count() -> int { return total_ir_count_; }
|
||||
auto mangle_string_fingerprint() const -> bool {
|
||||
return mangle_string_fingerprint_;
|
||||
}
|
||||
|
||||
auto printf_int_format_string() -> llvm::Value* {
|
||||
return printf_int_format_string_;
|
||||
@@ -171,6 +174,9 @@ class Context {
|
||||
// The total number of files.
|
||||
int total_ir_count_;
|
||||
|
||||
// Whether to use the string form of the fingerprint for mangling.
|
||||
bool mangle_string_fingerprint_;
|
||||
|
||||
// The `FileContext`s for each IR that is involved in this lowering action.
|
||||
using FileContextStore =
|
||||
FixedSizeValueStore<SemIR::CheckIRId, std::unique_ptr<FileContext>>;
|
||||
|
||||
@@ -322,7 +322,8 @@ auto FileContext::GetOrCreateLLVMFunction(
|
||||
sem_ir().clang_decls().Get(clang_decl_id).key.decl->getAsFunction());
|
||||
}
|
||||
|
||||
SemIR::Mangler m(sem_ir(), context().total_ir_count());
|
||||
SemIR::Mangler m(sem_ir(), context().total_ir_count(),
|
||||
context().mangle_string_fingerprint());
|
||||
std::string mangled_name = m.Mangle(function_id, specific_id);
|
||||
if (auto* existing = llvm_module().getFunction(mangled_name)) {
|
||||
// We might have already lowered this function while lowering a different
|
||||
@@ -691,7 +692,8 @@ auto FileContext::BuildGlobalVariableDecl(SemIR::VarStorage var_storage)
|
||||
|
||||
auto FileContext::BuildNonCppGlobalVariableDecl(SemIR::VarStorage var_storage)
|
||||
-> llvm::GlobalVariable* {
|
||||
SemIR::Mangler m(sem_ir(), context().total_ir_count());
|
||||
SemIR::Mangler m(sem_ir(), context().total_ir_count(),
|
||||
context().mangle_string_fingerprint());
|
||||
auto mangled_name = m.MangleGlobalVariable(var_storage.pattern_id);
|
||||
auto linkage = llvm::GlobalVariable::ExternalLinkage;
|
||||
|
||||
@@ -726,7 +728,8 @@ auto FileContext::BuildVtable(const SemIR::Vtable& vtable,
|
||||
}
|
||||
const auto& class_info = sem_ir().classes().Get(vtable.class_id);
|
||||
|
||||
SemIR::Mangler m(sem_ir(), context().total_ir_count());
|
||||
SemIR::Mangler m(sem_ir(), context().total_ir_count(),
|
||||
context().mangle_string_fingerprint());
|
||||
std::string mangled_name = m.MangleVTable(class_info, specific_id);
|
||||
|
||||
if (sem_ir()
|
||||
|
||||
@@ -25,7 +25,7 @@ auto LowerToLLVM(
|
||||
&tree_and_subtrees_getters,
|
||||
sem_ir.cpp_file() ? sem_ir.cpp_file()->GetCodeGenerator() : nullptr,
|
||||
sem_ir.filename(), total_ir_count, options.opt_level,
|
||||
options.vlog_stream);
|
||||
options.mangle_string_fingerprint, options.vlog_stream);
|
||||
|
||||
// TODO: Consider disabling instruction naming by default if we're not
|
||||
// producing textual LLVM IR.
|
||||
|
||||
@@ -37,6 +37,9 @@ struct LowerToLLVMOptions {
|
||||
|
||||
// The optimization level to set on lowered functions by default.
|
||||
OptimizationLevel opt_level = OptimizationLevel::Debug;
|
||||
|
||||
// Whether to use the string form of the fingerprint for mangling.
|
||||
bool mangle_string_fingerprint = false;
|
||||
};
|
||||
|
||||
} // namespace Carbon::Lower
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
// 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-FILE: toolchain/testing/testdata/min_prelude/int.carbon
|
||||
//
|
||||
// EXTRA-ARGS: --mangle-string-fingerprint
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// TIP: To test this file alone, run:
|
||||
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/packages/imported_package_mangle_string.carbon
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/packages/imported_package_mangle_string.carbon
|
||||
|
||||
// --- a.carbon
|
||||
package A;
|
||||
|
||||
class C {}
|
||||
|
||||
interface I {
|
||||
fn F() {}
|
||||
}
|
||||
|
||||
impl array(C, 1) as I {
|
||||
fn F() {}
|
||||
}
|
||||
|
||||
namespace N;
|
||||
class N.D;
|
||||
|
||||
impl array(N.D, 1) as I {
|
||||
fn F() {}
|
||||
}
|
||||
|
||||
// --- b.carbon
|
||||
package B;
|
||||
import A;
|
||||
|
||||
fn G() {
|
||||
(array(A.C, 1) as A.I).F();
|
||||
(array(A.N.D, 1) as A.I).F();
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: ; ModuleID = 'a.carbon'
|
||||
// CHECK:STDOUT: source_filename = "a.carbon"
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: ; Function Attrs: nounwind
|
||||
// CHECK:STDOUT: define void @"_CF.{array_type,{int_value,{Core.IntLiteral},1},{class_type,C,{namespace,{<namespace>},A,!invalid},!invalid}}:I.A"() #0 !dbg !4 {
|
||||
// CHECK:STDOUT: entry:
|
||||
// CHECK:STDOUT: ret void, !dbg !7
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: ; Function Attrs: nounwind
|
||||
// CHECK:STDOUT: define void @"_CF.{array_type,{int_value,{Core.IntLiteral},1},{class_type,D,{namespace,{<namespace>},N,{namespace,{<namespace>},A,!invalid},!invalid},!invalid}}:I.A"() #0 !dbg !8 {
|
||||
// CHECK:STDOUT: entry:
|
||||
// CHECK:STDOUT: ret void, !dbg !9
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: attributes #0 = { nounwind }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
|
||||
// CHECK:STDOUT: !llvm.dbg.cu = !{!2}
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5}
|
||||
// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3}
|
||||
// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
|
||||
// CHECK:STDOUT: !3 = !DIFile(filename: "a.carbon", directory: "")
|
||||
// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "F", linkageName: "_CF.{array_type,{int_value,{Core.IntLiteral},1},{class_type,C,{namespace,{<namespace>},A,!invalid},!invalid}}:I.A", scope: null, file: !3, line: 10, type: !5, spFlags: DISPFlagDefinition, unit: !2)
|
||||
// CHECK:STDOUT: !5 = !DISubroutineType(types: !6)
|
||||
// CHECK:STDOUT: !6 = !{null}
|
||||
// CHECK:STDOUT: !7 = !DILocation(line: 10, column: 3, scope: !4)
|
||||
// CHECK:STDOUT: !8 = distinct !DISubprogram(name: "F", linkageName: "_CF.{array_type,{int_value,{Core.IntLiteral},1},{class_type,D,{namespace,{<namespace>},N,{namespace,{<namespace>},A,!invalid},!invalid},!invalid}}:I.A", scope: null, file: !3, line: 17, type: !5, spFlags: DISPFlagDefinition, unit: !2)
|
||||
// CHECK:STDOUT: !9 = !DILocation(line: 17, column: 3, scope: !8)
|
||||
// CHECK:STDOUT: ; ModuleID = 'b.carbon'
|
||||
// CHECK:STDOUT: source_filename = "b.carbon"
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: ; Function Attrs: nounwind
|
||||
// CHECK:STDOUT: define void @_CG.B() #0 !dbg !4 {
|
||||
// CHECK:STDOUT: entry:
|
||||
// CHECK:STDOUT: call void @"_CF.{array_type,{int_value,{Core.IntLiteral},1},{class_type,C,{namespace,{<namespace>},A,{import,!invalid,A}},!invalid}}:I.A"(), !dbg !7
|
||||
// CHECK:STDOUT: call void @"_CF.{array_type,{int_value,{Core.IntLiteral},1},{class_type,D,{namespace,{<namespace>},N,{namespace,{<namespace>},A,{import,!invalid,A}},!invalid},!invalid}}:I.A"(), !dbg !8
|
||||
// CHECK:STDOUT: ret void, !dbg !9
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: declare void @"_CF.{array_type,{int_value,{Core.IntLiteral},1},{class_type,C,{namespace,{<namespace>},A,{import,!invalid,A}},!invalid}}:I.A"()
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: declare void @"_CF.{array_type,{int_value,{Core.IntLiteral},1},{class_type,D,{namespace,{<namespace>},N,{namespace,{<namespace>},A,{import,!invalid,A}},!invalid},!invalid}}:I.A"()
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: attributes #0 = { nounwind }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
|
||||
// CHECK:STDOUT: !llvm.dbg.cu = !{!2}
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5}
|
||||
// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3}
|
||||
// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
|
||||
// CHECK:STDOUT: !3 = !DIFile(filename: "b.carbon", directory: "")
|
||||
// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "G", linkageName: "_CG.B", scope: null, file: !3, line: 4, type: !5, spFlags: DISPFlagDefinition, unit: !2)
|
||||
// CHECK:STDOUT: !5 = !DISubroutineType(types: !6)
|
||||
// CHECK:STDOUT: !6 = !{null}
|
||||
// CHECK:STDOUT: !7 = !DILocation(line: 5, column: 3, scope: !4)
|
||||
// CHECK:STDOUT: !8 = !DILocation(line: 6, column: 3, scope: !4)
|
||||
// CHECK:STDOUT: !9 = !DILocation(line: 4, column: 1, scope: !4)
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "toolchain/sem_ir/inst_fingerprinter.h"
|
||||
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
@@ -23,49 +24,38 @@
|
||||
|
||||
namespace Carbon::SemIR {
|
||||
|
||||
namespace {
|
||||
struct Worklist {
|
||||
using FingerprintStore =
|
||||
FixedSizeValueStore<InstId, uint64_t, Tag<CheckIRId>>;
|
||||
using FilesFingerprintStores =
|
||||
FixedSizeValueStore<CheckIRId, FingerprintStore>;
|
||||
// A fingerprint store that computes a hash via a Merkle tree.
|
||||
class HashFingerprintStore {
|
||||
public:
|
||||
using ResultType = uint64_t;
|
||||
|
||||
// The file containing the instruction we're currently processing.
|
||||
const File* sem_ir = nullptr;
|
||||
// The instructions we need to compute fingerprints for.
|
||||
llvm::SmallVector<std::pair<
|
||||
const File*, std::variant<InstId, InstBlockId, ImplId, CppOverloadSetId>>>
|
||||
todo;
|
||||
// The contents of the current instruction as accumulated so far. This is used
|
||||
// to build a Merkle tree containing a fingerprint for the current
|
||||
// instruction.
|
||||
llvm::SmallVector<llvm::stable_hash> contents = {};
|
||||
// Known cached instruction fingerprints. Each item in `todo` will be added to
|
||||
// the cache if not already present.
|
||||
FilesFingerprintStores* fingerprints;
|
||||
explicit HashFingerprintStore(int total_ir_count)
|
||||
: fingerprints_(FilesFingerprintStores::MakeWithExplicitSizeFrom(
|
||||
total_ir_count, [] {
|
||||
return FingerprintStore::MakeForOverwriteWithExplicitSize(
|
||||
0, CheckIRId::None);
|
||||
})) {}
|
||||
|
||||
// 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());
|
||||
auto GetFingerprint(const File* file, InstId inst_id)
|
||||
-> std::optional<ResultType> {
|
||||
auto& store = fingerprints_.Get(file->check_ir_id());
|
||||
if (store.size() == 0) {
|
||||
return 0;
|
||||
return std::nullopt;
|
||||
}
|
||||
// These InstIds are constant values, so not in the ValueStore. We use a
|
||||
// constant (negative) fingerprint for them.
|
||||
if (inst_id == InstId::InitTombstone ||
|
||||
inst_id == InstId::ImplWitnessTablePlaceholder) {
|
||||
return inst_id.index;
|
||||
}
|
||||
return store.Get(inst_id);
|
||||
auto fingerprint = store.Get(inst_id);
|
||||
if (fingerprint == 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return fingerprint;
|
||||
}
|
||||
|
||||
// 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());
|
||||
auto SetFingerprint(const File* file, InstId inst_id, ResultType fingerprint)
|
||||
-> void {
|
||||
auto& store = fingerprints_.Get(file->check_ir_id());
|
||||
if (store.size() == 0) {
|
||||
store = FingerprintStore::MakeWithExplicitSize(
|
||||
file->insts().size(), file->insts().GetIdTag(), 0);
|
||||
@@ -73,15 +63,180 @@ struct Worklist {
|
||||
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.
|
||||
auto AddInvalid() -> void { contents.push_back(-1); }
|
||||
auto Prepare() -> void { contents_.clear(); }
|
||||
|
||||
auto Finish() -> ResultType { return llvm::stable_hash_combine(contents_); }
|
||||
|
||||
auto AddFingerprint(ResultType fingerprint) -> void {
|
||||
contents_.push_back(fingerprint);
|
||||
}
|
||||
|
||||
auto AddInvalid() -> void { contents_.push_back(-1); }
|
||||
|
||||
auto AddString(llvm::StringRef string) -> void {
|
||||
contents_.push_back(llvm::stable_hash_name(string));
|
||||
}
|
||||
|
||||
auto AddInteger(uint64_t value) -> void { contents_.push_back(value); }
|
||||
|
||||
auto AddAPInt(const llvm::APInt& value) -> void {
|
||||
contents_.push_back(value.getBitWidth());
|
||||
contents_.append(value.getRawData(),
|
||||
value.getRawData() + value.getNumWords());
|
||||
}
|
||||
|
||||
private:
|
||||
using FingerprintStore =
|
||||
FixedSizeValueStore<InstId, uint64_t, Tag<CheckIRId>>;
|
||||
using FilesFingerprintStores =
|
||||
FixedSizeValueStore<CheckIRId, FingerprintStore>;
|
||||
FilesFingerprintStores fingerprints_;
|
||||
llvm::SmallVector<llvm::stable_hash> contents_;
|
||||
};
|
||||
|
||||
// A fingerprint store that produces a string representation of the entity being
|
||||
// fingerprinted.
|
||||
class StringFingerprintStore {
|
||||
public:
|
||||
using ResultType = llvm::StringRef;
|
||||
|
||||
explicit StringFingerprintStore(int total_ir_count)
|
||||
: fingerprints_(FilesFingerprintStores::MakeWithExplicitSizeFrom(
|
||||
total_ir_count, [] {
|
||||
return FingerprintStore::MakeForOverwriteWithExplicitSize(
|
||||
0, CheckIRId::None);
|
||||
})) {}
|
||||
|
||||
auto GetFingerprint(const File* file, InstId inst_id)
|
||||
-> std::optional<ResultType> {
|
||||
auto& store = fingerprints_.Get(file->check_ir_id());
|
||||
if (store.size() == 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (inst_id == InstId::InitTombstone) {
|
||||
return llvm::StringRef("!tombstone");
|
||||
}
|
||||
if (inst_id == InstId::ImplWitnessTablePlaceholder) {
|
||||
return llvm::StringRef("!placeholder");
|
||||
}
|
||||
auto fingerprint = store.Get(inst_id);
|
||||
if (fingerprint.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return fingerprint;
|
||||
}
|
||||
|
||||
auto SetFingerprint(const File* file, InstId inst_id, ResultType fingerprint)
|
||||
-> void {
|
||||
auto& store = fingerprints_.Get(file->check_ir_id());
|
||||
if (store.size() == 0) {
|
||||
store = FingerprintStore::MakeWithExplicitSize(
|
||||
file->insts().size(), file->insts().GetIdTag(), llvm::StringRef());
|
||||
}
|
||||
store.Set(inst_id, fingerprint);
|
||||
}
|
||||
|
||||
auto Prepare() -> void { contents_.clear(); }
|
||||
|
||||
auto Finish() -> ResultType {
|
||||
std::string result = "{";
|
||||
bool first = true;
|
||||
for (const auto& item : contents_) {
|
||||
if (!first) {
|
||||
result += ",";
|
||||
}
|
||||
first = false;
|
||||
result += item;
|
||||
}
|
||||
result += "}";
|
||||
return SaveString(std::move(result));
|
||||
}
|
||||
|
||||
auto AddFingerprint(ResultType fingerprint) -> void {
|
||||
contents_.push_back(fingerprint);
|
||||
}
|
||||
|
||||
auto AddInvalid() -> void { contents_.push_back("!invalid"); }
|
||||
|
||||
auto AddString(llvm::StringRef string) -> void {
|
||||
constexpr llvm::StringRef SpecialChars = "{},!\\";
|
||||
auto num_special_chars = llvm::count_if(
|
||||
string, [&](char c) { return SpecialChars.contains(c); });
|
||||
if (num_special_chars) {
|
||||
std::string escaped;
|
||||
escaped.reserve(string.size() + num_special_chars);
|
||||
for (char c : string) {
|
||||
if (SpecialChars.contains(c)) {
|
||||
escaped.push_back('\\');
|
||||
}
|
||||
escaped.push_back(c);
|
||||
}
|
||||
contents_.push_back(SaveString(std::move(escaped)));
|
||||
} else {
|
||||
contents_.push_back(string);
|
||||
}
|
||||
}
|
||||
|
||||
auto AddInteger(uint64_t value) -> void {
|
||||
contents_.push_back(SaveString(std::to_string(value)));
|
||||
}
|
||||
|
||||
auto AddAPInt(const llvm::APInt& value) -> void {
|
||||
contents_.push_back(
|
||||
SaveString(llvm::toString(value, 10, /*isSigned=*/true)));
|
||||
}
|
||||
|
||||
private:
|
||||
auto SaveString(std::string str) -> llvm::StringRef {
|
||||
auto& entry = allocated_strings_.emplace_back(
|
||||
std::make_unique<std::string>(std::move(str)));
|
||||
return *entry;
|
||||
}
|
||||
|
||||
using FingerprintStore =
|
||||
FixedSizeValueStore<InstId, llvm::StringRef, Tag<CheckIRId>>;
|
||||
using FilesFingerprintStores =
|
||||
FixedSizeValueStore<CheckIRId, FingerprintStore>;
|
||||
FilesFingerprintStores fingerprints_;
|
||||
llvm::SmallVector<llvm::StringRef> contents_;
|
||||
llvm::SmallVector<std::unique_ptr<std::string>> allocated_strings_;
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename StoreT>
|
||||
struct Worklist {
|
||||
using ResultType = StoreT::ResultType;
|
||||
|
||||
// The file containing the instruction we're currently processing.
|
||||
const File* sem_ir = nullptr;
|
||||
// The instructions we need to compute fingerprints for.
|
||||
llvm::SmallVector<std::pair<
|
||||
const File*, std::variant<InstId, InstBlockId, ImplId, CppOverloadSetId>>>
|
||||
todo;
|
||||
// Known cached instruction fingerprints.
|
||||
StoreT* store;
|
||||
|
||||
// Finish fingerprinting and compute the fingerprint.
|
||||
auto Finish() -> ResultType { return store->Finish(); }
|
||||
|
||||
// Gets the known fingerprint from the cache, or returns std::nullopt.
|
||||
auto GetFingerprint(const File* file, InstId inst_id)
|
||||
-> std::optional<ResultType> {
|
||||
return store->GetFingerprint(file, inst_id);
|
||||
}
|
||||
|
||||
// Sets the fingerprint for an instruction in the cache.
|
||||
auto SetFingerprint(const File* file, InstId inst_id, ResultType fingerprint)
|
||||
-> void {
|
||||
store->SetFingerprint(file, inst_id, fingerprint);
|
||||
}
|
||||
|
||||
// Add an invalid marker to the contents.
|
||||
auto AddInvalid() -> void { store->AddInvalid(); }
|
||||
|
||||
// Add a string to the contents.
|
||||
auto AddString(llvm::StringRef string) -> void {
|
||||
contents.push_back(llvm::stable_hash_name(string));
|
||||
}
|
||||
auto AddString(llvm::StringRef string) -> void { store->AddString(string); }
|
||||
|
||||
// Each of the following `Add` functions adds a typed argument to the contents
|
||||
// of the current instruction. If we don't yet have a fingerprint for the
|
||||
@@ -131,7 +286,7 @@ struct Worklist {
|
||||
return;
|
||||
}
|
||||
if (auto fingerprint = GetFingerprint(file, inner_id)) {
|
||||
contents.push_back(fingerprint);
|
||||
store->AddFingerprint(*fingerprint);
|
||||
return;
|
||||
}
|
||||
todo.push_back({file, inner_id});
|
||||
@@ -157,7 +312,7 @@ struct Worklist {
|
||||
|
||||
template <typename T>
|
||||
auto AddBlock(llvm::ArrayRef<T> block) -> void {
|
||||
contents.push_back(block.size());
|
||||
store->AddInteger(block.size());
|
||||
for (auto inner_id : block) {
|
||||
Add(inner_id);
|
||||
}
|
||||
@@ -190,9 +345,9 @@ struct Worklist {
|
||||
return;
|
||||
}
|
||||
auto block = sem_ir->custom_layouts().Get(custom_layout_id);
|
||||
contents.push_back(block.size());
|
||||
store->AddInteger(block.size());
|
||||
for (auto size : block) {
|
||||
contents.push_back(size.bits());
|
||||
store->AddInteger(size.bits());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,7 +420,7 @@ struct Worklist {
|
||||
const auto& require = sem_ir->require_impls().Get(require_id);
|
||||
Add(sem_ir->constant_values().Get(require.self_id));
|
||||
Add(sem_ir->constant_values().Get(require.facet_type_inst_id));
|
||||
contents.push_back(require.extend_self);
|
||||
store->AddInteger(require.extend_self);
|
||||
Add(require.parent_scope_id);
|
||||
}
|
||||
|
||||
@@ -298,7 +453,7 @@ struct Worklist {
|
||||
auto Add(FacetTypeId facet_type_id) -> void {
|
||||
const auto& facet_type = sem_ir->facet_types().Get(facet_type_id);
|
||||
auto add_constraints = [&](auto constraints) {
|
||||
contents.push_back(constraints.size());
|
||||
store->AddInteger(constraints.size());
|
||||
for (auto [first, second] : constraints) {
|
||||
Add(first);
|
||||
Add(second);
|
||||
@@ -307,7 +462,7 @@ struct Worklist {
|
||||
add_constraints(facet_type.extend_constraints);
|
||||
add_constraints(facet_type.self_impls_constraints);
|
||||
add_constraints(facet_type.rewrite_constraints);
|
||||
contents.push_back(facet_type.other_requirements);
|
||||
store->AddInteger(facet_type.other_requirements);
|
||||
}
|
||||
|
||||
auto Add(GenericId generic_id) -> void {
|
||||
@@ -339,11 +494,7 @@ struct Worklist {
|
||||
Add(interface.specific_id);
|
||||
}
|
||||
|
||||
auto Add(const llvm::APInt& value) -> void {
|
||||
contents.push_back(value.getBitWidth());
|
||||
contents.append(value.getRawData(),
|
||||
value.getRawData() + value.getNumWords());
|
||||
}
|
||||
auto Add(const llvm::APInt& value) -> void { store->AddAPInt(value); }
|
||||
|
||||
auto Add(IntId int_id) -> void { Add(sem_ir->ints().Get(int_id)); }
|
||||
|
||||
@@ -355,7 +506,7 @@ struct Worklist {
|
||||
const auto& real = sem_ir->reals().Get(real_id);
|
||||
Add(real.mantissa);
|
||||
Add(real.exponent);
|
||||
contents.push_back(real.is_decimal);
|
||||
store->AddInteger(real.is_decimal);
|
||||
}
|
||||
|
||||
auto Add(PackageNameId package_id) -> void {
|
||||
@@ -407,7 +558,7 @@ struct Worklist {
|
||||
ElementIndex, FloatKind, IntKind, CallParamIndex>)
|
||||
auto Add(T arg) -> void {
|
||||
// Index-like ID: just include the value directly.
|
||||
contents.push_back(arg.index);
|
||||
store->AddInteger(arg.index);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -424,20 +575,20 @@ struct Worklist {
|
||||
|
||||
// Add an instruction argument to the contents of the current instruction.
|
||||
auto AddWithKind(IdAndKind arg) -> void {
|
||||
arg.Dispatch<void>([this](auto id) { Add(id); });
|
||||
arg.Dispatch<void>([&](auto id) { Add(id); });
|
||||
}
|
||||
|
||||
// Ensure all the instructions on the todo list have fingerprints. To avoid a
|
||||
// re-lookup, returns the fingerprint of the first instruction on the todo
|
||||
// list, and requires the todo list to be non-empty.
|
||||
auto Run() -> uint64_t {
|
||||
auto Run() -> ResultType {
|
||||
CARBON_CHECK(!todo.empty());
|
||||
while (true) {
|
||||
const size_t init_size = todo.size();
|
||||
auto [next_sem_ir, next] = todo.back();
|
||||
|
||||
sem_ir = next_sem_ir;
|
||||
contents.clear();
|
||||
store->Prepare();
|
||||
|
||||
if (!std::holds_alternative<InstId>(next)) {
|
||||
// Add the contents of the `next` instruction so they all contribute to
|
||||
@@ -483,7 +634,7 @@ struct Worklist {
|
||||
if (auto fingerprint = GetFingerprint(next_sem_ir, next_inst_id)) {
|
||||
todo.pop_back();
|
||||
if (todo.empty()) {
|
||||
return fingerprint;
|
||||
return *fingerprint;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -510,7 +661,7 @@ struct Worklist {
|
||||
// pop it from the todo list. Otherwise, we leave it on the todo list so
|
||||
// we can compute its fingerprint once we've finished the work we added.
|
||||
if (todo.size() == init_size) {
|
||||
uint64_t fingerprint = Finish();
|
||||
ResultType fingerprint = Finish();
|
||||
SetFingerprint(next_sem_ir, next_inst_id, fingerprint);
|
||||
todo.pop_back();
|
||||
if (todo.empty()) {
|
||||
@@ -520,35 +671,54 @@ struct Worklist {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
auto InstFingerprinter::GetOrCompute(const File* file, InstId inst_id)
|
||||
-> uint64_t {
|
||||
Worklist worklist = {.todo = {{file, inst_id}},
|
||||
.fingerprints = &fingerprints_};
|
||||
template <typename StoreT, typename ResultT>
|
||||
InstFingerprinterTemplate<StoreT, ResultT>::InstFingerprinterTemplate(
|
||||
int total_ir_count)
|
||||
: store_(std::make_unique<StoreT>(total_ir_count)) {}
|
||||
|
||||
template <typename StoreT, typename ResultT>
|
||||
InstFingerprinterTemplate<StoreT, ResultT>::~InstFingerprinterTemplate() =
|
||||
default;
|
||||
|
||||
template <typename StoreT, typename ResultT>
|
||||
auto InstFingerprinterTemplate<StoreT, ResultT>::GetOrCompute(const File* file,
|
||||
InstId inst_id)
|
||||
-> ResultT {
|
||||
Worklist<StoreT> worklist = {.todo = {{file, inst_id}},
|
||||
.store = store_.get()};
|
||||
return worklist.Run();
|
||||
}
|
||||
|
||||
auto InstFingerprinter::GetOrCompute(const File* file,
|
||||
InstBlockId inst_block_id) -> uint64_t {
|
||||
Worklist worklist = {.todo = {{file, inst_block_id}},
|
||||
.fingerprints = &fingerprints_};
|
||||
template <typename StoreT, typename ResultT>
|
||||
auto InstFingerprinterTemplate<StoreT, ResultT>::GetOrCompute(
|
||||
const File* file, InstBlockId inst_block_id) -> ResultT {
|
||||
Worklist<StoreT> worklist = {.todo = {{file, inst_block_id}},
|
||||
.store = store_.get()};
|
||||
return worklist.Run();
|
||||
}
|
||||
|
||||
auto InstFingerprinter::GetOrCompute(const File* file, ImplId impl_id)
|
||||
-> uint64_t {
|
||||
Worklist worklist = {.todo = {{file, impl_id}},
|
||||
.fingerprints = &fingerprints_};
|
||||
template <typename StoreT, typename ResultT>
|
||||
auto InstFingerprinterTemplate<StoreT, ResultT>::GetOrCompute(const File* file,
|
||||
ImplId impl_id)
|
||||
-> ResultT {
|
||||
Worklist<StoreT> worklist = {.todo = {{file, impl_id}},
|
||||
.store = store_.get()};
|
||||
return worklist.Run();
|
||||
}
|
||||
|
||||
auto InstFingerprinter::GetOrCompute(const File* file,
|
||||
CppOverloadSetId overload_set_id)
|
||||
-> uint64_t {
|
||||
Worklist worklist = {.todo = {{file, overload_set_id}},
|
||||
.fingerprints = &fingerprints_};
|
||||
template <typename StoreT, typename ResultT>
|
||||
auto InstFingerprinterTemplate<StoreT, ResultT>::GetOrCompute(
|
||||
const File* file, CppOverloadSetId overload_set_id) -> ResultT {
|
||||
Worklist<StoreT> worklist = {.todo = {{file, overload_set_id}},
|
||||
.store = store_.get()};
|
||||
return worklist.Run();
|
||||
}
|
||||
|
||||
template class InstFingerprinterTemplate<HashFingerprintStore, uint64_t>;
|
||||
template class InstFingerprinterTemplate<StringFingerprintStore,
|
||||
llvm::StringRef>;
|
||||
|
||||
} // namespace Carbon::SemIR
|
||||
|
||||
@@ -5,52 +5,57 @@
|
||||
#ifndef CARBON_TOOLCHAIN_SEM_IR_INST_FINGERPRINTER_H_
|
||||
#define CARBON_TOOLCHAIN_SEM_IR_INST_FINGERPRINTER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "toolchain/base/fixed_size_value_store.h"
|
||||
#include "toolchain/sem_ir/file.h"
|
||||
#include "toolchain/sem_ir/ids.h"
|
||||
|
||||
namespace Carbon::SemIR {
|
||||
|
||||
class HashFingerprintStore;
|
||||
class StringFingerprintStore;
|
||||
|
||||
// Computes fingerprints for instructions. These fingerprints are intended to be
|
||||
// stable across compilations and across minor changes to the compiler.
|
||||
class InstFingerprinter {
|
||||
template <typename StoreT, typename ResultT>
|
||||
class InstFingerprinterTemplate {
|
||||
public:
|
||||
explicit InstFingerprinter(int total_ir_count)
|
||||
: fingerprints_(FilesFingerprintStores::MakeWithExplicitSizeFrom(
|
||||
total_ir_count, [] {
|
||||
return FingerprintStore::MakeForOverwriteWithExplicitSize(
|
||||
0, CheckIRId::None);
|
||||
})) {}
|
||||
using StoreType = StoreT;
|
||||
using ResultType = ResultT;
|
||||
|
||||
explicit InstFingerprinterTemplate(int total_ir_count);
|
||||
~InstFingerprinterTemplate();
|
||||
|
||||
// Gets or computes a fingerprint for the given instruction.
|
||||
auto GetOrCompute(const File* file, InstId inst_id) -> uint64_t;
|
||||
auto GetOrCompute(const File* file, InstId inst_id) -> ResultType;
|
||||
|
||||
// Gets or computes a fingerprint for the given instruction block.
|
||||
auto GetOrCompute(const File* file, InstBlockId inst_block_id) -> uint64_t;
|
||||
auto GetOrCompute(const File* file, InstBlockId inst_block_id) -> ResultType;
|
||||
|
||||
// Gets or computes a fingerprint for the given impl.
|
||||
auto GetOrCompute(const File* file, ImplId impl_id) -> uint64_t;
|
||||
auto GetOrCompute(const File* file, ImplId impl_id) -> ResultType;
|
||||
|
||||
// Gets or computes a fingerprint for the given C++ overload set.
|
||||
auto GetOrCompute(const File* file, CppOverloadSetId overload_set_id)
|
||||
-> uint64_t;
|
||||
-> ResultType;
|
||||
|
||||
private:
|
||||
// The fingerprint for each instruction that has had its fingerprint computed,
|
||||
// indexed by the InstId's index.
|
||||
//
|
||||
// TODO: Experiment with also caching fingerprints for instruction blocks once
|
||||
// we can get realistic performance measurements for this. This would simplify
|
||||
// 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.
|
||||
using FingerprintStore =
|
||||
FixedSizeValueStore<InstId, uint64_t, Tag<CheckIRId>>;
|
||||
using FilesFingerprintStores =
|
||||
FixedSizeValueStore<CheckIRId, FingerprintStore>;
|
||||
FilesFingerprintStores fingerprints_;
|
||||
std::unique_ptr<StoreT> store_;
|
||||
};
|
||||
|
||||
using HashInstFingerprinter =
|
||||
InstFingerprinterTemplate<HashFingerprintStore, uint64_t>;
|
||||
using StringInstFingerprinter =
|
||||
InstFingerprinterTemplate<StringFingerprintStore, llvm::StringRef>;
|
||||
using InstFingerprinter = HashInstFingerprinter;
|
||||
|
||||
extern template class InstFingerprinterTemplate<HashFingerprintStore, uint64_t>;
|
||||
extern template class InstFingerprinterTemplate<StringFingerprintStore,
|
||||
llvm::StringRef>;
|
||||
|
||||
} // namespace Carbon::SemIR
|
||||
|
||||
#endif // CARBON_TOOLCHAIN_SEM_IR_INST_FINGERPRINTER_H_
|
||||
|
||||
@@ -18,6 +18,17 @@
|
||||
|
||||
namespace Carbon::SemIR {
|
||||
|
||||
Mangler::Mangler(const SemIR::File& sem_ir, int total_ir_count,
|
||||
bool use_string_fingerprint)
|
||||
: sem_ir_(sem_ir),
|
||||
fingerprinter_(
|
||||
use_string_fingerprint
|
||||
? std::variant<HashInstFingerprinter, StringInstFingerprinter>(
|
||||
std::in_place_type<StringInstFingerprinter>, total_ir_count)
|
||||
: std::variant<HashInstFingerprinter, StringInstFingerprinter>(
|
||||
std::in_place_type<HashInstFingerprinter>,
|
||||
total_ir_count)) {}
|
||||
|
||||
auto Mangler::MangleNameId(llvm::raw_ostream& os, SemIR::NameId name_id)
|
||||
-> void {
|
||||
CARBON_CHECK(name_id.AsIdentifierId().has_value(),
|
||||
@@ -124,9 +135,7 @@ auto Mangler::MangleInverseQualifiedNameScope(llvm::raw_ostream& os,
|
||||
}
|
||||
default: {
|
||||
// Fall back to including a fingerprint.
|
||||
llvm::write_hex(
|
||||
os, fingerprinter_.GetOrCompute(&sem_ir(), self_const_inst_id),
|
||||
llvm::HexPrintStyle::Lower, 16);
|
||||
MangleFingerprint(os, &sem_ir(), self_const_inst_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -199,9 +208,7 @@ auto Mangler::Mangle(SemIR::FunctionId function_id,
|
||||
|
||||
case SemIR::Function::SpecialFunctionKind::CoreWitness:
|
||||
os << ".";
|
||||
llvm::write_hex(
|
||||
os, fingerprinter_.GetOrCompute(&sem_ir(), function.self_param_id),
|
||||
llvm::HexPrintStyle::Lower, 16);
|
||||
MangleFingerprint(os, &sem_ir(), function.self_param_id);
|
||||
os << ":core";
|
||||
break;
|
||||
case SemIR::Function::SpecialFunctionKind::Thunk:
|
||||
@@ -246,11 +253,8 @@ auto Mangler::MangleSpecificId(llvm::raw_ostream& os,
|
||||
// but isn't necessarily stable across toolchain changes.
|
||||
if (specific_id.has_value()) {
|
||||
os << ".";
|
||||
llvm::write_hex(
|
||||
os,
|
||||
fingerprinter_.GetOrCompute(
|
||||
&sem_ir(), sem_ir().specifics().Get(specific_id).args_id),
|
||||
llvm::HexPrintStyle::Lower, 16);
|
||||
MangleFingerprint(os, &sem_ir(),
|
||||
sem_ir().specifics().Get(specific_id).args_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#define CARBON_TOOLCHAIN_SEM_IR_MANGLER_H_
|
||||
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
#include "clang/AST/Mangle.h"
|
||||
#include "toolchain/sem_ir/constant.h"
|
||||
@@ -21,8 +22,8 @@ class Mangler {
|
||||
public:
|
||||
// Initialize a new Mangler instance for mangling entities within the
|
||||
// specified `File`.
|
||||
Mangler(const SemIR::File& sem_ir, int total_ir_count)
|
||||
: sem_ir_(sem_ir), fingerprinter_(total_ir_count) {}
|
||||
Mangler(const SemIR::File& sem_ir, int total_ir_count,
|
||||
bool use_string_fingerprint = false);
|
||||
|
||||
// Produce a deterministically unique mangled name for the function specified
|
||||
// by `function_id` and `specific_id`.
|
||||
@@ -77,8 +78,24 @@ class Mangler {
|
||||
return sem_ir().constant_values();
|
||||
}
|
||||
|
||||
template <typename IdT>
|
||||
auto MangleFingerprint(llvm::raw_ostream& os, const File* file, IdT id)
|
||||
-> void {
|
||||
std::visit(
|
||||
[&](auto& f) -> void {
|
||||
using ResultT = typename std::decay_t<decltype(f)>::ResultType;
|
||||
if constexpr (std::is_same_v<ResultT, llvm::StringRef>) {
|
||||
os << f.GetOrCompute(file, id);
|
||||
} else {
|
||||
llvm::write_hex(os, f.GetOrCompute(file, id),
|
||||
llvm::HexPrintStyle::Lower, 16);
|
||||
}
|
||||
},
|
||||
fingerprinter_);
|
||||
}
|
||||
|
||||
const SemIR::File& sem_ir_;
|
||||
SemIR::InstFingerprinter fingerprinter_;
|
||||
std::variant<HashInstFingerprinter, StringInstFingerprinter> fingerprinter_;
|
||||
};
|
||||
|
||||
} // namespace Carbon::SemIR
|
||||
|
||||
Reference in New Issue
Block a user