<table border="1" cellspacing="0" cellpadding="8">
    <tr>
        <th>Issue</th>
        <td>
            <a href=https://github.com/llvm/llvm-project/issues/57322>57322</a>
        </td>
    </tr>

    <tr>
        <th>Summary</th>
        <td>
            [Flang] performance regression on x86 after D126885
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
            llvm:SLPVectorizer,
            flang
      </td>
    </tr>

    <tr>
      <th>Assignees</th>
      <td>
      </td>
    </tr>

    <tr>
      <th>Reporter</th>
      <td>
          vzakhari
      </td>
    </tr>
</table>

<pre>
    After the cost model change in https://reviews.llvm.org/D126885 SLP stopped producing vector stores from separate scalar stores.  This causes about 9% regression in CU2000/168.wupwise@skylake with Flang.

Small reproducer:
```
subroutine sub()
  complex*16 I
  PARAMETER (I=(0.0D+0,1.0D+0))
  call bar(I)
end subroutine sub
```

[test.zip](https://github.com/llvm/llvm-project/files/9410301/test.zip) - this archive contains test.fir and test.ll produced by Flang.

Initialization of `I` is represented as:
```
  %1 = alloca { double, double }, i64 1, align 8, !dbg !7
  store { double, double } { double 0.000000e+00, double 1.000000e+00 }, ptr %1, align 8, !dbg !7
```

Command to reproduce the optimization pipeline: `clang++ -O3 -march=skylake -c -o test.o test.ll`

Both before and after D126885 `SROAPass` splits the structured store into two scalar stores:
```
*** IR Dump After SROAPass on sub_ ***
define void @sub_() !dbg !3 {
  %1 = alloca { double, double }, i64 1, align 8, !dbg !7
  %.fca.0.gep = getelementptr inbounds { double, double }, ptr %1, i32 0, i32 0, !dbg !7
  store double 0.000000e+00, ptr %.fca.0.gep, align 8, !dbg !7
  %.fca.1.gep = getelementptr inbounds { double, double }, ptr %1, i32 0, i32 1, !dbg !7
  store double 1.000000e+00, ptr %.fca.1.gep, align 8, !dbg !7
```

These two stores were combined into a vector store before D126885, but now they don't:
**Before:**

```
*** IR Dump After SLPVectorizerPass on sub_ ***
define void @sub_() local_unnamed_addr !dbg !3 {
  %1 = alloca { double, double }, align 8, !dbg !7
  store <2 x double> <double 0.000000e+00, double 1.000000e+00>, ptr %1, align 8, !dbg !7
  call void @bar_(ptr nonnull %1), !dbg !7
  ret void, !dbg !9
}
```

**After:**

```
*** IR Dump After SLPVectorizerPass on sub_ ***
define void @sub_() local_unnamed_addr !dbg !3 {
  %1 = alloca { double, double }, align 8, !dbg !7
  store double 0.000000e+00, ptr %1, align 8, !dbg !7
  %.fca.1.gep = getelementptr inbounds { double, double }, ptr %1, i64 0, i32 1, !dbg !7
  store double 1.000000e+00, ptr %.fca.1.gep, align 8, !dbg !7
  call void @bar_(ptr nonnull %1), !dbg !7
  ret void, !dbg !9
}
```

In `168.wupwise` multiple complex arguments are passed to `dcabs1` routine that computes `abs` for the real and imaginary components:
```
define double @dcabs1_(ptr %0) !dbg !3 {
  %2 = alloca double, i64 1, align 8, !dbg !7
  %3 = getelementptr [2 x double], ptr %0, i32 0, i64 0, !dbg !9
  %4 = load double, ptr %3, align 8, !dbg !9
  %5 = call double @llvm.fabs.f64(double %4), !dbg !9
  %6 = getelementptr [2 x double], ptr %0, i32 0, i64 1, !dbg !9
  %7 = load double, ptr %6, align 8, !dbg !9
  %8 = call double @llvm.fabs.f64(double %7), !dbg !9
  %9 = fadd double %5, %8, !dbg !9
```

This ends up being SLPd into:
```
*** IR Dump After SLPVectorizerPass on dcabs1_ ***
; Function Attrs: argmemonly mustprogress nofree nosync nounwind readonly willreturn
define double @dcabs1_(ptr nocapture readonly %0) local_unnamed_addr #0 !dbg !3 {
  %2 = load <2 x double>, ptr %0, align 8, !dbg !7
  %3 = call <2 x double> @llvm.fabs.v2f64(<2 x double> %2), !dbg !7
  %4 = extractelement <2 x double> %3, i32 0, !dbg !7
  %5 = extractelement <2 x double> %3, i32 1, !dbg !7
  %6 = fadd double %4, %5, !dbg !7
```

So we have two separate 8-byte stores followed by a 16-byte load, which is a store forwarding issue on some CPUs.

Given that SLP may vectorize the "use" part of the memory into a wider load, does it make sense to account for this in the cost model code computing the cost for replacing two scalar stores with one vector store?  For example, add assumed store-forwarding cost in case the address escapes or is already used by a vector loading operation.

I suppose Flang could also do something here, e.g. lower `fir.complex` stores/loads into vector stores/loads rather than structured ones.
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJzlWElv4zgW_jX2hYghUfF28MFJKoMA3eigqnquBUqibE5JpEBSdly_vr9HSV4SO-XMdE8fOlCsheRbv7eQqcl3i2XhpWV-LVlmnGeVyWXJsrXQK8mUZmvvazdIlgP-iMvKjZJbNyrLTTUydoVPDzGfzGZj9uWXZ-a8qWuZs9qavMmUXrGNzLyxNGClY4U1FXOyFlZ4yVwmStGPjRj7ulaOZaJxmClS03g2H_Axs3KFcaeMJnnuf-dRFIFvPJmNtk29VU4ObiP3fVeK75JtlV-zxxLSjwbRwyBatr9fKlGWoNQKJi0p1A5Pou4Kr65JLRgrDemadMBnAz5vRxjMU9WlfBnwZTxhT_3X5-Xn5a-fvn76zDD7aZA84BaNMMjvIOV9vH-cH5MiaVJhw5L-s9Q5e8X_nIjd7_jOS-dHP1Q9GBPPUz-tYIYmHUFkvJCzutsNDPAfuASvhSqlw31-G0dJFONpT5DP2Q0QAW8Im63VhqChvVDasTCnUJYJSBteoEln1ZyluzO2f9LKK1GqH8KTD03BoMsT_hkYkEukk9pjtXCX3MJg3HHMYF0Gy5lMsMH0juWmSUsJI3dP-PhAb2pyy2J6ANOVZjN6HPA4T1d0m_YkA-4uEjoaYPBn-JPkyehoXnwy0POvvQ3y_kyEs469N1UVbGsOcA3RaWqvqt6ItaplCZTAYGTNjIwOGXCxm98SdlOR42CuPixuMnZjWn-Z3m2vGN8ZRE4qCzIKCSBCXuijG5O_fP5t-SycI8-5ulTeBbmct03mGwsHthZVGrL7rTmN70uuRTi1F3v6zB6aqmZtQuq5MWiLUPjG9hPbdbksKEo2RuWM4h9T2nA9snNCTvwLEQSSoyITo2i0knWgvZJelrICngkESiOL6dy9y-sYLSrhLDp5uIjbi9Ds6B0Eu16P-C_RI75Gj_hdPeJr9DgbTl_XyC4tGtsStJWW0lmVAj15i1VxUqX6GOiQT6xSlCJttgT3HQTWAz71BzwHUN6FRSH_HjD6McD_8vzvIIb6Ie1_h3zCdfmt0VpUMv8m8tz-GcFwVRZN7jl76Ukkn-jDx5InFn0kd3Y1tDcCaikZgVZro3WDoZbK_MJqK31YfDrcVWJS_J3SG3wRnPaP9ffPss8Hkuefl3SQtP_vSedvxOGTpqp83AajMFdN6RXa1L5bRQe3asik1MtJVgNnMjQXmJxnInUxrep7Tr8WPqxs0CPQFEygceS2UOutFGVoDlQlVkoLuwuzjSYGlyp8h9_ecbcd395OsE_0ftnmx8g9IOH6Ip2cgRca6KOMNT5G02kF3sPqjXsC7dtAuzQiPxKtI5RclO6YwjhQCDA62ChssArYaVRMbmGpfgQM34DpmNjkf1U1vkx7-o6qk6tUnX1M1em7qs4DsQJpjx2WjNv54_NCXGgRsA-RlGmaGsWfdq3IzW1v8OGu9VxS7_D-Oq8Pkjv22OgsNPRL7y1FEMVrJSujyx1i2XnsAMLuF-mksFLi5nY6w63RW4VARETmYfJWlSWSSWP1VVGnEUs1de0HCn0gnq0qSfTTAA3AeN0IvMbalbEaIPK2qTgGy4a3cHk7CwJdTrj7kJUv3oqsj5MzzLr4fa8V30fvR4hdKk376H0F6dsO0uPru94vBn0uW4tN1_r2Jy6zm3RHBy_daYxBSt2223bB4kk7SG4kTtu1yta0RRdd0UQR2AqbU3wo5xoZ-hVTSXb__Ls72fH_S22kbosJnQpVYtd12IiKUEcGnDeoV5yjIFlPRwL0lXCPitL15FuVI6B6aXIDgZUHLexmndTU1GNWliEQfFefIKvSb06y8NuVNJJ8P0pLsMEuRTimerNdbQ-SDPVdR3uDQfLI2CPe5Iug8hoQndPJhUOR7TbAN0eGCqwgVCZcqzmFE4WzBLcabECLTFxSFO5Y43pvdFxJfaJjamnDzv_0ZAX9Yl0bkA6nLuDWlBCmdAb2Cr6BUfB9jT0PiSpHqxEjl1uq7YWyo_5Mi7b07TadPxJP13rh5PRuPwRJ1uHIUOjj3T-M5UZDuYgnk5jPJnE8GeaLJJ8nczH0ypdygUIUBEUNYlAIdqqEzuTxCR-ul9nk9PBh2Nhy8eHzrYBREno8TTgfrhecj7M4iuc5H0uEWFrweZwleTq-TUU0LYphKVJZOpISyAxEk-VJQsfnNgZ50R64cGgyVAsecR7NeIJ7PJ6Mouks43JczOI443k6RdqSlVDl_tR0aBdB2LRZOcppyvnDkeoQYEKOlMFcRF80fm3sYvNDfF8Lq4ZBsUXQ6g_Q2lbs">