> Am 12.08.2026 um 18:30 schrieb Patrick Palka <[email protected]>:
>
> On Tue, 11 Aug 2026, Richard Biener wrote:
>
>>> On Mon, 10 Aug 2026, Patrick Palka wrote:
>>>
>>>
>>>> On Mon, 10 Aug 2026, Jason Merrill wrote:
>>>
>>>> On 8/7/26 9:55 AM, Richard Biener wrote:
>>>>> From: Bernhard M. Wiedemann <[email protected]>
>>>>>
>>>>> typename_htab is written to a precompiled header. A hash table is
>>>>> streamed out slot array and all: gt_pch_nx() relocates the pointers
>>>>> inside the entries but leaves every entry in the slot it happened to
>>>>> occupy. typename_hasher hashed on the addresses of the scope and the
>>>>> fullname, so the slots were chosen from addresses that ASLR randomises
>>>>> in the process writing the header, and they no longer correspond to the
>>>>> hash of anything once the header has been read back at a different
>>>>> address.
>>>>>
>>>>> Lookups then find a restored TYPENAME_TYPE only when it happens to lie
>>>>> on the probe sequence of the slot the new hash points at, so most miss
>>>>> and build a duplicate, and which ones miss depends on the layout the
>>>>> writing process had. That makes a compile using a PCH differ from the
>>>>> same compile without one, and differ from itself between runs: the
>>>>> duplicates consume DECL_UIDs, every later DECL_UID shifts, and
>>>>> var-tracking hashes on DECL_UID, so .debug_loclists comes out different.
>>>>>
>>>>> Hash on TYPE_UID/DECL_UID and IDENTIFIER_HASH_VALUE instead, which the
>>>>> header preserves. The name replaces the fullname in the hash because a
>>>>> TEMPLATE_ID_EXPR fullname has no address-independent hash of its own;
>>>>> both are compared by equal() either way.
>>>>
>>>> We could handle TEMPLATE_ID_EXPR by using iterative_hash_template_arg
>>>> instead
>>>> of iterative_hash_object?
>>>>
>>>> But I suppose it's unlikely that we'd need a bunch of typenames that differ
>>>> only in template args, so OK either way.
>>>
>>> IIRC the PR65328 compile-time-hog testcase had a lot of TEMPLATE_ID_EXPR
>>> typenames, and indeed this patch as-is significantly regresses compile
>>> time of the PR65328#c6 testcase since it effectively undoes part of
>>> r13-1047-g343d83c7a89d0c. So I think we need to go with using
>>> iterative_hash_template_arg instead (with comparing_specializations
>>> set so that we produce distinct hashes for nested TYPENAME_TYPE).
>>>
>>>>
>>>>> Compiling qgstopologicalmesh.cpp of qgis 4.2.1, 4757 lookups in the
>>>>> table:
>>>>>
>>>>> no PCH 3046 misses, 3046 entries
>>>>> PCH, before 483-484 misses, 3135-3136 entries, varies per run
>>>>> PCH, after 394 misses, 3046 entries
>>>>>
>>>>> and the object file now matches the one built without a PCH, byte for
>>>>> byte, from any of eight independently generated headers.
>>>>>
>>>>> Bootstrapped and tested on x86_64-unknown-linux-gnu.
>>>>>
>>>>> OK?
>>>>>
>>>>> Thanks,
>>>>> Richard.
>>>>>
>>>>> gcc/cp/ChangeLog:
>>>>>
>>>>> PR pch/124811
>>>>> * decl.cc (typename_hasher::hash): Hash the UIDs of the context
>>>>> and the name rather than their addresses.
>>>>>
>>>>> Assisted-by: Claude
>>>>> ---
>>>>> gcc/cp/decl.cc | 20 +++++++++++++-------
>>>>> 1 file changed, 13 insertions(+), 7 deletions(-)
>>>>>
>>>>> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
>>>>> index b5d9ed05874..c66f59fef79 100644
>>>>> --- a/gcc/cp/decl.cc
>>>>> +++ b/gcc/cp/decl.cc
>>>>> @@ -4920,27 +4920,33 @@ struct typename_hasher : ggc_ptr_hash<tree_node>
>>>>> {
>>>>> typedef typename_info *compare_type;
>>>>> - /* Hash a TYPENAME_TYPE. */
>>>>> + /* Hash a TYPENAME_TYPE. This table goes into a precompiled header,
>>>>> which
>>>>> + moves everything it contains to a different address; entries keep
>>>>> the
>>>>> + slot they were put in, so hashing on the addresses of CONTEXT and
>>>>> NAME
>>>>> + would leave them unfindable once the header has been read back.
>>>>> Hash
>>>>> on
>>>>> + the UIDs instead, which the header preserves. */
>>>
>>> Since this is a common problem with GC'd hash tables and PCH and not
>>> specific to this hash table, I don't think this comment is necessary
>>> here FWIW. It'd be better suited in a more central piece of
>>> documentation such as in the gccint manual, if it's not already
>>> documented there.
>>
>> ISTR seeing such comment in other places, so yes it's redundant.
>>
>> Can you take the patch from here? I'd have to second-guess
>> which tree is the TEMPLATE_ID_EXPR while it's probably obvious to
>> you.
>>
>> The PR identifies two other GC'd and PCH streamed hash tables that
>> hash pointers (but any incremental improvement is good!), which
>> are cp/tree.cc:list_hash_table and cp/constraint.cc:atom_cache
>
> FWIW atom_cache is marked 'deletable' so it's not written to PCH and
> shouldn't affect TYPE/DECL_UID generation, I think...
>
> list_hash_table seems to be only used sometimes for sharing identical
> TYPE_ARG_TYPES nodes inside a FUNCTION_TYPE. At first glance this
> seems to be purely a memory-use optimization and so shouldn't result in
> extra TYPE/DECL_UID nodes being created with PCH vs without.
Ah, that means it should possibly deletable al well (at least not PCH streamed)
> For the typename table, this is what I had in mind, bootstrapped and
> regtested on x86_64-pc-linux-gnu, does this look OK for trunk?
Thanks for taking care of this.
Richard
> -- >8 --
>
> Subject: [PATCH] c++: do not hash TYPENAME_TYPEs on pointers [PR124811]
>
> typename_htab is written to a precompiled header. A hash table is
> streamed out slot array and all: gt_pch_nx() relocates the pointers
> inside the entries but leaves every entry in the slot it happened to
> occupy. typename_hasher hashed on the addresses of the scope and the
> fullname, so the slots were chosen from addresses that ASLR randomises
> in the process writing the header, and they no longer correspond to the
> hash of anything once the header has been read back at a different
> address.
>
> Lookups then find a restored TYPENAME_TYPE only when it happens to lie
> on the probe sequence of the slot the new hash points at, so most miss
> and build a duplicate, and which ones miss depends on the layout the
> writing process had. That makes a compile using a PCH differ from the
> same compile without one, and differ from itself between runs: the
> duplicates consume DECL_UIDs, every later DECL_UID shifts, and
> var-tracking hashes on DECL_UID, so .debug_loclists comes out different.
>
> Hash on TYPE_UID/DECL_UID instead, which the header preserves, and use
> iterative_hash_template_arg to safely hash fullname which can be an
> arbitrary TEMPLATE_ID_EXPR. It's important to hash the fullname instead
> of just the name for sake of the PR c++/65328 compile-time-hog testcase.
>
> PR c++/124811
>
> gcc/cp/ChangeLog:
>
> * decl.cc (typename_hasher::hash): Hash the TYPE_HASH of the
> context and use iterative_hash_template_arg for hashing the
> fullname instead of their addresses.
>
> Co-authored-by: Bernhard M. Wiedemann <[email protected]>
> ---
> gcc/cp/decl.cc | 8 ++++++--
> 1 file changed, 6 insertions(+), 2 deletions(-)
>
> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
> index b5d9ed058748..9699517ee2d4 100644
> --- a/gcc/cp/decl.cc
> +++ b/gcc/cp/decl.cc
> @@ -4926,8 +4926,12 @@ struct typename_hasher : ggc_ptr_hash<tree_node>
> hash (tree context, tree fullname)
> {
> hashval_t hash = 0;
> - hash = iterative_hash_object (context, hash);
> - hash = iterative_hash_object (fullname, hash);
> + hash = iterative_hash_hashval_t (TYPE_HASH (context), hash);
> + /* FULLNAME could be a template-id, so use iterative_hash_template_arg
> here.
> + And might as well set comparing_specializations for stronger hashing.
> */
> + ++comparing_specializations;
> + hash = iterative_hash_template_arg (fullname, hash);
> + --comparing_specializations;
> return hash;
> }
>
> --
> 2.55.0.559.g11c6700f10
>