Re: [PATCH] c++: do not hash TYPENAME_TYPEs on pointers [PR124811]
Richard Biener <[email protected]>
| Newsgroups | gmane.comp.gcc.patches |
|---|---|
| Message-ID | <[email protected]> |
> 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 >