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

    <tr>
        <th>Summary</th>
        <td>
            Wrong floating point promotion during type legalisation
        </td>
    </tr>

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

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

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

<pre>
    ```c++
#include <stdio.h>

__attribute__((noinline))
float f(_Float16 a, _Float16 b) {
  a += b;
  return a;
}

int main(void) {
 printf("%f\n", f(32, 65504));
}
```
`float` is capable of representing 65536, but `_Float16` is not, and the assignment to `a` means that holding the value in a larger type is not permitted: casts and assignments are supposed to discard excess precision and range. This is supposed to print `inf` on all targets where `_Float16` is supported.

However, on ARM (testing on QEmu), I am seeing:

```sh
$ bin/clang -O0 --target=armv7a-linux-gnueabihf test.c && qemu-arm -L /usr/arm-linux-gnueabihf a.out
inf
$ bin/clang -O1 --target=armv7a-linux-gnueabihf test.c && qemu-arm -L /usr/arm-linux-gnueabihf a.out
65536.000000
```
Same as `-O1` for other optimisation levels too.

Clang initially generates:

```llvm
; Function Attrs: noinline nounwind
define dso_local float @f(half noundef %a, half noundef %b) #0 {
entry:
 %a.addr = alloca half, align 2
  %b.addr = alloca half, align 2
  store half %a, ptr %a.addr, align 2, !tbaa !5
  store half %b, ptr %b.addr, align 2, !tbaa !5
  %0 = load half, ptr %b.addr, align 2, !tbaa !5
 %ext = fpext half %0 to float
  %1 = load half, ptr %a.addr, align 2, !tbaa !5
  %conv = fpext half %1 to float
  %add = fadd float %conv, %ext
  %conv1 = fptrunc float %add to half
  store half %conv1, ptr %a.addr, align 2, !tbaa !5
  %2 = load half, ptr %a.addr, align 2, !tbaa !5
  %conv2 = fpext half %2 to float
  ret float %conv2
}
```
That is, it performs the addition in `float`, and converts the result to `half`. This is a perfectly valid way of doing this.

InstCombine then transforms that as

```diff
-*** IR Dump Before InstCombinePass on f ***
+*** IR Dump After InstCombinePass on f ***
 ; Function Attrs: noinline nounwind
 define dso_local float @f(half noundef %a, half noundef %b) local_unnamed_addr #0 {
 entry:
- %ext = fpext half %b to float
-  %conv = fpext half %a to float
-  %add = fadd float %conv, %ext
-  %conv1 = fptrunc float %add to half
+ %conv1 = fadd half %a, %b
   %conv2 = fpext half %conv1 to float
   ret float %conv2
 }
```
Turning it into an `fadd` on type `half` directly. This is not necessarily a useful transformation when `fadd half` is not a legal operation, but it is a valid transformation that preserves the behaviour.

During type legalisation, though, because of it not being a legal operation, it is again converted back to an `fadd` on type `float`:

```diff
@@ -1,13 +1,12 @@
   t0: ch,glue = EntryToken
             t2: f32,ch = CopyFromReg t0, Register:f32 %0
 t5: i32 = bitcast t2
-          t6: i16 = truncate t5
- t7: f16 = bitcast t6
+        t18: i32 = and t5, Constant:i32<65535>
+ t16: f32 = fp16_to_fp t18
             t4: f32,ch = CopyFromReg t0, Register:f32 %1
           t8: i32 = bitcast t4
-          t9: i16 = truncate t8
-        t10: f16 = bitcast t9
-      t11: f16 = fadd t7, t10
-    t12: f32 = fp_extend t11
-  t14: ch,glue = CopyToReg t0, Register:f32 $s0, t12
+        t21: i32 = and t8, Constant:i32<65535>
+ t19: f32 = fp16_to_fp t21
+    t20: f32 = fadd t16, t19
+  t14: ch,glue = CopyToReg t0, Register:f32 $s0, t20
   t15: ch = ARMISD::RET_GLUE t14, Register:f32 $s0, t14:1
```

However, in this promotion back to `float`, the truncation is lost, hence the changed result. There seem to be no relevant flags (beyond general "don't optimise") to control this.

