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

    <tr>
        <th>Summary</th>
        <td>
            Scalar matrix multiply code can't recover from <float x 2> ABI restriction
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
            missed-optimization
      </td>
    </tr>

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

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

<pre>
    https://godbolt.org/z/19564zseG

Starting from generic C++ based vector4/matrix4 style types:
```c
struct Vector4 {
  float x, y, z, w;
};
struct Matrix4 {
    Vector4 mCol0, mCol1, mCol2, mCol3;
};
```
Passing Vector4 args by value results in them being converted to { <2 x float>, <2 x float> } types for the x64 ABI, and we never manage to recover from this, even if all calls are eventually inlined away. The matrix multiply code in the compiler explorer link above results in the following IR:
```ll
define void @mulme(ptr nocapture noundef nonnull readonly align 4 dereferenceable(64) %a, ptr nocapture noundef nonnull readonly align 4 dereferenceable(64) %b, ptr nocapture noundef nonnull writeonly align 4 dereferenceable(64) %c) {
entry:
 %aa.sroa.5.0.a.sroa_idx = getelementptr inbounds i8, ptr %a, i64 8
 %aa.sroa.7.0.a.sroa_idx = getelementptr inbounds i8, ptr %a, i64 16
 %aa.sroa.9.0.a.sroa_idx = getelementptr inbounds i8, ptr %a, i64 24
 %aa.sroa.11.0.a.sroa_idx = getelementptr inbounds i8, ptr %a, i64 32
 %aa.sroa.13.0.a.sroa_idx = getelementptr inbounds i8, ptr %a, i64 40
 %aa.sroa.15.0.a.sroa_idx = getelementptr inbounds i8, ptr %a, i64 48
 %aa.sroa.17.0.a.sroa_idx = getelementptr inbounds i8, ptr %a, i64 56
 %bb.sroa.0.0.copyload = load <2 x float>, ptr %b, align 4
 %bb.sroa.4.0.b.sroa_idx = getelementptr inbounds i8, ptr %b, i64 8
 %bb.sroa.4.0.copyload = load <2 x float>, ptr %bb.sroa.4.0.b.sroa_idx, align 4
  %bb.sroa.5.0.b.sroa_idx = getelementptr inbounds i8, ptr %b, i64 16
  %bb.sroa.5.0.copyload = load <2 x float>, ptr %bb.sroa.5.0.b.sroa_idx, align 4
  %bb.sroa.6.0.b.sroa_idx = getelementptr inbounds i8, ptr %b, i64 24
  %bb.sroa.6.0.copyload = load <2 x float>, ptr %bb.sroa.6.0.b.sroa_idx, align 4
  %bb.sroa.7.0.b.sroa_idx = getelementptr inbounds i8, ptr %b, i64 32
  %bb.sroa.7.0.copyload = load <2 x float>, ptr %bb.sroa.7.0.b.sroa_idx, align 4
 %bb.sroa.8.0.b.sroa_idx = getelementptr inbounds i8, ptr %b, i64 40
 %bb.sroa.8.0.copyload = load <2 x float>, ptr %bb.sroa.8.0.b.sroa_idx, align 4
  %bb.sroa.9.0.b.sroa_idx = getelementptr inbounds i8, ptr %b, i64 48
  %bb.sroa.9.0.copyload = load <2 x float>, ptr %bb.sroa.9.0.b.sroa_idx, align 4
  %bb.sroa.10.0.b.sroa_idx = getelementptr inbounds i8, ptr %b, i64 56
  %bb.sroa.10.0.copyload = load <2 x float>, ptr %bb.sroa.10.0.b.sroa_idx, align 4
  %0 = load <2 x float>, ptr %a, align 4
  %1 = load <2 x float>, ptr %aa.sroa.7.0.a.sroa_idx, align 4
  %2 = load <2 x float>, ptr %aa.sroa.11.0.a.sroa_idx, align 4
  %3 = load <2 x float>, ptr %aa.sroa.15.0.a.sroa_idx, align 4
  %4 = shufflevector <2 x float> %bb.sroa.0.0.copyload, <2 x float> poison, <2 x i32> zeroinitializer
  %5 = fmul fast <2 x float> %4, %0
  %6 = shufflevector <2 x float> %bb.sroa.0.0.copyload, <2 x float> undef, <2 x i32> <i32 1, i32 1>
  %7 = fmul fast <2 x float> %6, %1
  %8 = fadd fast <2 x float> %7, %5
  %9 = shufflevector <2 x float> %bb.sroa.4.0.copyload, <2 x float> poison, <2 x i32> zeroinitializer
  %10 = fmul fast <2 x float> %9, %2
  %11 = fadd fast <2 x float> %8, %10
  %12 = shufflevector <2 x float> %bb.sroa.4.0.copyload, <2 x float> undef, <2 x i32> <i32 1, i32 1>
  %13 = fmul fast <2 x float> %12, %3
  %14 = fadd fast <2 x float> %11, %13
 %15 = shufflevector <2 x float> %bb.sroa.5.0.copyload, <2 x float> poison, <2 x i32> zeroinitializer
  %16 = fmul fast <2 x float> %15, %0
  %17 = shufflevector <2 x float> %bb.sroa.5.0.copyload, <2 x float> undef, <2 x i32> <i32 1, i32 1>
  %18 = fmul fast <2 x float> %17, %1
  %19 = fadd fast <2 x float> %18, %16
  %20 = shufflevector <2 x float> %bb.sroa.6.0.copyload, <2 x float> poison, <2 x i32> zeroinitializer
  %21 = fmul fast <2 x float> %20, %2
  %22 = fadd fast <2 x float> %19, %21
 %23 = shufflevector <2 x float> %bb.sroa.6.0.copyload, <2 x float> undef, <2 x i32> <i32 1, i32 1>
  %24 = fmul fast <2 x float> %23, %3
  %25 = fadd fast <2 x float> %22, %24
  %26 = shufflevector <2 x float> %bb.sroa.7.0.copyload, <2 x float> poison, <2 x i32> zeroinitializer
  %27 = fmul fast <2 x float> %26, %0
  %28 = shufflevector <2 x float> %bb.sroa.7.0.copyload, <2 x float> undef, <2 x i32> <i32 1, i32 1>
  %29 = fmul fast <2 x float> %28, %1
  %30 = fadd fast <2 x float> %29, %27
 %31 = shufflevector <2 x float> %bb.sroa.8.0.copyload, <2 x float> poison, <2 x i32> zeroinitializer
  %32 = fmul fast <2 x float> %31, %2
  %33 = fadd fast <2 x float> %30, %32
  %34 = shufflevector <2 x float> %bb.sroa.8.0.copyload, <2 x float> undef, <2 x i32> <i32 1, i32 1>
  %35 = fmul fast <2 x float> %34, %3
  %36 = fadd fast <2 x float> %33, %35
 %37 = shufflevector <2 x float> %bb.sroa.9.0.copyload, <2 x float> poison, <2 x i32> zeroinitializer
  %38 = fmul fast <2 x float> %37, %0
  %39 = shufflevector <2 x float> %bb.sroa.9.0.copyload, <2 x float> undef, <2 x i32> <i32 1, i32 1>
  %40 = fmul fast <2 x float> %39, %1
  %41 = fadd fast <2 x float> %40, %38
  %42 = shufflevector <2 x float> %bb.sroa.10.0.copyload, <2 x float> poison, <2 x i32> zeroinitializer
 %43 = fmul fast <2 x float> %42, %2
  %44 = fadd fast <2 x float> %41, %43
  %45 = shufflevector <2 x float> %bb.sroa.10.0.copyload, <2 x float> undef, <2 x i32> <i32 1, i32 1>
  %46 = fmul fast <2 x float> %45, %3
 %47 = fadd fast <2 x float> %44, %46
  %48 = load <2 x float>, ptr %aa.sroa.5.0.a.sroa_idx, align 4
  %49 = load <2 x float>, ptr %aa.sroa.9.0.a.sroa_idx, align 4
  %50 = load <2 x float>, ptr %aa.sroa.13.0.a.sroa_idx, align 4
  %51 = load <2 x float>, ptr %aa.sroa.17.0.a.sroa_idx, align 4
  %52 = fmul fast <2 x float> %4, %48
  %53 = fmul fast <2 x float> %6, %49
  %54 = fadd fast <2 x float> %53, %52
  %55 = fmul fast <2 x float> %9, %50
  %56 = fadd fast <2 x float> %54, %55
  %57 = fmul fast <2 x float> %12, %51
  %58 = fadd fast <2 x float> %56, %57
  %59 = fmul fast <2 x float> %15, %48
  %60 = fmul fast <2 x float> %17, %49
  %61 = fadd fast <2 x float> %60, %59
  %62 = fmul fast <2 x float> %20, %50
  %63 = fadd fast <2 x float> %61, %62
  %64 = fmul fast <2 x float> %23, %51
  %65 = fadd fast <2 x float> %63, %64
  %66 = fmul fast <2 x float> %26, %48
  %67 = fmul fast <2 x float> %28, %49
  %68 = fadd fast <2 x float> %67, %66
  %69 = fmul fast <2 x float> %31, %50
  %70 = fadd fast <2 x float> %68, %69
  %71 = fmul fast <2 x float> %34, %51
  %72 = fadd fast <2 x float> %70, %71
  %73 = fmul fast <2 x float> %37, %48
  %74 = fmul fast <2 x float> %39, %49
  %75 = fadd fast <2 x float> %74, %73
  %76 = fmul fast <2 x float> %42, %50
  %77 = fadd fast <2 x float> %75, %76
  %78 = fmul fast <2 x float> %45, %51
  %79 = fadd fast <2 x float> %77, %78
  store <2 x float> %14, ptr %c, align 4
 %ref.tmp.sroa.4.0.c.sroa_idx = getelementptr inbounds i8, ptr %c, i64 8
 store <2 x float> %58, ptr %ref.tmp.sroa.4.0.c.sroa_idx, align 4
 %ref.tmp.sroa.5.0.c.sroa_idx = getelementptr inbounds i8, ptr %c, i64 16
 store <2 x float> %25, ptr %ref.tmp.sroa.5.0.c.sroa_idx, align 4
 %ref.tmp.sroa.6.0.c.sroa_idx = getelementptr inbounds i8, ptr %c, i64 24
 store <2 x float> %65, ptr %ref.tmp.sroa.6.0.c.sroa_idx, align 4
 %ref.tmp.sroa.7.0.c.sroa_idx = getelementptr inbounds i8, ptr %c, i64 32
 store <2 x float> %36, ptr %ref.tmp.sroa.7.0.c.sroa_idx, align 4
 %ref.tmp.sroa.8.0.c.sroa_idx = getelementptr inbounds i8, ptr %c, i64 40
 store <2 x float> %72, ptr %ref.tmp.sroa.8.0.c.sroa_idx, align 4
 %ref.tmp.sroa.9.0.c.sroa_idx = getelementptr inbounds i8, ptr %c, i64 48
 store <2 x float> %47, ptr %ref.tmp.sroa.9.0.c.sroa_idx, align 4
 %ref.tmp.sroa.10.0.c.sroa_idx = getelementptr inbounds i8, ptr %c, i64 56
 store <2 x float> %79, ptr %ref.tmp.sroa.10.0.c.sroa_idx, align 4
  ret void
}
```
While GCC manages to load/store and process the vectors and matrices in whole xmm/ymm/zmm, LLVM ends up with float2 sub-vectors and relies on the DAG to load/store combine them back together as best it can.
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJysWk1v4zgS_TXKpdCGxE_74EMnmTQaOwMsphezxwUllW3uUKJB0UmcX7-QZClyLK8p20DDkdXi0yu-KlYVTVVVel0iLiP-GPHnB7XzG-uWf_7jly5s-ZDafL_ceL-tIvo9Ii8ReVnbPLXGz6xbR-TlIyIvyYIL9lHhjyh-juLv7ecvr5zX5RpWzhawxhKdzuApIo8ReYRUVZjDK2beOhaRl0J5p98ZVH5vEPx-i80LWzwRt_-y9nvl3S7z8Fc7GCL52N4HWBmrPLxH5An29cdH_fEW0cMDkXzurw8gfxzeOwCBHrl4siauIeqLpLsg3QUdBe7Ztl__WU9wue4xlVtXkO7hVZkdgsNqZ3wFugS_wQJSrJ_NbPmKzmMO3tbMIKJPBN5b8yL6W03gyy2I5HM7bbCyrgaDd8Hg--PP-mFV5vCGUOIrOihUqdZYQzvMbH2nUchvdFU_i69Ygl6BMgYyZUwFymFz1--UMXvQpdEl5qDe1H4G_9ogtOJBsTNeb80eMpvjwSTIbLHVBh3g-9ZYhw6MLv8GldrXr-bDyhpj3-oZ-PnnqfrGtDdyXOkS4dXqHCIWFztTYETmW--gtJna-p1DKO2uzHEFpS3LnTHgUOW2NHtQRq9LYJCjwxU6LDNUqakBBIvIAiLCVT0N94NLL8O9Oe0xFC9r_nb-iqV3-36yGvpqVjmrZnwWz9rL_-j8HSL6DGv0aLDA0td8dJnWPCrQ845iZ70WDOankPJGyEScYi5uxCTsFDNJbgSlZASU3gjK4hHQW1ViIzIlt-rEBzqlaQsaz-JZZrd7Y1XeIB4uTlamA1jj9gd3PkVjs3iWTmeYjjjnEHISwVEqp6yHz_IbafcB8BX0GuJ8AnFxI3FyDvQa4mICcXkj8T6Sv4JeQ1xeID54dH4j7-FiMcS8hvZ8wnwvbuU9PwN6DfHFBOJJfCNzPhabyeRlb5zOOPc4BFGNj02Cxo4m7nFAMgXwS4YdR6STEHkAImsQq81utTLYdg-nlfB4zhormrdWV7Yc_I-mpL7_gc7qUnutjP5ANyDAGwKrYmdgpSo_9nLW4BEeD4aJ-_JuqsdT2hF90pRA06m0F_S3AQl5mbs4cE8Gw-btMJXnZ4fJwzA-GLaYZjK7u1RJfNnexYH4MEkkyWWD5908DUVOyH1Nvk7lhF42OyEH_nQ4kF22O0k6w-lnekr4NLv5_aUWATbzkbhM5H2pXynZPIC-HAnNZBEgWe-rw_xG4mmGi7trRpLLRpN4JD4JCTC6D-zk008Jva_N14lNWIDddCQ-Cb9sN-kC-6heJxPTj7y_2AG5h4iRACXz-1K_UrNFAP35SIDSOECz3lflp6_SZJrd87tLRsllm2kyEp-UXraZdoF91J7RieXdJaOvE5sG1HiUjQQoFQGG95HNB2JPzEKL-4sdkIGoHIlPOrHMu0T9OslYQKlHFyPxyQJqPdb76rC1ZROLveS-vUjNIKDQY2QkQFlAoce6yGZDD2cTK72LRl8pd0C5x_iXCK3vyQDDu8hmw3KJzae0skGd7GIK4iIAMXA3YXw3-wzkpE2GJGSXgQeklV6CYcDxAHfvKgi2GA4McHfeLctHgRLS7XerytGqyAMyQW_lUdcc0qT33dvRUhbSp_cTxOVwZEB103dPR5qIgHW3b1yORBEBC6_oFl5-NDLAgfq-4XgTJqAwEd26J4aOIKbU7EeqiICiXXQjxTBQRMAi15fMx6qEFNvzUVUCfEh0eorhAikCfKivFo9UkQElsujYiiFbGdBB9oXakSoyoIWUnQ_Jo5EBq1BfKB2pIgN8qK9RjlSRAT4kOzvlMF_LkERJRlUJSJWyWxPk0BNkQCnZJ-djVQJ2M2Q3t7Kf28pbh6MrDxskqWz01xqHq5kvtoP9uOk_IGTRl98kzxLiw2H_59WXqfIbqfZ7QWe5En6OK5_IVdzItd_KOMtVnOUqJnKVN3Lt29mzXKk4x1VO5Dq_kWv_C-NZrpKc4zqfyHVxK9eLscXkOa6LiVzbpuUGsvxicMnFObJfXj5SQzv0zdmnz5NnY8fO_r3RBuHH09PhuFcF3kLbhb20tFSZw9bZDKuqOXzVdnNVc7850JVhczDrbWMNwntRRORl33x-1J9P8Pvvf_0BWE_Dbgtv2m9aEwlUu_TbEM2h0ViBbQ95PX__ccIls0WqSzycgVPZ3-DtGv0GHagKUqw8aA-ZKmcP-ZLmC7pQD7hMhOSEU8LEw2aZz0kWZ1xggjHlqWRIWSJWEhVSxlf8QS9JTGhM4iQhsSRilrIkp_M0mzOepfXazGIslDYzY16LmXXrB11VO1yKmLHkwagUTdWckiSk0FWF-Te79brQH8rrunEnEX9-cMt69Ld0t64iFhtd-eoTz2tvcPkrU0a58UNzmSojIv3xybyIPh0ONkLTHX9__AkOq1qi-s0PO2e-HtHUfrNLZ5mt5apff_jzbevsfzHzEXlpbKsi8tKY978AAAD__zER6u4">