On Tue, Jul 1, 2025 at 11:20 PM David Faust <[email protected]> wrote:
>
>
>
> On 7/1/25 01:02, Richard Biener wrote:
> > On Mon, Jun 30, 2025 at 9:12 PM David Faust <[email protected]> wrote:
> >>
> >>
> >>
> >> On 6/30/25 06:11, Richard Biener wrote:
> >>>> +static void
> >>>> +gen_btf_decl_tag_dies (tree t, dw_die_ref target, dw_die_ref
> >>>> context_die)
> >>>> +{
> >>>> + if (t == NULL_TREE || !DECL_P (t) || !target)
> >>>> + return;
> >>>> +
> >>>> + tree attr = lookup_attribute ("btf_decl_tag", DECL_ATTRIBUTES (t));
> >>>> + if (attr == NULL_TREE)
> >>>> + return;
> >>>> +
> >>>> + gen_btf_tag_dies (attr, target, context_die);
> >>>> +
> >>>> + /* Strip the decl tag attribute once we have created the annotation
> >>>> DIEs
> >>>> + to avoid attempting process it multiple times. Global variable
> >>>> + declarations may reach this function more than once. */
> >>>> + DECL_ATTRIBUTES (t)
> >>>> + = remove_attribute ("btf_decl_tag", DECL_ATTRIBUTES (t));
> >>> I do not like modifying trees as part of dwarf2out. You should be able to
> >>> see whether a DIE already has the respective attribute applied?
> >>
> >> Yes, you're right. For decl_tag the case is simple and better handled by
> >> consulting the hash table. Simple fix and this remove_attribute can be
> >> deleted.
> >>
> >> Understood re: modifying trees in dwarf2out. I agree it's not ideal.
> >>
> >> For this case the remove_attribute can be deleted. For the two below,
> >> one is already immediately restored and the other could be as well so
> >> that there are no lasting changes in the tree at all.
> >>
> >> I will explain the reasoning some more below.
> >>
> >>>
> >>>> +}
> >>>> +
> >>>> /* Given a pointer to an arbitrary ..._TYPE tree node, return a
> >>>> debugging
> >>>> entry that chains the modifiers specified by CV_QUALS in front of the
> >>>> given type. REVERSE is true if the type is to be interpreted in the
> >>>> @@ -13674,6 +13894,7 @@ modified_type_die (tree type, int cv_quals, bool
> >>>> reverse,
> >>>> tree item_type = NULL;
> >>>> tree qualified_type;
> >>>> tree name, low, high;
> >>>> + tree tags;
> >>>> dw_die_ref mod_scope;
> >>>> struct array_descr_info info;
> >>>> /* Only these cv-qualifiers are currently handled. */
> >>>> @@ -13783,10 +14004,62 @@ modified_type_die (tree type, int cv_quals,
> >>>> bool reverse,
> >>>> dquals &= cv_qual_mask;
> >>>> if ((dquals & ~cv_quals) != TYPE_UNQUALIFIED
> >>>> || (cv_quals == dquals && DECL_ORIGINAL_TYPE (name) !=
> >>>> type))
> >>>> - /* cv-unqualified version of named type. Just use
> >>>> - the unnamed type to which it refers. */
> >>>> - return modified_type_die (DECL_ORIGINAL_TYPE (name),
> >>>> cv_quals,
> >>>> - reverse, context_die);
> >>>> + {
> >>>> + tree dtags = lookup_attribute ("btf_type_tag",
> >>>> + TYPE_ATTRIBUTES (dtype));
> >>>> + if ((tags = lookup_attribute ("btf_type_tag",
> >>>> + TYPE_ATTRIBUTES (type)))
> >>>> + && !attribute_list_equal (tags, dtags))
> >>>> + {
> >>>> + /* Use of a typedef with additional btf_type_tags.
> >>>> + Create a new typedef DIE to which we can attach the
> >>>> + additional type_tag DIEs without disturbing other
> >>>> users of
> >>>> + the underlying typedef. */
> >>>> + dw_die_ref mod_die = modified_type_die (dtype,
> >>>> cv_quals,
> >>>> + reverse,
> >>>> context_die);
> >>>> + mod_die = clone_die (mod_die);
> >>>> + add_child_die (comp_unit_die (), mod_die);
> >>>> + if (!lookup_type_die (type))
> >>>> + equate_type_number_to_die (type, mod_die);
> >>>> +
> >>>> + /* 'tags' is an accumulated list of type_tag attributes
> >>>> + for the typedef'd type on both sides of the typedef.
> >>>> + 'dtags' is the set of type_tag attributes only
> >>>> appearing
> >>>> + in the typedef itself.
> >>>> + Find the set of type_tags only on the _use_ of the
> >>>> + typedef, i.e. (tags - dtags). By construction these
> >>>> + additional type_tags have been chained onto the
> >>>> head of
> >>>> + the attribute list of the original typedef. */
> >>>> + tree t = tags;
> >>>> + bool altered_chain = false;
> >>>> + while (t)
> >>>> + {
> >>>> + if (TREE_CHAIN (t) == dtags)
> >>>> + {
> >>>> + TREE_CHAIN (t) = NULL_TREE;
> >>>> + altered_chain = true;
> >>>> + break;
> >>>> + }
> >>>> + t = TREE_CHAIN (t);
> >>>> + }
> >>> And this might be part of that and should thus split out, or be part
> >>> of gen_btf_type_tag_dies itself? I expected the hashtable to
> >>> cover the DIEs we have for existing chains of tags?
> >>
> >> OK, this can move into gen_btf_type_tag_dies.
> >>
> >> The reason the hash table doesn't help us here is that the uncertainty
> >> is in the attribute list itself.
> >>
> >> Specifically, if the type of a variable is a typedef with an additional
> >> type attribute applied, then it is unclear from just the tree node of
> >> the type of the variable whether that attribute is "new" on the use of
> >> the typedef, or is part of the typedef itself.
> >>
> >> For example:
> >>
> >> typedef int __attribute__((btf_type_tag ("tag1"))) foo;
> >> foo __attribute__((btf_type_tag ("tag2"))) x;
> >>
> >> For the typedef, we create a DW_TAG_typedef DIE with name "foo", with
> >> AT_type referring to a base type DIE for an integer type with "tag1"
> >> attached via AT_GNU_annotation. So far so good.
> >>
> >> When generating dwarf for variable 'x', the tree node we have for
> >> the type of 'x' is an integer type node with name "foo" but the attribute
> >> list contains _both_ "tag2" and "tag1". This is what I mean with the
> >> comment in the code above about the "accumulated list" from both sides
> >> of the typedef. It happens because we have a variant type node of
> >> the named type foo created, in order to be a distinct node for
> >> (foo + "tag2") for the type of 'x'.
> >
> > So I view DW_TAG_GNU_annotation as a way to represent attributes
> > on types and decls going forward (eventually DWARF will gain sth
> > standard here for [[..]] - who knows). As you say we treat attributed
> > types as variants, I do not see why the attribute list of the
> > variant type for 'x' has both tags?
> AFAIU, the attribute list has both tags just because there is not a
> distinct type node for the typedef, rather it adds a name to the
> integer type node. So there isn't a place in the tree currently to
> put the attribute to distinguish it being on one side of the typedef
> or the other, they all get lumped into one attribute list.
I see. So
typedef int __attribute__((btf_type_tag ("tag1"))) foo;
typedef int __attribute__((btf_type_tag ("tag2"))) bar;
gets us two identical tagged types? Or is it that
foo __attribute__((btf_type_tag ("tag2"))) x;
fails to generate a type variant for applying the attribute to? And
thus a followup
foo y;
will also be tag2-tagged?
It feels like we must get one of those cases "wrong" if it is like
you say?
> It's also possible that I am doing something wrong in the FE
> attribute handler, but I'm not sure how else the attributes could
> be represented in the tree.
I'll see in the other patch if I spot something.
> > And even if it has, I'd simply let
> > it go and have DW_TAG_GNU_annotation with both tags on the
> > type DIE and one with just 'tag1' on the typedef DIE for 'foo'.
>
> OK, I can do that. For the current uses of type_tag in the kernel
> BPF subsystem, I don't think this case is very relevant.
>
> >
> > What about
> >
> > typedef int __attribute__((btf_type_tag ("tag1"))) foo;
> > foo __attribute__((btf_type_tag ("tag2"), btftype_tag ("tag1"))) x;
> >
> > in your scheme?
>
> The resulting tree node for the type of x here is actually identical
> to what we get in the above example when x only has "tag2". We can't
> distinguish the two cases from the tree.
>
> If we have both, e.g.
>
> typedef int __attribute__((btf_type_tag ("tag1"))) foo;
> foo __attribute__((btf_type_tag ("tag2"), btf_type_tag ("tag1"))) x;
> foo __attribute__((btf_type_tag ("tag2"))) y;
>
> Then both var_decl nodes for 'x' and 'y' share exactly the same
> node as their type, an integer_type node named "foo" with exactly
> one each of "tag1" and "tag2" in the attribute list.
>
> I think that the current attribute handling will not add an attribute
> with the same name and value more than once to a given type's
> attribute list. That affects us here because there isn't a distinct
> attribute list for the typedef. So any variant of "foo" just has
> one attribute list that lumps together all the relevant attributes,
> which also ends up in effect deduplicating them.
OK, I see. I think given it's imperfect on the tree side we should
(at least initially) not worry about the DWARF side getting things
"wrong".
> >
> > I think dwarf2out needs to emit what is on the actual tree types and
> > decls, not what we reverse engineer would be likely present on the
> > source. If what the trees represent does not agree with what we like
> > to be present in the DWARF we should fix the tree side.
>
> That makes sense to me. I do think that this means putting information
> in the tree that isn't necessarily relevant from program-semantics or
> C type system view, but allows us to more accurately reflect source
> constructs in DWARF.
>
> Modifying the tree nodes like this will probably ultimately be the right
> approach. No objection there, nor to working on the implementation.
>
> I am worried though that it could take quite some time to design and fully
> implement. The kernel BPF subsystem has real need for these tags in some
> form, it's been a couple of years now and they are piling up hacks to work
> around GCC not supporting type_tag and decl_tag yet.
>
> So, if the overall design is OK (with a 2nd ack) it would be great if
> we can give them something that works, and then work on improving and
> extending it.
I think the overall design is OK.
> >
> >> IOW, 'dtags' holds "tag1" since it comes from the typedef itself,
> >> and 'tags' holds both "tag1" and "tag2". We need to do some extra work
> >> looking at the tree to figure out which type_tags are actually on the
> >> _use_ of the typedef vs. part of the typdef itself, in order to correctly
> >> decide which chains of tags should be associated to which DIE.
> >>
> >> Note that the attribute chain in the tree is immediately restored
> >> after generating the tag dies...
> >>>
> >>>> +
> >>>> + /* Now generate the type_tag DIEs only for the new
> >>>> + type_tags appearing in the use of the typedef, and
> >>>> + attach them to the cloned typedef DIE. */
> >>>> + gen_btf_type_tag_dies (type, tags, mod_die,
> >>>> context_die);
> >>>> +
> >>>> + /* Restore the list of type_tags. In principle, the
> >>>> same
> >>>> + attribute list could be shared by unrelated types.
> >>>> */
> >>>> + if (altered_chain)
> >>>> + TREE_CHAIN (t) = dtags;
> >>
> >> ...here. So there are no lasting changes in the tree.
> >>
> >> It could be done w/o altering the tree at all by duplicating the relevant
> >> attribute list and operating on that, but it seems a little wasteful.
> >>
> >>>> +
> >>>> + return mod_die;
> >>>> + }
> >>>> + /* cv-unqualified version of named type. Just use
> >>>> + the unnamed type to which it refers. */
> >>>> + return modified_type_die (DECL_ORIGINAL_TYPE (name),
> >>>> cv_quals,
> >>>> + reverse, context_die);
> >>>> + }
> >>>> /* Else cv-qualified version of named type; fall through. */
> >>>> }
> >>>> }
> >>>> @@ -13887,6 +14160,17 @@ modified_type_die (tree type, int cv_quals,
> >>>> bool reverse,
> >>>> first_quals |= dwarf_qual_info[i].q;
> >>>> }
> >>>> }
> >>>> + else if ((tags = lookup_attribute ("btf_type_tag", TYPE_ATTRIBUTES
> >>>> (type))))
> >>>> + {
> >>>> + /* Remove type tags before the recursive call to avoid processing
> >>>> the
> >>>> + same attribute multiple times. */
> >>>> + TYPE_ATTRIBUTES (type) = remove_attribute ("btf_type_tag",
> >>>> + TYPE_ATTRIBUTES (type));
> >>> See above.
> >>
> >> The reason for this one is simply the issue of recursion.
> >>
> >> Doing remove_attribute here is analogous to the cv-qual handling using
> >> get_nearest_type_subqualifiers and recursing with the lesser qualified
> >> type. In this case the lesser qualified type is the same type without
> >> the type_tag attribute(s)
> >
> > But don't we have a variant type without the attribute? That's the basic
> > premise of how modified_type_die works. If we cannot fit attribute
> > handling into this scheme then it's not the appropriate "target"? Maybe
> > we need to force the attribute to be always applied to a decl then?
> > Or make sure there's a type variant with all CV qualifiers but no attributes
> > present. Like for
> >
> > const int __attribute__((btf_type_tag("tag1"))) a;
> >
> > make sure we have type trees for 'int' (main variant - always present),
> > 'const int' (possibly not present variant tree right now?) and
> > 'const int __attribute__((btf_type_tag("tag1")))' (the TREE_TYPE of 'a')?
>
> Hmm... right. There should always be a non-attributed variant of the type
> somewhere. Rather than remove the attribute and recurse to generate
> DWARF for it, it would be better to directly locate that variant and
> ensure DWARF is generated for it first. If the non-attributed type is
> used, then we duplicate its DIE to use here and attach the annotations.
>
> But, if the non-attributed variant is not used elsewhere in the TU, then
> maybe by this point it doesn't exist to find. It sounds a little familiar,
> maybe I ran into this originally which led to the remove_attribute...
Quite possibly.
> >
> > Otherwise the attribute handling would need to be integrated into the
> > "CV" qualification handing of dwarf2out.
> >
> >> I think the alternative would be to split the type_tags into a separate
> >> argument to modified_type_die, similar to cv_quals, which seemed like
> >> a much more invasive set of changes to make.
> >
> > It might be the proper thing if the attribute-less type tree isn't in
> > the variant
> > set. That is, I'd pass the whole TYPE_ATTRIBUTES as separate qualification
> > from cv_quals and to be handled/stripped first, forcing a separate type DIE
> > to not interfere with cv_quals handling.
>
> Maybe this will be the best option for now then.
>
> Either way, I agree that the remove_attribute is not good and
> should not be necessary. I will look into this more and find a way
> to get rid of it.
Thanks!
> >
> > This is after all laying ground for TYPE_ATTRIBUTES encoding (currently
> > nothing in dwarf2out looks at those, almost nothing at DECL_ATTRIBUTES).
> >
> > Richard.
>
> Thank you for the reviews and feedback so far, it's very helpful :)
>
> >
> >>>
> >>>> + dw_die_ref mod_die = modified_type_die (type, cv_quals, reverse,
> >>>> + context_die);
> >>>> + gen_btf_type_tag_dies (type, tags, mod_die, context_die);
> >>
> >> It should work fine to save TYPE_ATTRIBUTES above and restore it to the
> >> original here after the recursive call so that ultimately the tree
> >> is unchanged.
> >>
> >> Would the same approach with that modification be OK?
> >>
> >>>> + return mod_die;
> >>>> + }
> >>>> else if (code == POINTER_TYPE || code == REFERENCE_TYPE)
> >>>> {
> >>>> dwarf_tag tag = DW_TAG_pointer_type;
> >>>> @@ -22770,6 +23054,7 @@ gen_array_type_die (tree type, dw_die_ref
> >>>> context_die)
> >>
>