Quuxplusone / LLVMBugzillaTest

0 stars 0 forks source link

CNTTP materialisation exported even for unnamed-namespace content #51196

Open Quuxplusone opened 3 years ago

Quuxplusone commented 3 years ago
Bugzilla Link PR52229
Status CONFIRMED
Importance P normal
Reported by Thiago Macieira (thiago@kde.org)
Reported on 2021-10-19 14:11:34 -0700
Last modified on 2021-10-19 15:06:26 -0700
Version trunk
Hardware PC Linux
CC blitzrakete@gmail.com, erik.pilkington@gmail.com, llvm-bugs@lists.llvm.org, richard-llvm@metafoo.co.uk
Fixed by commit(s)
Attachments
Blocks
Blocked by
See also
When a non-type class template parameter is materialised because its address is
taken, clang emits it as weak but always global. That content does not obey
either -fvisibility=hidden nor the fact that the type may be in an unnamed
namespace. The latter produces broken code.

$ cat test1.cpp
#include <stdio.h>

[[gnu::noinline]] void dump(const void *data, size_t len)
{
    const char *ptr = reinterpret_cast<const char *>(data);
    const char *end = ptr + len;
    printf("%p(%zd) = ", data, len);
    for ( ; ptr != end; ++ptr)
        printf("%02x", *ptr);
    puts("");
}

namespace { struct Foo { short i; }; }
template <Foo f> void test1()
{
    dump(&f, sizeof(f));
    printf("expected: %d\n", f.i);
}

void test2();
int main()
{
    test1<Foo{}>();
    test2();
}

$ cat test2.cpp
#include <stdio.h>

void dump(const void *data, size_t len);

namespace { struct Foo { int Foo::* i; }; }
template <Foo f> void test2()
{
    dump(&f, sizeof(f));
    printf("expected: %td\n", f.i);
}

void test2()
{
    test2<Foo{}>();
}

$ g++ -O2 -std=c++20 test1.cpp test2.cpp
$ ./a.out
0x402022(2) = 0000
expected: 0
0x402038(8) = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
expected: -1
$ clang -O2 -std=c++20 test1.cpp test2.cpp
tjmaciei@tjmaciei-mobl5 /tmp $ ./a.out
0x402028(2) = 0000
expected: 0
0x402028(8) = 0000657870656374
expected: -1

The assembly for test2.cpp ends in:
        .type   _ZTAXtlN12_GLOBAL__N_13FooEEE,@object # @_ZTAXtlN12_GLOBAL__N_13FooEEE
        .section        .rodata._ZTAXtlN12_GLOBAL__N_13FooEEE,"aG",@progbits,_ZTAXtlN12_GLOBAL__N_13FooEEE,comdat
        .weak   _ZTAXtlN12_GLOBAL__N_13FooEEE
        .p2align        3
_ZTAXtlN12_GLOBAL__N_13FooEEE:
        .quad   -1                              # 0xffffffffffffffff

The _ZTA symbol is .weak but exported. Therefore, the <unnamed
namespace>::Foo{} expression in it matches the other Foo expression, thus
making the two symbols get merged by the linker, even though they don't have
the same size or contents.

$ clang --version
clang version 13.0.0
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
Quuxplusone commented 3 years ago

Confirmed. We're already computing the linkage and visibility of the template argument and using it to determine the linkage and visibility of the template specialization, but we don't seem to use that information to determine the linkage and visibility of the template parameter object.

Quuxplusone commented 3 years ago
Another similar case we should make sure we handle properly:

struct Foo { int *i; }; namespace { int n; }
template <Foo f> void test() {
  // ...
}
// Should result in an internal linkage template parameter object.
void test() { test<Foo{&n}>(); }
Quuxplusone commented 3 years ago

Please ignore the too-many-ff output (should have used unsigned char).

BTW, my first attempt when I was told of this issue was to test with template and pass the Foo{} as a parameter. That DOES NOT produce the problem.