<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/140009>140009</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
Clang emits llvm.memcpy for closure type without lambda-capature in C++20 but not in C++17
</td>
</tr>
<tr>
<th>Labels</th>
<td>
clang:codegen
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
MaskRay
</td>
</tr>
</table>
<pre>
In C++20, a closure type without lambda-capture has a defaulted default constructor, different from previous versions of standards.
https://eel.is/c++draft/expr.prim.lambda#capture
> The closure type associated with a lambda-expression has no default constructor if the lambda-expression has a lambda-capture and a defaulted default constructor otherwise.
This leads to a redundant `memcpy` call for the following C++ code: https://godbolt.org/z/9xbM5fj5W
```cpp
#define F(a, b) { \
auto check = [](const char *, long) {}; \
check(a, b); \
asm("// %0 %1" : : "r"(a), "r"(b)); }
void foo() { F("hello", 3); }
```
```llvm
// -std=c++17 -O0
%class.anon = type { i8 }
@.str = private unnamed_addr constant [6 x i8] c"hello\00", align 1
define dso_local void @foo()() #0 {
%1 = alloca %class.anon, align 1
call void @"foo()::$_0::operator()(char const*, long) const"(ptr noundef nonnull align 1 dereferenceable(1) %1, ptr noundef @.str, i64 noundef 3)
call void asm sideeffect "// $0 $1", "r,r,~{dirflag},~{fpsr},~{flags}"(ptr @.str, i32 3) #2
ret void
}
// -std=c++20 -O0
%class.anon = type { i8 }
@__const.foo().check = private unnamed_addr constant %class.anon undef, align 1
@.str = private unnamed_addr constant [6 x i8] c"hello\00", align 1
define dso_local void @foo()() #0 {
%1 = alloca %class.anon, align 1
call void @llvm.memcpy.p0.p0.i64(ptr align 1 %1, ptr align 1 @__const.foo().check, i64 1, i1 false)
call void @"foo()::$_0::operator()(char const*, long) const"(ptr noundef nonnull align 1 dereferenceable(1) %1, ptr noundef @.str, i64 noundef 3)
call void asm sideeffect "// $0 $1", "r,r,~{dirflag},~{fpsr},~{flags}"(ptr @.str, i32 3) #3
ret void
}
```
In -O0, the `memcpy` is retained, and leads to something like `movb .L__const.foo().check(%rip), %al` on x86-64.
A redundant capture `auto check = [x=a](const char *, long) {};` suppresses the `llvm.memcpy` intrinsic (but introduces a useless `const char*` store).
</pre>
<img width="1" height="1" alt="" src="http://email.email.llvm.org/o/eJzsVluL47gS_jXKS9FGkW_xQx7SyQkMnOHAYeA8NrJUjjUjS0aSM93nYX_7Itm59Uwzu7BPy4LTHVdcl--rT67i3quTQdyS8pmUhxWfQm_d9jP33_7L31atlW_bTwb2hD0T9swoYXvgILT1k0MIbyPCdxV6OwXQfGglfxJ8DPG3nnvgILHjkw4oL99AWOODm0SwLgaTquvQoQnQOTvA6PCs7OThjM4razzYDnzgRnInfUborg9h9CTfEXYk7IioM-UJO4q5Qul4F6L9dXTZ6NSQzWURli-FEbqLV_4v-NLjIxLuvRWKx2ojKOAXTDEa-lhPgmXsz9CA6iD0-IEPf88PN_JX_IANPbrvymM2V_2lVx40cukhWODgUE5GchOAVHTAQYxvpKIguNbQWZfK6azW9rsyp0sTQViJJN_BI5MnK1urQ2bdibDj_wk7Nq_t57L7Wv5voayi8yXGMd6yXGKnDMKRsA2PvWwJa4DUz0DKPaE74FOwIHoU34DkB5glRtgmQQTRcweE7aKntua0OJP6QPJrCIA5wH2Kx5-5HwjbEMZmGEBYSeOfNWEMIsz0YcylR2KUJga6WVLIJWp9mLGerZLQWZsiz5iOc5YetbbJcQ_5o9uFn3d0aX0eEl-pvCcfJMkPi1zXNTz9JzmwUmjufcaNNYmtpMiYWG1udZGCZj649MDo1JkHhMkYPqB84VK6WT1JEOVzBa-gNqQ8gLgWXu4pXarnWp0MrOfASyulty_aCq4hMUAKeiXhQgXLaWpTZD_SnGrhOnrBA4rHHDCr8hKWMHaLnO-SCosXOn-1Izqe3g9L4iSVBO2dXhZbbOMYHBg7GYkdGGvMpPUlPUh0mN4yAnmrkbDNesYSVbKHe9eF4WhWVXE1p1Y_ouB-AK8kYtehCHCvwCIqsFgvRM9S28fPb6R-lsp1mp9iT2dDN3p3d6f5yafbC6r7knKWSoltYKkehyGVE9t4VcnPpMbon5bay0viN7t2Krsd5l-o7yFHovCd5P4uQo6nO5tfvdlI46WqYmncRX73OrvaPqT3Ir3kotbQce3xR_n9c4j-ikOUf3CIHt_ln0w6O2yfJurDrFU-enNlUCadGHkb0N4OGPo4erX6NvvZcwvZvz_s_Iaw0qnxOqNKrmMSa-B1Uz1VxbIH7O4m_2WfIBX9cd6-kvzA__DQjan8NKa9Bf0F653CE2ATnDJeCYizcwrJYOUkMC45k0eN3ke_W8KYL4YO1kUhZyu5zWWTN3yF23Vd1KyhzbpY9VtBG543RdFIzGlXCyHrXFSsbcq2rsuiXakto6yk5bqkVV6ui6zKK9E0gudNXTNW1KSgOHCls1S1daeV8n7C7bqglDYrzVvUPi27jAnNzYnku7gPndBErZSHldtG16d2Ovl4vJUP_hYsqKBxu4-OgIMKHu7YSSvXL1Zjnnql7jZqiBwaG-6M63o1Ob19t5-p0E9tJuxA2DHtFPO_p9HZryji0pugxmV4QXvest8DAAD__8Ffirw">