<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/83616>83616</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
Incorrect Microsoft mangled name for the guard variable of a static local variable
</td>
</tr>
<tr>
<th>Labels</th>
<td>
new issue
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
tahonermann
</td>
</tr>
</table>
<pre>
Clang and MSVC generate different mangled names for the guard variable of a static local variable as demonstrated in the following example exercised with MSVC 19.35.32216.1 for x64 from Visual Studio 2022 and Clang 17. See https://godbolt.org/z/MznoPb8bb.
```
> type t.cpp
struct __declspec(IMPEXP) S {
static S& get();
static S& singleton() {
static S& s = get();
return s;
}
};
S& f() {
return S::singleton();
}
# MSVC compilation for dllexport:
> cl /nologo /c /DIMPEXP=dllexport t.cpp
t.cpp
> llvm-objdump.exe -t t.obj | find "singleton"
[18](sec 6)(fl 0x00)(ty 20)(scl 2) (nx 0) 0x00000000 ?singleton@S@@SAAEAU1@XZ
...
[39](sec 11)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000000 ?s@?1??singleton@S@@SAAEAU2@XZ@4AEAU2@EA
[42](sec 12)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000000 ?$TSS0@?1??singleton@S@@SAAEAU2@XZ@4HA
# Clang compilation for dllexport:
> clang -c -DIMPEXP=dllexport t.cpp
> llvm-objdump -t t.o | find "singleton"
[13](sec 5)(fl 0x00)(ty 20)(scl 2) (nx 0) 0x00000000 ?singleton@S@@SAAEAU1@XZ
[25](sec 8)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000000 ?s@?1??singleton@S@@SAAEAU2@XZ@4AEAU2@EA
[28](sec 9)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000000 ?$TSS0@?1??singleton@S@@SAAEAU1@XZ@4HA
...
```
The mangled names for `S::singleton()` and its static local variable `s` match, but the guard variable names differ with regard to a substitution reference ("2" vs "1"):
- MSVC: `?$TSS0@?1??singleton@S@@SAAEAU2@XZ@4HA`
- LLVM: `?$TSS0@?1??singleton@S@@SAAEAU1@XZ@4HA`
Demangling demonstrates that the MSVC generated one is correct. Note that the return type is wrong for the second name below.
```
# MSVC:
> llvm-undname '?$TSS0@?1??singleton@S@@SAAEAU2@XZ@4HA'
?$TSS0@?1??singleton@S@@SAAEAU2@XZ@4HA
int `public: static struct S & __cdecl S::singleton(void)'::`2'::$TSS0
# LLVM:
> llvm-undname '?$TSS0@?1??singleton@S@@SAAEAU1@XZ@4HA'
?$TSS0@?1??singleton@S@@SAAEAU1@XZ@4HA
int `public: static struct singleton & __cdecl S::singleton(void)'::`2'::$TSS0
```
Since the guard variable is only accessible by `S::singleton()`, this mismatch normally isn't an issue; any TU that exports a definition of `S::singleton()` will provide an implementation that references the guard variable exported from the same TU. For TUs that import the definition, no references to the symbols for the local static variable or its guard variable will be emitted.
```
# MSVC compilation for dllimport:
> cl /nologo /c /DIMPEXP=dllimport t.cpp
t.cpp
> llvm-objdump.exe" -t t.obj | find "singleton"
[ 9](sec 0)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000000 __imp_?singleton@S@@SAAEAU1@XZ
# Clang compilation for dllimport:
> clang -c -DIMPEXP=dllimport t.cpp
> llvm-objdump -t t.o | find "singleton"
[14](sec 0)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000000 __imp_?singleton@S@@SAAEAU1@XZ
```
However, when `S::singleton()` is declared with `__forceinline`, problems ensue. MSVC apparently does not inline imported inline functions even when declared `__forceinline`. Meanwhile, Clang inlines the function definition, but emits import references for the static local variable and its guard variable. See https://godbolt.org/z/qKdazcTah.
```
# Same source file above with __forceinline added to the S::singleton() declaration.
> type t.cpp
struct __declspec(IMPEXP) S {
static S& get();
__forceinline static S& singleton() {
static S& s = get();
return s;
}
};
S& f() {
return S::singleton();
}
# MSVC compilation for dllimport with __forceinline:
> cl /nologo /c /DIMPEXP=dllimport t.cpp
t.cpp
> llvm-objdump.exe -t t.obj | find "singleton"
[ 9](sec 0)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000000 __imp_?singleton@S@@SAAEAU1@XZ
# Clang compilation for dllimport with __forceinline:
> clang -c -DIMPEXP=dllimport t.cpp
> llvm-objdump -t t.o | find "singleton"
[14](sec 0)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000000 __imp_?$TSS0@?1??singleton@S@@SAAEAU1@XZ@4HA
[19](sec 0)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000000 __imp_?s@?1??singleton@S@@SAAEAU2@XZ@4AEAU2@EA
```
This difference in behavior results in link failures when a `__forceinline` function with a static local variable is:
1. compiled for export via `declspec(dllexport)` in a DLL using MSVC, and
2. compiled for import via `declspec(dllimport)` in a DLL/Executable using Clang.
The link failure occurs because Clang emits a dependency on the guard variable symbol and that dependency isn't satisfied by the MSVC built DLL (due to the difference in name mangling).
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJzcWd1u47oRfhr6ZhBBovx74YtsHOMcdFMs4OzioDcBRY1sbinSJSk73qcvSMq_UbLJSbqnbRDIlkTODIcz33xDM2vFUiFOyeATGcx6rHErbaaOrbRCUzOleoUud9MbydQSmCrhbvHtBpao0DCHUIqqQoPKQc3UUmIJitVoodIG3Aph2TBTwoYZwQqJoCtgYB1zgoPUnMnjK2ahxFor67zgEoQKAiotpd4KtQR8ZPVaIuAjGi4slrAVbhXtySZJPkhySrNhkgXlj8M-VEbX8E3YhklYuKYUGmhKaVhGXFA2SmCBCCvn1pbk14TOCZ0vdVlo6RJtloTOfxA6v_uh9JdiXBQJSWckvSbDtP2Pt_ktuN0awSV8vY7PrDMNd_DwUCKXdo2c0PHvd19u__hC6AQWQEaf4kDYO2RB6BCW6AgdEzoheed7K7ybnVZx1KmYi4FA8lmnOACDrjEK7PEhGc3apYxmh6dBTvVUUTt94R2WX19YdJh9FBmvNI97xXW9FpI5oVXYqVJKfFxr47y4gzu5BELnSku91P4b95dZ68B8dph06vKTr0dBUm7qK118L5t6neAjwpWfo4vvQEY3UAlVAqH0ZBG0nTv4lI3JYEbo2CIHGPq10XElIX1M03jjdgC0_W65BAAaXEXH6hH88zA2_gHJ50ct_XRB-qn_uL6-vf6akX76xz-i4iRJDhbkk6MFWfaMBfAWC7zWfJ6RfP6SPTTa00_7-7vb64NRfXpiFP0Aowjt3y8W6VtN--36Mr5iUr82wPzYKw5XP4mq7lBqw-gVQZSfBNHgVwURGXyigxPF4_-S2KGnKTX51bGTPYmdY7JdIHq43q-wo66RYfoM9A3TUFuEs88UOTJMrR9VM8dXhN5A0biuOhmVxeIay5zBpR_htC-gTWGdcE2IcIOhAHOEYASlhFLYWB-PmY9Dj8jtcq4CAJP82tvxrrzbu-kKPn_-dvcnJWZdEuN1hsHtvuyfkAILbsWiv85YSAlaIQgLXBuD3CXwd-3wOLitV6FCCwtbo9XywFAscq3i9kKBUm-fCYi2fJ1hSACERpVhMqGjd_mUjvaC3wuIQjm_H-umkIL7zWmDsaUkC_B1_eGBe2bSVcU3WpQhA0fxHRmm9HjT2nYBvW0cfJxvso_wzdOE_4lvDpI-0EddyLIQPmM7Ml9Y0ErugHGO1gr_qNi9CDkeRtxKWKiFDbgCSpuaSbkDYRWhIwdMgbC2QZJ_AqZ2cP81JkcsdhYYlFgJJQKg6OplhNsKKWFt9EaUGCR7Vl6jcrHiBsEHULJda4xqsYwEPSShD5L7rwnMtYH7r22iizrW4hWe2OeXq_SZBh1l7OpCy2PrEaG33d5jB2ICPF9YFNZUIGAtnMPyZQjoIhjR1Dcy2P363shgPcC_nsTCCYXcF9Y_X3EfHkS9fng1C_k5M-t0XCcze-qt9zKz_l_omS5Q-E1vcYPGR_h2herlNBS-X-aSmX0jTIbpw0OlDUehpFDYQsPa6EJibQGVbTCJEczWa-a7drmDUqMFpR3EWW3ShQY83FeN4n7DLOAGVTTsoLhDZwJ3yNR2JSR69XHj49sIBnuBFzntmZBPP7tP-5MMP9Tq7pODlnKd5_TrGvt__a1kP_g9Wz2f8guPTVY3hiNUwusr9Aajz88WD6wssdzDUffOta4LKZD8isODcws_9iTh6THCX3yO0EbO0635NcD8_4bKP3fk_zRQv5NJesP-Q7v47ka7u5kV9nBeyxGEggJXbCO0AYO2kR56FUih_gkVE7IxaCPcsy6cP-J4iJLnjnWFPYZMuGZJG3Ce_2nTkkHYiKDlBO-OxzZtwfOGzD5_hsZ7JDZj9MajfxRMLwS3YdgpuGUd54IJnd8-Im9cMDxqCRmSXJ4JnPoINOeNsVAgZ43FNqdiJfO8eo2qRMV3oFUXF46sNdSwwHlPJuzZu2VO2Epg6TuBQ_NbNEK64A-_pAb3ded8h0P3te-lCZ0kvXKal5N8wno4zUbpJKf9bDLoraZZOcBBmaXFoF-N0kHOxjgejSfD8XiUT4ajUU9MaUr7aZ5mNM2G-SDJyzEtM5wMeTXhbDQi_RRrJmTik9zX2F5oOqbjfJgNe5IVKG34nYFShdu2I6GUDGY9Mw3AUDRLH_tSWGePUpxwEqe_q7a7hzvBjba6Ov_N4e0_OfQaI6cXBEG4VVMkXNeEzr0B7cfV2ujvyB2h82C2JXQelvXvAAAA__-W1V_P">