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

    <tr>
        <th>Summary</th>
        <td>
            Inlining loses ABI-relevant target feature information at `call` operations
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
            new issue
      </td>
    </tr>

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

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

<pre>
    The behavior of the `call` terminator is (currently) very sensitive to surrounding target features: e.g. on x86-64, when an argument of type `<8 x float>` is passed, the exact way that argument will be passed depends on whether the function that contains the `call` has the AVX feature enabled.

This means that the seemingly harmless operation of moving `call` from one function to another (e.g. via inlining) can lead to the behavior of `call` changing, which is obviously bad! This is what happens in [this example](https://llvm.godbolt.org/z/EbTvPf7rj), where the uninlined program would behave as intended (caller and callee use matching ABI everywhere), but the program produced by `clang -emit-llvm` calls `no_target_feature` in a context where the AVX feature is available, thus breaking the call.

Before:
```llvm
; no target features
define internal void @no_target_feature_intermediate(float %dummy, ptr align 32 %x) unnamed_addr #0 {
start:
  %0 = load <8 x float>, ptr %x, align 32
  ; call uses "no-avx" ABI
  call void @no_target_feature(float %dummy, <8 x float> %0)
  ret void
}

; "target-features"="+avx"
define void @with_target_feature(ptr align 32 %x) unnamed_addr #2 {
start:
  %0 = alloca <8 x float>, align 32
 %1 = load <8 x float>, ptr %x, align 32
  store <8 x float> %1, ptr %0, align 32
  call void @no_target_feature_intermediate(float 0.000000e+00, ptr align 32 %0)
  ret void
}
```
After:
```llvm
; "target-features"="+avx"
define void @with_target_feature(ptr align 32 %x) unnamed_addr #1 {
start:
  %0 = alloca <8 x float>, align 32
 %1 = load <8 x float>, ptr %x, align 32
  store <8 x float> %1, ptr %0, align 32
  %2 = load <8 x float>, ptr %0, align 32
  ; call uses "avx" ABI
  call void @no_target_feature(float 0.000000e+00, <8 x float> %2)
  ret void
}
```

The obvious way to avoid this is to not do inlining when the target features differ between caller and callee, but that seems like a big hammer that leaves a lot of optimization potential on the table -- and indeed, if I understood correctly, the most recent attempt to enforce this for all kinds of inlining (including `alwaysinline`) [quickly led to problems](https://github.com/llvm/llvm-project/issues/67054).

An alternative would be to make `call` less context-dependent -- after all it's not the inlining itself that is the fundamental problem here, it's the fact that `call` doesn't know the ABI it has to use for the callee, and then takes the *caller* target flags to fill that information gap -- a fragile heuristic, as the inlining issue shows. So I wonder if it wouldn't be possible to in fact still do the inlining, but end up with code like
```llvm
; "target-features"="+avx"
define void @with_target_feature(ptr align 32 %x) unnamed_addr #1 {
start:
  %0 = alloca <8 x float>, align 32
  %1 = load <8 x float>, ptr %x, align 32
 store <8 x float> %1, ptr %0, align 32
  %2 = load <8 x float>, ptr %0, align 32
  call void @no_target_feature(float 0.000000e+00, <8 x float> %2) !abi-target-features=""
  ret void
}
```
IOW, the call terminator would stop relying on context clues (which are fragile since inlining changes the context), and instead it would carry an explicit annotation saying which target features should be considered when determining how arguments must be passed for this call. This approach would enable arbitrary inlining from "less feature" to "more feature" functions, provided the inliner takes care to equip the `call` instructions it is copying with the right annotation.

(I don't know the LLVM internal data structures so I'm not sure how that annotation would best be represented, but LLVM already supports tons of annotations at various instructions so I hope adding one more isn't too hard.)
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJzcWN-P47gN_muUFyJBIk9-PeRhsnMDTHFFi3Zx7duCtmhbN7Lkk-Rkcn99QSnOJNlpu7ctcMAtFrM7sSWSHz-SH4Mh6MYS7cRyL5ZPExxi6_zub2jqPw22mZROnXafW4KSWjxo58HVEFsCsZpXaIxYzSGS77TF6DzoAEJuqsF7stGchNzCgfwJAtmgoz4QRAdh8N4NVmnbQETfUISaMA6egigegWbNDJyFt81qunoQ8hMcW7KAFtA3Q0c2Jh9OfXJCFJ828Aa1cRhF8QP7owP0GAIpPsu-0htWEY54gthifL_mqI2Bks5vg6KerAps-9hSbMmn0_Vgq6idzYcrZyNqG-5BaDF_9PjTP8dogCyWhtRMzJ_E_DH__NzqAB1hugFjOhOIOm0bc4IWfWcoBHA9eUxWXQ2dOzBWV9Zq7zpw9to5B2hdclrITcLwoBG0Ndpq23AmKrRgCBW_G-9SenV31aJt0hFGXlctA-rKg3ZDMCcoUQm5gBSHDnDkIFrse7IBtAWx3Ed-RG_Y9YbE8knITRtjz7kV8lnIZ2MO3axxqnQmzpxvhHz-VcjnH8rPh7_Wa_-zkNtz1j0lRwebwiAFvXeNxw6ObjAqB0CAbDiSVaQS-9AY8oBWQfovwRAIOoxVyyg-7l-AmJTp_rOtcsiZGO_vvVNDRQrKU4LGoG1gSp2OU_Y-wYTGBH5o3ZfM4i_nvCcOWsDEFXqLV5Fcs0MHwANqwxzJTB0ClJ7wNRVGS8nEDXn2VDtPDGT-cDXPf5NP-aNiD9Z9VVbpmaJaW0pYeYsGDk4rEA9fB_AlvdKR0hhJyE0qLhByqYauO7GvffSARjcWCskP3phfg7XYkfqCSjEJizmI9T6bDhF9vPgNfGQOongC41DBfQ2fDeR7P10MXQ4X-wQN55X7jbRuioc3ISUnd3wrvfFvQ_wwqjs_kpdMkPONnmK68Iz0-uk6NeyUkDJbmV6Al1IUT_xT7rOLN6kY3Tvq2H7t4DeBLL8BZDTGVfgRzHfQCrlcfHdaQnSePsJwcXVy_tHJ_5yqj9k4n83THxJyP59_xMlvSd1YQPnXxzqS_2_V9XskefEHSLKQS_ktZj8-fF_y313vX_Pmg2Dkb6bOONxpnJRZbjjA5FI8T8vowLoIyl0GcxY33O3vejYoXdfkoaR4JLLw1Vh7n1sYk4QIYPQrAUKpG2ix65KAwchD_0ABEIxL2sn1UXf616wvehfJRo0G3OhHaQim02RKW0VZSOkaXmCwinyIzimonPdUJZGXVVbnQgRPFSsrjJG6PnK8ZGvnK8oQ1I7ZbuBVJ6FVv8Mg5EbbygzqrHTQHPEU8txnlOWWpcUvg65ezQkMJRXTe1ca6sJHKqPRsR3KWeW6s-Q4_zPtvfuZqijksw5h4PJ9Xq3nywchtzfD9tECmjQqk3AdJQfb7fD1RvwlyXae9tMsIxkFxpBbSgpZRyHXIaWf0boErmMgU-dE6TAqToWsUNGMIUIWK5_Ga9JrrGrTuStXlKNghVxHeLXumEXH_gV0zArVJTHEaRgFRiYS5zomJuIrncWtfMycE_LxQk6DTbqlZumcfeb8dplLDfYpaKg9NtoQtDR4HaKuko1wFznDD6F1xzCDvzt4gaNjfjHVdMyA51BYo7sQNBMzcu3k2ENkL5S7uXYsC7IKhh6460LlFKXi-GN39v-ptf9Onf3_3bZByAWWenqfyXMa5W9r6y9_-cfY3pKjV6tu7gchuh48mRMT2tmL5K_MkAbVJm9Q6OlSE0Hb6qoK0rp1Lrnz6fNSkvtviLyyjfUAFXp_4mWY3nqjKx0BrXUx11_AUx4pbPN-noR27GCVs0Er8qTy9FGU4-KzrTteFuQA3RDi1Yac-4YOeTPJOyD2vXdYtWf_8s4L6EsdPfrTe6BpZxVSpmZ5ya_kghZSdsy-q0_HxTYkAnl30LzeXQqdR1vqVBVDy2Pml0H390s5o-eHfA9DyJ67PmPEfYFf97ppr0G8mQFCbl5AubuG-uOPP_35fY1SGBGynYyzgxch111q9YE3vTadw5tUjeMk4-up9xTIxjxruX8lI2g8oTpBGPre-cit16a5-X5TAIxwQJ9Ex03A7Ai0ridApTI_eUqn1TNHFJ2DFr2aCbmdqF2htsUWJ7RbrLab5WJdFMtJu9tsi7LcbktVlFQsy4eieFBytVCrVVWW9WY90Ts5l8ViLjeLjZSL5UzRqio2D7RakCo21VY8zKlDbWZp83e-maTWv1vPl6tiYrAkE9IXUFJaOua5wKW6fJr4XZrZ5dAE8TA3OsTwfkvU0dDuZSSYcSwOH_cvU0-GDmjjXQ3czKrbsXn5viVMBm923y8lUkz_CgAA__9-HzRv">