<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/147456>147456</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
Missed optimization: adjacent stores to memory not merged
</td>
</tr>
<tr>
<th>Labels</th>
<td>
new issue
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
es1024
</td>
</tr>
</table>
<pre>
Example ([godbolt](https://godbolt.org/z/hc3csnvqo)):
```cc
#include <cstdint>
#include <cstddef>
template<size_t N>
struct Test {
uint16_t a;
uint8_t b;
uint8_t c;
uint32_t d;
uint64_t extra[N] = {};
};
template<size_t N>
Test<N> test(uint64_t x) {
Test<N> t;
t.a = static_cast<uint16_t>(x);
t.b = static_cast<uint8_t>(x >> 16);
t.c = static_cast<uint8_t>(x >> 24);
t.d = static_cast<uint32_t>(x >> 32);
return t;
}
template Test<1> test<1>(uint64_t);
template Test<2> test<2>(uint64_t);
```
For `test<2>` (24 byte case), assignment of a/b/c/d is done exactly as specified above (3 shifts, 4 truncates, 4 stores) when it could be reduced to just a single 64-bit store:
```llvm
define weak_odr dso_local void @Test<2ul> test<2ul>(unsigned long)(ptr dead_on_unwind noalias writable sret(%struct.Test.0) align 8 %0, i64 noundef %1) local_unnamed_addr #0 comdat !dbg !138 {
#dbg_value(i64 %1, !142, !DIExpression(), !144)
#dbg_declare(ptr %0, !143, !DIExpression(), !145)
#dbg_value(ptr %0, !146, !DIExpression(), !154)
%3 = getelementptr inbounds nuw i8, ptr %0, i64 8, !dbg !156
tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) %3, i8 0, i64 16, i1 false), !dbg !156
%4 = trunc i64 %1 to i16, !dbg !161
store i16 %4, ptr %0, align 8, !dbg !162
%5 = lshr i64 %1, 16, !dbg !167
%6 = trunc i64 %5 to i8, !dbg !168
%7 = getelementptr inbounds nuw i8, ptr %0, i64 2, !dbg !169
store i8 %6, ptr %7, align 2, !dbg !170
%8 = lshr i64 %1, 24, !dbg !172
%9 = trunc i64 %8 to i8, !dbg !173
%10 = getelementptr inbounds nuw i8, ptr %0, i64 3, !dbg !174
store i8 %9, ptr %10, align 1, !dbg !175
%11 = lshr i64 %1, 32, !dbg !177
%12 = trunc nuw i64 %11 to i32, !dbg !178
%13 = getelementptr inbounds nuw i8, ptr %0, i64 4, !dbg !179
store i32 %12, ptr %13, align 4, !dbg !180
ret void, !dbg !182
}
```
For `test<1>` (16 byte case), the assignments are combined as expected (but this case is a bit different since no store to memory is involved):
```llvm
define weak_odr dso_local { i64, i64 } @Test<1ul> test<1ul>(unsigned long)(i64 noundef %0) local_unnamed_addr #0 comdat !dbg !130 {
#dbg_value(i64 %0, !134, !DIExpression(), !136)
#dbg_value(i64 0, !135, !DIExpression(DW_OP_LLVM_fragment, 64, 64), !136)
#dbg_value(i64 %0, !135, !DIExpression(DW_OP_LLVM_fragment, 0, 16), !136)
#dbg_value(i64 %0, !135, !DIExpression(DW_OP_constu, 16, DW_OP_shr, DW_OP_LLVM_convert, 64, DW_ATE_unsigned, DW_OP_LLVM_convert, 8, DW_ATE_unsigned, DW_OP_stack_value, DW_OP_LLVM_fragment, 16, 8), !136)
#dbg_value(i64 %0, !135, !DIExpression(DW_OP_constu, 24, DW_OP_shr, DW_OP_LLVM_convert, 64, DW_ATE_unsigned, DW_OP_LLVM_convert, 8, DW_ATE_unsigned, DW_OP_stack_value, DW_OP_LLVM_fragment, 24, 8), !136)
#dbg_value(i64 %0, !135, !DIExpression(DW_OP_constu, 32, DW_OP_shr, DW_OP_stack_value, DW_OP_LLVM_fragment, 32, 32), !136)
%2 = insertvalue { i64, i64 } poison, i64 %0, 0, !dbg !137
%3 = insertvalue { i64, i64 } %2, i64 0, 1, !dbg !137
ret { i64, i64 } %3, !dbg !137
}
```
</pre>
<img width="1" height="1" alt="" src="http://email.email.llvm.org/o/eJzMWF2P6jgS_TXmpXRR4nzywAPdNNJKM7P7MNp5jBy7AN8bbNZ26O7761d2SAiB_ritXWkkJGK7TtU5ZZcrwKyVO4W4JNkDydYz1rq9Nku0cUTTWa3F6_LphR2ODQKhJckedlrUunEkWxNa7p07WpKsCN0QujkvzbXZEbr5SehmzxNu1ek_mtCF_yQrEq1IHnUfzv2IJlLxphUIJHnk1gmpHEme7i4J3J6XopXDw7FhDknyaOVPrBz80a1ZZ1ru4E-0DkjxQKIVtFK5OK8cMJKEiTBTVg7q6QQfTyS0ciC6mWEyTysH-OIMI9nDHyRbA0nWIVKxPpsOD-_Q9PxI8uhH4PwzLQfvL4Queu5wZXhm5-YsRLWOOckrzoJJL9MHoOVLyHjnw83rN-zLwRz8d_IEcX4Bujn_NI6mVwHFG0Cf0wkyoSOkQdca1Sv1qbzKY5-OeMhb9zxK3-BsiqEjDH0DM5zOLuxGGyB5NAblkS8FmkL96hA4sxhO9yN0tXRA5UBvgRG6qQndcEI3AqQFoRUCvjDumldgFuwRudxKFMBqfQr1lYDdy62z3lsKzrSKM4fnoXXa-OcFPO9RgXTAddsIqBEMipajAKfhe2sdMLBS7RqEPP1WS9dBJ9XXNKcDiVYCt1IhPCP7UWlhQFhdNZqzBk5aCiBp1CevbcbpCyOfQBUuEAGNVruQiPLoDAhkotKqatWzVAKUZo1kFp6NdKxuEKxBf-IJzbpynfso88irY43cKSiB0CzyymWegtKtErj1c7G3CQyrVil2QFExIQwQmkTA9UEwB4TGot75rzgph0ICbyPqXXViTYuElt5z5_Ex2Kb0_LT-x9PL0aC1UqvAcjGYhEN-5U0gb5jBs_CedTBOPvaX3fjr2d14yz_0lg3sCM2SUIA7dNigP5Ten1S1z6QF1T6DLD1uHMYnpDw76xOY5V1BM9kAZ83lXPgDND_gwaKbH6O5zNMz6X6zlFaqbZphQwUa3KJBxdGfAULLcNUEriF6CQOLOIiVMWxZMxTYLStCszTIDLUC_Yb6QpBxPsHkccCEYvDLAT1NwZnsFEr7cFkI19i9gfHxuQ1W9Ij8lmAWCN4EKXtI8bWto1OHi7HgUFH5CFdc9E6RRdRTKe_rpekUMWRocau3vKu3SHpIHH1NcDL1mN4IXoxw8WiH4yk0G8jE9yUnN0katjimI82B7hnXncRb5LDT8Rer9Cb9V1ud0I7VWHty0T4Fl1HfeUNxT1fppQ-_1xzjS3OM85vm6PY4apAWmEF_W9fS9w5mAV-OyB0KD69bB24vbcD71snAdzEht-H6cL67cQSlz3qdhgMetHn1tlKddHNCcfu--WHHI8UDhGusyzEp1qP-F1_1v_id_jdpWNEvNqzow4Y1tIQk_bAlJPm5JdxxdfGT3fez_qv657-q33779-_V1rCd3zlv2KUoTz8Z5orxr0WK-rv1fxyIa2Vde7m4u1m7N5dB4MK1OqEZiV7_Va3-fKr6XX_bvHzX2jrGf_Tcr1yM1XfcPrubX1FP07-v-o7b_1N9dy_fUf85gh28-_EyZej5dE1BKovGBV_3LpijltaTe4SximhyByfF9VvdB0598H7cldB9f_7Gvw-fdtYAmDSBmVgmYpEs2AyXcZHFizRPomK2X7KIblOxyAuab_OUZ9k2XZTIOI3FNsY8n8kljWgWFVEZJWkWFfNI0CyPkbKsXERxKUga4YHJZh5eMrXZzaS1LS7jtEizfNawGhsb_rOgVOEzhFVCKcnWM7P0oG91u7P-LVVaZy9unHQNLn-X1qIAfXTyIH8y549HsgImvjMe-kv4qTXqK0o7OKDZoZi1pllO_veQbt_Wc64PhG5Ck-m-vh2N_o7cEboJ_Cyhm7OA05L-NwAA__-OAu5S">