From 6438d977fa7b83583d75bf48ab16fb2095d34c9a Mon Sep 17 00:00:00 2001 From: Ethan Sifferman Date: Wed, 30 Sep 2026 16:51:19 -0700 Subject: [PATCH] Fix dehash of a suffix after a hashed name (#8556) --- src/V3String.cpp | 11 +++++++++-- src/V3String.h | 4 ++++ test_regress/t/t_trace_hashed_name.py | 18 +++++++++++++++++ test_regress/t/t_trace_hashed_name.v | 28 +++++++++++++++++++++++++++ 4 files changed, 59 insertions(+), 2 deletions(-) create mode 100755 test_regress/t/t_trace_hashed_name.py create mode 100644 test_regress/t/t_trace_hashed_name.v diff --git a/src/V3String.cpp b/src/V3String.cpp index 992d79dee..2f811e933 100644 --- a/src/V3String.cpp +++ b/src/V3String.cpp @@ -656,7 +656,7 @@ string VHashSha512::digestSymbol24() { | (static_cast(binhash[pos + 2] & 0xc0) >> 6)]; result += digits[((binhash[pos + 2] & 0x3f))]; // Keep symbols short-ish, with 24 chars/144 bits we won't have hash collisions - if (result.size() >= 24) break; + if (result.size() >= DIGEST_SYMBOL24_LENGTH) break; } // Any leftover bits don't matter for our purpose return result; @@ -732,6 +732,7 @@ void VHashSha512::selfTest() { string VName::dehash(const string& in) { static constexpr const char VHSH[] = "__Vhsh"; + static constexpr size_t VHSH_LEN = sizeof(VHSH) - 1 + VHashSha512::DIGEST_SYMBOL24_LENGTH; static const size_t DOT_LEN = std::strlen("__DOT__"); std::string dehashed; @@ -748,7 +749,11 @@ string VName::dehash(const string& in) { const auto begin_vhsh = std::search(search_begin, search_end, std::begin(VHSH), std::end(VHSH) - 1); if (begin_vhsh != search_end) { - const std::string vhsh{begin_vhsh, search_end}; + // V3SplitVar appends a bit range to a name hashedName already hashed, so the + // hash does not always reach the end of the component. + const auto end_vhsh + = begin_vhsh + std::min(std::distance(begin_vhsh, search_end), VHSH_LEN); + const std::string vhsh{begin_vhsh, end_vhsh}; const auto& it = s_dehashMap.find(vhsh); UASSERT(it != s_dehashMap.end(), "String not in reverse hash map '" << vhsh << "'"); // Is this not the first component, but the first to require dehashing? @@ -760,6 +765,8 @@ string VName::dehash(const string& in) { dehashed += std::string{search_begin, begin_vhsh}; // Append the bit that was lost to truncation but retrieved from the dehash map. dehashed += it->second; + // Append what follows the hash, such as a split variable bit range. + dehashed += std::string{end_vhsh, search_end}; } // This component doesn't need dehashing but a previous one might have. else if (!dehashed.empty()) { diff --git a/src/V3String.h b/src/V3String.h index 23063fc60..5371acad7 100644 --- a/src/V3String.h +++ b/src/V3String.h @@ -178,6 +178,10 @@ public: } ~VHashSha512() = default; + // CONSTANTS + // Characters digestSymbol24 keeps, 6 bits each, so 144 bits and no collisions in practice + static constexpr size_t DIGEST_SYMBOL24_LENGTH = 24; + // METHODS string digestBase64(); // Return digest formatted in Base64 (non-URL flavor) string digestBinary(); // Return digest as 64 character binary diff --git a/test_regress/t/t_trace_hashed_name.py b/test_regress/t/t_trace_hashed_name.py new file mode 100755 index 000000000..a17cc7c56 --- /dev/null +++ b/test_regress/t/t_trace_hashed_name.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of either the GNU Lesser General Public License Version 3 +# or the Perl Artistic License Version 2.0. +# SPDX-FileCopyrightText: 2026 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile(verilator_flags2=['--trace-vcd']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_trace_hashed_name.v b/test_regress/t/t_trace_hashed_name.v new file mode 100644 index 000000000..31900575f --- /dev/null +++ b/test_regress/t/t_trace_hashed_name.v @@ -0,0 +1,28 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Ethan Sifferman +// SPDX-License-Identifier: CC0-1.0 + +// Every name here is over the 128 character limit at which VName hashes it, so the +// passes that append to a name append to a hash: V3SplitVar a bit range or an element +// index, V3Class a package suffix. + +class class_with_a_name_long_enough_that_verilator_replaces_it_with_a_hash_because_it_is_over_the_one_hundred_twenty_eight_character_limit; + int x; +endclass + +module t; + logic [1:0] packed_signal_with_a_name_long_enough_that_verilator_replaces_it_with_a_hash_because_it_is_over_the_one_hundred_twenty_eight_char_limit /*verilator split_var*/; + logic unpacked_array_with_a_name_long_enough_that_verilator_replaces_it_with_a_hash_because_it_is_over_the_one_hundred_twenty_eight_char_limit [1:0] /*verilator split_var*/; + class_with_a_name_long_enough_that_verilator_replaces_it_with_a_hash_because_it_is_over_the_one_hundred_twenty_eight_character_limit obj; + + always_comb packed_signal_with_a_name_long_enough_that_verilator_replaces_it_with_a_hash_because_it_is_over_the_one_hundred_twenty_eight_char_limit[0] = 1'b1; + always_comb unpacked_array_with_a_name_long_enough_that_verilator_replaces_it_with_a_hash_because_it_is_over_the_one_hundred_twenty_eight_char_limit[0] = 1'b1; + + initial begin + obj = new; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule