a.cpp:template<typename T>
struct t1 { T t = sizeof(T); };
void f(t1<int> &p1, t1<short> *&p2);
b.cpp:#include "a.h"
int main() {
t1<int> v1;
t1<short> *v2 = nullptr;
t1<bool> *v3 = nullptr;
f(v1, v2);
}
#include "a.h"
void f(t1<int> &p1, t1<short> *&p2) {
static t1<short> v2;
p2 = &v2;
}
DW_TAG_compile_unit
DW_AT_name ("a.cpp")
DW_TAG_structure_type
DW_AT_name ("t1")
DW_TAG_template_type_parameter
DW_AT_type (0x00000098 "int")
DW_AT_name ("T")
DW_TAG_member
DW_AT_name ("t")
DW_AT_type (0x00000098 "int")
DW_TAG_structure_type
DW_AT_name ("t1")
DW_AT_declaration (true)
DW_TAG_template_type_parameter
DW_AT_type (0x000000e2 "short")
DW_AT_name ("T")
DW_TAG_structure_type
DW_AT_name ("t1")
DW_AT_declaration (true)
DW_TAG_template_type_parameter
DW_AT_type (0x000000fd "bool")
DW_AT_name ("T")
DW_TAG_compile_unit
DW_AT_name ("b.cpp")
DW_TAG_structure_type
DW_AT_name ("t1")
DW_TAG_template_type_parameter
DW_AT_type (0x0000019e "short")
DW_AT_name ("T")
DW_TAG_member
DW_AT_name ("t")
DW_AT_type (0x0000019e "short")
DW_TAG_structure_type
DW_AT_name ("t1")
DW_AT_declaration (true)
DW_TAG_template_type_parameter
DW_AT_type (0x000001b9 "int")
DW_AT_name ("T")
$ gdb ./a.out
(gdb) start
(gdb) ptype v1
type = struct t1<int> [with T = int] {
T t;
}
(gdb) ptype v2
type = struct t1<short> [with T = short] {
T t;
} *
(gdb) ptype v3
type = struct t1<bool> {
<incomplete type>
} *
(gdb) ptype v1.t
type = int
(gdb) ptype v2->t
type = short
(gdb) ptype v3->t
There is no member named t.
Fully expanded names of template instantiations can become impressively large, yeah.
The DWARF Wiki’s Best Practices page http://wiki.dwarfstd.org/index.php?title=Best_Practices recommends including a canonical form of the template parameters in the DW_AT_name attribute. I don’t know that I agree; it talks about omitting qualifiers (namespaces, containing classes) because those can be reconstructed from the DIE hierarchy, but the same argument can be made for template parameters (the difference being that qualifiers come from higher up the tree, while template parameters come from farther down). The DRY principle would seem to apply here.
I’ll verify with our debugger team, but I’m confident that dropping the <params> from the type name will not affect Sony, as our debugger looks at the template-parameter children already (that’s why we have that turned on by default for sce tuning). LLDB seems to be the odd debugger out, here, and we have some control over that. 😊
Oh, is there any consequence for deduplication in LTO? Isn’t that name-based?
--paulr
Fully expanded names of template instantiations can become impressively large, yeah.
The DWARF Wiki’s Best Practices page http://wiki.dwarfstd.org/index.php?title=Best_Practices recommends including a canonical form of the template parameters in the DW_AT_name attribute. I don’t know that I agree; it talks about omitting qualifiers (namespaces, containing classes) because those can be reconstructed from the DIE hierarchy, but the same argument can be made for template parameters (the difference being that qualifiers come from higher up the tree, while template parameters come from farther down). The DRY principle would seem to apply here.
I’ll verify with our debugger team, but I’m confident that dropping the <params> from the type name will not affect Sony, as our debugger looks at the template-parameter children already (that’s why we have that turned on by default for sce tuning). LLDB seems to be the odd debugger out, here, and we have some control over that. 😊
Oh, is there any consequence for deduplication in LTO? Isn’t that name-based?
On Jun 4, 2021, at 6:33 PM, David Blaikie <dbla...@gmail.com> wrote:tl;dr: What if we used only the base name of templates in the DW_AT_name field for function and class templates (eg: "vector" instead of "vector<int, std::allocator<int>>")?
Context:
We (at Google) have been seeing some significant DWARF growth in binaries lately due to increased use of libraries like Eigen and TensorFlow that use expression templates.
This includes some cases where the debug_str.dwo section has exceeded the DWARF32 limit (& the binutils dwp tool silently wrote overflowed indexes into the debug_str_offsets.dwo section, unfortunately - leading to corrupted/garbled names in backtraces) & most of the growth is from the demangled names of complicated/large expression templates.
Options:
One solution would be to move to DWARF64 - though that does make DWARF overall larger, which is an unfortunate cost that would be nice to avoid.
Another might be to rely solely on linkage names (add linkage names to types), since mangled names generally reduce a lot of the duplication - though in some cases it's not a matter of duplication within a single name, but possibly many distinct types used as template parameters - though those types may also be used in other names (& mangled names have no sharing across names).
but perhaps not necessarily the most useful ones. LLDB will search types by name in many situations, but the fact that template types can be formatted in many different ways and may contain whitespace makes this process brittle already.
In order to support currently supported workflows we may need to implement a type lookup where we stri out everything but the basename in the searched type, then do a by-(base)name lookup, and then filter for template arguments. From afar this sounds doable, but we should make sure not to enable this debug info optimization without qualifying it in LLDB first.
I’ve heard back from the Sony debugger folks, and dropping the <params> from the parent DIE’s name will have no bad effect on us. We rebuild the <params> from the children, and we don’t currently make use of the index. If this tactic goes under an option, we’d definitely like to have that set by default under sce-tuning to get the space savings.
> (out of curiosity, any idea what the Sony debugger does for
> pointer template parameters? At least GCC doesn't seem to be
> able to reconstruct those back into strings (you'd basically
> have to symbolize the address value of the parameter (GCC
> doesn't even encode this value, so it's not surprising GDB
> doesn't try to do anything when it's present)) - so I'll
> probably implement this under a flag but not include (ie: use
> the current full textual encoding) any template with a pointer
> template parameter)
>
> eg: extern int i; template<int *> void f1() { } int main() { f1<&i>(); }
Nice catch, we don't do anything clever here either.
This would
appear to be a special case of non-type template params that can
be arbitrary compile-time expressions; for non-pointer params we
provide the computed value of the compile-time expression, which
is arguably sufficient for cases like
constexpr int a = 1; constexpr int b = 2;
template <int> void f2() { }
void int_expr() { f2<a + b>(); }
but it would be nice to do something useful for
extern int j[4];
void ptr_expr() { f1<&j[a+b]>(); }
$ cat > nttp.cpp
extern int i;
template<int*> void f1() { }
int main() { f1<&i>(); }
$ clang++-tot nttp.cpp -g
/usr/local/google/home/blaikie/install/bin/../lib/gcc/x86_64-pc-linux-gnu/10.0.0/../../../../x86_64-pc-linux-gnu/bin/ld: /tmp/nttp-e4e14a.o:(.debug_info+0x63): undefined reference to `i'
clang-13: error: linker command failed with exit code 1 (use -v to see invocation)
$ clang++-tot nttp.cpp
$ g++-tot nttp.cpp
$ g++-tot nttp.cpp -g
$
>> Oh, is there any consequence for deduplication in LTO? Isn’t that name-based?
> Should be OK - that's based on the fully mangled/linkage name of the type, which would be untouched by this.
I’ve recently been reminded that type-unit signatures are hashes of the name, not using the standard-recommended algorithm of hashing the content; I tried to work out which name is actually used, but it’s buried deeper than I am comfortable excavating. Can we make sure that hash is using either the name-with-parameters, or the linkage name, as the input string? We don’t want “foo<int>” and “foo<float>” using the same type-unit signature!
--paulr
Worth checking, but yeah, not a problem - we don't emit class linkage
names, so the only reason we carry the linkage name on types is for
ODR deduplicating during LTO linking, and also using it for type units
when those are enabled - the linkage name is stored in the
DICompositeType's "identifier" field - not something readily confused
with being guaranteed to be the linkage name nor used for
DW_AT_linkage_name, etc. Only used as a unique identifier. That won't
be touched.
As an aside: I do have another direction I'm interested in pursuing
that's related to linkage names, rather than the pretty names: We
could reduce the number of DW_AT_linkage_names we emit by
reconstituting linkage names in symbolizers instead (eg: if we see a
function called "f3" with a single "int" formal parameter and void
return type - we can reconstruct the linkage name for that function as
_Zf3iv or whatever it is).
On one particularly pathological case I'm looking at, the simplified
pretty template names is worth 43% reduction in the final dwp
.debug_str.dwo and a rough estimate on the linkage name (omitting
linkage names from most cases when Clang's building the IR - there are
certain kinds of template cases that are hard to reconstruct, but
others that are easy/do-able with our current DWARF) 52%, and combined
for 95% reduction in debug string size. (a less pathalogical case, one
of Google's largest binaries, it was 26%/56% for 82% total reduction)