I initially thought this was another instance of #44218 but this seems like a completely different part of the compiler where things go wrong. But attempts to fix #44218 may, depending on how they are done, result in this issue.
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJy8WN1v47gR_2uYl4ENifLnQx4SZ90ucIdrc1v00aCkkcRbilTJkbP-74uh5K_ECbLXQw0hkeWZ3wzne6RC0LVFvBfzRzF_ulM9Nc7fN_tS__H9Lnfl4V4skuEqhHzkK3kSyYOQmbaF6UsEkW0CldpNG5F9GX-Nf3c7ReR13hPudkKuhFxZp63RFoVc8xXJKuMUQSXkarfl23QBSsgNnL7lQq5BLEfJAApYjewJcpGdHnqk3ltQp0di-XSpjLYErdJWyNXe6fIasvPaUhVVlELOKzHf2Hi7iXpl8W4xnyezUfG3Qo5WOn2NxxKLBHSAQnUqNwiuAo-dx4CWtK0ZMlswdt4TiEVyPPLIZh3xj8qWQA3C4KwWLQE5JldM16KyAahRBI0zJcMy8V6ZHkFbUGCUr9EDHTocUaFD32oiLEX2AIUKFKKUs4QAyiOEvutcwJLllToUypeAPwoMATqPhQ7a2cjola1xCt8aHVjEJV-0LWurbcX6MocxQKwUBXhp0OONs0cIT1hOL734d_eCe_RsFWfh4flXEHJFGKI1nYV_fmn76KINfAXVQkDUthbZwyXIyVehOQbzDHIOjW1hlK1h8lsCk8mgocielG_3SzUx2vY_JrXtUeW6qYDFTgsQciHkAv6DbT9RvoXJLyDktg9eyK3y7Rs2NXU9HWOyek-B9P-iQIy_aRI_N-P4d9Vy3LF_Jr-l7JrKeXDUoAfXkW51UMRBYHCPJgA5d-WvTTyOtpq0MuYANVr0ijC85xJj9u34KHuEbW-LCP9A5JkHjgUErOvti7blQFxixQ_L4HbGFcrAUFTELOH8bZSpIkOJFQg5j-Xl9cOhysgsOdcFtOQPJ00j51SVpQcuPsqwpAgTk9To2oI8liMG_CxtIOdx0OeoXEf-LO6SRW5AyJRyxUUwnd-GyC8g8s9CCDlPorLGqfKk6k-iCDnHHxRhqo7vjiolXAqGkniWl74r79MHF3JeOLu_ITG9JVGV5UDKN2OMDAiDANb-FXQ6YpPvbXHmYQByg943nRB5_-SR5F9mGHnDMvKNZTzStTXkx-3tG3cbHVi2js2kcr4NQ5MqSx1TVlu46ILHPsbg6Gmg9Rh6c2xm8aCL5NxEVATGgsyBu5ku4UUduIWWbmhyOlzVmq820Ma1ORcCatACeWXDUTNFoMLNklPqanThRMiH4YKvz_DUtx08YsVOvcD-hwqBWw2bcqQ-FvHHt_wPFaH_FDv8VMGDv6riReZdb61qsdwNFeuqCMJ1FZy8n-P5q7iafJSe6ibx5_Nz8tMJKuTjKxYmuSq70ShjTnyUQQPG6yx6N43g3TzqveVY1gTakgM15Iwqy3FSijPbOTmg1D4mxDlLeJqzyCOZ8tocQEEfsOrNOfqHBv3CGTGCwxFu5FdgsFYGXMfNWTt7HElZL07EIf1eIcacisOs3-OQ0Dk2aq9d768S86n3MWH5LFHQODSwFGpcXzdRHhaqD3FG1hTVynl2u63cqFittD2WFCwhV8V3-MiMp2r0zvRxLgVilohZAhOu4GnGC0e8kzD8cHI5JXGC5hPUPHBzrHzhhPnmvqM9kZ0_JJmhiitF0UT6jesOW-_aZ6wZT27gGWsdCL3IHqpMxv45QtGc2XU2RGWuiYd3Bj0mxUnOIhKmi0gYc0MRMv9IScuoyEhwQlqck-WIlK4uZcZtZM5abpwNpCyJ7EFnUmQbHifn5yVQPgLP88NxxyxKFztyu6qLqDesM_tT1knfQNHqpp1mb-20vm2n1StKSpOb9lpf0VGaXlLFZKNlDPQ0uaCkVF7bZYc_CNmyaXoio3T2NrjYHN_cB8aYhWSQJ996UqZvPLn6tCfX73lSpleSSCZXlNEG6WJQan1B-r-dT14kYTofgCLCw_OvX39_4hTPHp6_fNv97Zd_fYmyPrQWq5Le3uVfb57axuEDOu9aF0vhsfC8Gni4JI4RFSeiAMaFuNI3aIs4pUDR8OpcjrMQ13XehwNiy4A5933waHCvLDcXVQdeeXM8OFuO65QBIWXJhXFJx60M4-uLNWMUzpJ35sbAdLGYDXWYhoO9qADKDluejpFRxLosZDabyXQVe0OkZD0DGP0dQUHh2s4goTkAF1L0aAk65Yl541ld22mDflz6qdG2DlA7ePHO1lN47AkUEbYdD4gOKv3jLLNVB7ZciR3aclz3G_fCwIf4sqJ0FplinCqPXtIh9Di9K--zcp2t1R3ep8sklct0nazumvtstZTprEyWy3SdlRXms6xK0jTN5XyWrZf5nb6XiczSVK7TeZbOVtNkXqpqVmS4SjFdpQsxS7BV2kx5d506X99FkffLbJXM74zK0YT4fk1Kiy-DPuyd-dOdv2eeSd7XQcwSowOFMwppMnj_bzbNMFXwoTun2ainyCvf6a13vTf3DVEXN225FXJba2r6fFq4Vsht3LOHf5POuz-wICG3Ubcg5Dbq_t8AAAD__we78cQ">