Verilator --savable: packages get their own __Vserialize body called from Syms; state-hash tools must handle it
If you hash or diff a Verilator 5.05x --savable model's serialised state (parsing the generated __Vserialize bodies to map byte offsets to variable names), don't assume a single root body. With a SystemVerilog package in the design (e.g. a CPU's *_pkg), Verilator emits a class Vtb_<pkg> with its own __Vserialize in Vtb_<pkg>__Slow.cpp. Vtb__Syms::__Vserialize calls TOP.__Vserialize(os) and then TOP__<pkg>.__Vserialize(os). Even a package with no variables gets a body containing a __Vcheckval and os << vlSymsp->_vm_contextp__, so the context is serialised twice. A generator that exits on 'extra serialize bodies' therefore fails on any design with a package. Fix: walk the Syms body in order, find the member's class in Vtb__Syms.h, and prefix its entries (e.g. TOP__<pkg>.<member>). Give the local checkval a unique name per module, because the generated code is flattened into one function.
When comparing two builds whose layouts differ (hash only the variables both share), drop Verilator internals by name. Drop them both at top level and under the TOP__<x>. prefix: ^(TOP__\w+\.)?(__V|_vm_contextp__$). __Vtrigprevexpr_*, __VdfgRegularize_* and __Vm_* get renumbered by small RTL changes. Use the same filter in every tool that compares by name (hash-include list generation and per-variable diff/locate), otherwise the per-variable diff can report package checkvals or the context block as 'diverged'. On a small RISC-V core: 575 map entries, 71 internals (499 bytes), and 503 common entries covering 99.3% of state bytes.
Related: a two-stage search (coarse K1 samples first, then per-cycle hashing inside the first differing K1 interval) misses differences that appear and vanish between samples. In one real minstret bug, the exact first difference was cycle 22, but K1=256 reported cycle 1025. Keep the exact single-stage search as the default and make the coarse search opt-in.