<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/208141>208141</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
[BPF] -g rewrites member-function declare: this promoted to value, aggregate param lost (19/20 miscompile, 21/22 crash)
</td>
</tr>
<tr>
<th>Labels</th>
<td>
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
choury
</td>
</tr>
</table>
<pre>
### Summary
When compiling C++ code for the BPF target with `-g` (debug info), clang **mangles the `declare` of certain member functions**: the `this` parameter (a `ptr dereferenceable(N)` reference) gets incorrectly "promoted" into a value type, and subsequent aggregate-by-value parameters are **lost** from the declaration. The `call` instruction, however, still passes the correct number of arguments — so `declare` and `call` become inconsistent, leading to miscompilation (clang 19/20) or a hard crash (clang 21/22).
### Reproducer
```cpp
// repro.cpp — uses libc++ <filesystem>; the bug manifests on path::__compare(string_view)
#include <filesystem>
int main() {
std::filesystem::path a = "a.txt";
std::filesystem::path b = "a.txt";
return a == b ? 0 : 1; // operator== calls path::__compare
}
```
Compile (the `-D` flags only work around libc++ `__config` probes for the freestanding BPF target; they are unrelated to the bug):
```
clang++ -std=c++20 -target bpf -mcpu=v4 -O0 \
-nostdinc -nostdinc++ -fno-builtin \
-D_LIBCPP_HAS_THREAD_API_PTHREAD -D_LIBCPP_HAS_MUSL_LIBC -D_LIBCPP_HAS_NO_INT128 \
-isystem <libc++>/include/c++/v1 -isystem <musl>/include \
-Xclang -disable-llvm-passes -g -S -emit-llvm repro.cpp -o repro.ll
```
### Expected vs actual `declare`
`path::__compare(string_view) const` is a member function: `this` (`const path&` → `ptr dereferenceable(24)`) + one `string_view` (a 16-byte aggregate, lowered as `[2 x i64]`).
**Without `-g`** (correct — 2 parameters):
```llvm
declare dso_local noundef i32 @_ZNKSt3__14__fs10filesystem4path9__compareENS_17basic_string_viewIcNS_11char_traitsIcEEEE(ptr noundef nonnull align 8 dereferenceable(24), [2 x i64]) #2
```
**With `-g`** (wrong — 1 parameter; `this` became `[2 x i64]`, the `string_view` arg disappeared):
```llvm
declare !dbg !4483 dso_local noundef i32 @_ZNKSt3__14__fs10filesystem4path9__compareENS_17basic_string_viewIcNS_11char_traitsIcEEEE([2 x i64] noundef nonnull align 8 dereferenceable(24)) #2
```
Note the `call` still passes 2 arguments:
```llvm
call ... __compare(...)(ptr noundef nonnull align 8 dereferenceable(24) %a, [2 x i64] %sv)
```
So `declare` (1 arg) and `call` (2 args) disagree → the callee reads garbage at runtime (`operator==` returns wrong results, `directory_iterator`/`canonical` crash, etc.).
### Version matrix
Verified with the `silkeh/clang` docker images and `debian:sid` (clang-22):
| clang | without `-g` | with `-g` | symptom |
|---|---|---|---|
| 19.1.7 | ✅ declare 2 params | ❌ declare 1 param | wrong IR (param lost) → miscompilation |
| 20.1.8 | ✅ declare 2 params | ❌ declare 1 param | wrong IR (param lost) → miscompilation |
| 21.1.8 | ✅ declare 2 params | 💥 **crash** | `DIBuilder::finalize` → `MDNode::replaceOperandWith` null deref |
| 22.1.8 | ✅ declare 2 params | 💥 **crash** | same crash as 21 |
19→22 are all affected. 21/22 made it worse: instead of silently emitting wrong IR, the compiler now crashes.
### Crash backtrace (clang 21/22, `-g`)
```
#5 llvm::MDNode::replaceOperandWith(unsigned int, llvm::Metadata*)
#6 llvm::DIBuilder::finalizeSubprogram(llvm::DISubprogram*)
#7 llvm::DIBuilder::finalize()
#8 clang::CodeGen::CGDebugInfo::finalize()
#9 clang::CodeGen::CodeGenModule::Release()
```
### Analysis
- Without `-g`, the `declare` is correct. With `-g` (even at `-O0` with `-Xclang -disable-llvm-passes`), the `this` parameter (`ptr dereferenceable(24)`) is rewritten into a `[2 x i64]` value type and the trailing aggregate parameter vanishes.
- This only affects **external declarations** of member functions whose `this` is a `dereferenceable` pointer and that take a small aggregate-by-value parameter. The definition (compiled in a different TU) is emitted correctly, so caller/callee ABI diverge after linking.
- Workaround: compile without `-g`.
### Environment
- Reproduced on x86_64 Linux with clang 19.1.7 (Debian), 20.1.8, 21.1.8, 22.1.8 (docker).
</pre>
<img width="1" height="1" alt="" src="http://email.email.llvm.org/o/eJzEWFtv67gR_jXMy0CGRDu2_JAHx453g-7mBCdn9xR9MShpJLOhSJeknLi_vhhStuVcdk-LAgsYiUTxMpdvZj6OcE42GvGGXd-y69WV6PzW2Jtyazp7uCpMdbhhfBx_8NS1rbAHli5Yuvi-RQ2laXdSSd3AkvFbxm-hNBVCbSz4LcLt4xq8sA16eJF-C2yaJg2bpsB4XmHRNSB1bRifM76EUgndAOMLxhet0I1CFzZh07TCUgmLtNLUUKL1QmposS3QQt3p0kujXVzKxovjMr-VjtbshBUterR0rqAvO2-hQos1WtQlikIh4_kDSTJN4TTO-Bwa9A6kLo21WHp1AMb5zprWeKwY5yC1NyBgL1SH4A87JF2ErsB1hcN_dag9iKax2AiPSXFI4syTTA6ExV5tZZyPT1Bb0wY9ou6CNBzBt6hYKZQiQaV23nZBezp1a15wj5YenZdKwU4411uxlx90F4xmahC26VrU3gG74yxP2XwCzrwxNykyOLDA0rQYzKGddB61p9MUiopA4A200kVQBInJ4NGv2ZzxNU_JosaCgK2wFZRWuO15Ds9oDmd8PooYO0PvK-6sqboSbf9lmsZfuduFiWvG12Bp1qjc7QYqdWQBJYuyBygbL2up0B2cx5aN79j4NhiI0NgKLWt03oHRsBN-y8aEp82GVCKL8Nx5K3Wz2Ut8IbQEGaUuVVfh-63ThdQeWiE14zmpzma3LF0AADhfxc2HK-idjgUBbLwipImRf_WMczb-sYXF5wst-s7quDVNoqlrSIECJiMrQG9Fs0MrvLH9PPK9-9Aa6YLNVkNnRN8sg_8J03kfiMmKwFMr0ZBl1QFejH0GYU2nqwvfTFPaXdcyZImdNQW6UzapLaLzQgeonVNL78BDiKNOW1TCY0VY7N1Kfhov3uCGpYsAuv7kJJh11QvCU0j6vFXsakjactex8Wo_geRLCux62Zs00cb5Sury_HTcr9YmKTqpvNTDBavNL_e3y8fHzc-Lp823n7_eLVabxeP95jE-v5nw629Pv4T3N-MPXzb3D98yng-3lhEMhMKzSQmFfN0DlPH1cZiv99nFkrZz6mLycOu_x_hMKukoVSZK7dukTy5JA8kTJNhKH8YHQZiY_kWp9yg5x_bd6w5LctnegSh9J9RlEjp57kciEigz-ZAcHYi3NYKwPigMFJXTNKyIAOfTMEzJY8rm_PNSwSexVoSg5rdgdAD6UJa4v4BsmhQHj-ciEDKmeUGLFQhHy9j1LYdXkNMJu17FXU8ZkH7fpd-azp_qZ18jKHH2af2c7_igtJyRf7Q9eYili964UDmzUaYUCjTFItYgxxzYJN384-FvT3682WSTzaZ2WXrONhOy1PzkgbuHp002K4ST5Wag_n1J41m5FXbjrZDe3Zd3d3d3jOdk0eNx2mjdKQVCyUZD_qml-RIujRTsPuYf4eposQ_M9WINcYyTsbKzsSiNDKBRYCla_Ng5yyPBeONuYRugENntUFhiB39ifcazqiDKk00m-fivccaFdv-9Wz7zwoPxeLTSkTxccBJ-5h8fG4lWwWg0gmGsj0ajcOz_giFg_Fq8AxKNun1fyi91eHpLhhjPMxKbNntDjOgQ-kQhFzDQWMRBIgkUTCiFCBZF5aARthANgvBgO-1li302uqy-kY5S4XYQwWvRdcq7oMg0rSSFv7GHjfT9OkLoOoimjZalCPIFpkVr0JejjwjW72gdEbZWeCtf49ff0cpaYhXJ-xHzUj3jlkpJKJ_TFCpTPqMF2YoG3dEwFRZSUL51suoNFBYkgd-d6_HsRPxny3DOMM-dBi9H3KHdedPSc9wiSZIP__YHZPNRNpqFpeSR-ZLl13AMwj5fuvPnFcuXp899hoiSBAfcfyVl4mgk7POBo9_y35MQPB1lo_yvFiL7YSHWKZuv2ZyzxXV_O-kxFHPpLMBvdX_bSVVR9oyUVAsl_43viuivqwdTYZxkcadEiV8I57qiRE3TQwSHyB2Ky_-P4jrK5_HGIRzw7HhOusjmJ2E5DzSSco-o60BLRsd7CbSiQpCe2KsjZcL9C0VFtyknFWq6HhIR8sRQj4461ovoEaS89RLlQPcuDJdBvkKUz96KEj-4Gy0HZW3-ntQyPr4GCDk0WPtPLM_zTocOQEUX2cBMzkvRi0p4EWzYX3Wmw70_8f5TV-ysaaxoGc-Hs4cfzlvOfmDLeH2K83OIGSPOWJoKf0Ldv_y0wqJr7nVtPl8__4P18eVXU3Wqt9lXVCjcYIPPWOxCC3Vw0sXxBN5ztuUH7QzpjjfzEXx_2yLBPWoqEDT2hc4858I_YOQ9Ms7HfdgG-SFmKx1YfLHSe9THRscHjGjQ_Ajpn84lmhEaQyfaOzh_L7Q8wj-Bb1vZXwtjyLk-gPHVo9VCDVsgfZOHIu5t-wdetsZdKBxuAMHel0qSLYzUJEkUV3jw4hlBgGtD6P9BvyY2YSqspZanFkcMbYohEFDJOpzm4dtvvRVDUsAKTl2k0KMxkRJYKqaRGyxu76GSe7TEDGoSUEn9LHUTLfXd2Od4a6bs05_6rmq-Syp3ei-t0US2jug89VMqMBpe8-lmOoFfpO5eI8aOTZtYOHm-iuU84iqWsvCUnZ76VM3zSAd6jnFV3Yyr-XgurvAmm-XjSX495dnV9mZeF6IssjznOJ9n00LkKGazNK2LFDHn8yt5w1M-TWdpnk4mKc9GJc4w4-NxkY7ra54im6TYCqlGhP2Rsc2VdK7DG57m2SS7UqJA5Y59TXsTIqToGscmqZLOu_M6L70KHdDbxzVRwqTpYY-uR1lyRNmx-sQuo3RwbAWCNzEMQvvvEvOhQgf2GJtg5_qM0YixvPQFa37VWXWz9X4XmHHoyTTSb7tiVJqW8XXIlvFfsrPmn1h6uraT8i7sH_Tf3_D_BAAA__9IypsO">