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

    <tr>
        <th>Summary</th>
        <td>
            Missing fold of subtract and comparison by using sign flag on ARM64 and x86-64
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
      </td>
    </tr>

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

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

<pre>
    Full example:
https://cpp.compiler-explorer.com/z/GaPEch7dj

For the code in the example, LLVM generates the following ARM64 assembly:
```
make_id(T*, long):
        ldr     w9, [x0, x1, lsl #2]
        mov     w8, #-2097152
        asr     w9, w9, #9
 sub     w8, w8, w9
        cmn     w9, #512, lsl #12
        csel w0, w9, w8, gt
        ret
```

While I would expect, and GCC generates, something that reuses the `sub`:
```
make_id(T*, long):
 ldr     w1, [x0, x1, lsl 2]
        mov     w2, -2097152
 asr     w1, w1, 9
        subs    w0, w2, w1
        csel    w0, w0, w1, pl
        ret
```

The same failure also occurs on x86-64, with LLVM generating:
```
make_id(T*, long):
        mov     ecx, dword ptr [rdi + 4*rsi]
        sar     ecx, 9
        mov     eax, -2097152
        sub     eax, ecx
        cmp     ecx, -2097151
 cmovge  eax, ecx
        ret
```

While I would expect, and GCC generates:
```
make_id(T*, long):
        mov     edx, DWORD PTR [rdi+rsi*4]
        mov     eax, -2097152
        sar     edx, 9
 sub     eax, edx
        cmovs   eax, edx
 ret
```

The LLVM IR for x86-64 (should be the same for ARM64):
```llvm
%struct.T = type { i32 }

define dso_local noundef range(i32 -2097151, 4194304) i32 @make_id(T*, long)(ptr nocapture noundef readonly %data, i64 noundef %i) local_unnamed_addr {
entry:
 %arrayidx = getelementptr inbounds %struct.T, ptr %data, i64 %i
  %bf.load = load i32, ptr %arrayidx, align 4
  %shr = ashr i32 %bf.load, 9
  %cmp = icmp sgt i32 %shr, -2097152
  %sub = sub nuw nsw i32 -2097152, %shr
 %retval.0 = select i1 %cmp, i32 %shr, i32 %sub
  ret i32 %retval.0
}
```

Note that the `nuw` and `nsw` flags are inferred here due to the `ashr`.

The original C++ code in question:
```cpp
#include <assert.h>
#include <stdint.h>
#include <sys/types.h>

enum E : uint8_t {
  V1,
  V2,
};

struct T {
  E e : sizeof(E) * 8;
  bool b : 1;
  unsigned payload : 23;
};

constexpr int32_t InvalidId = 0b1111'1111'1110'0000'0000'0000'0000'0000;

int32_t make_id(T* data, ssize_t i) {
  uint32_t payload = data[i].payload;
  constexpr int shift = 32 - 23;
  int32_t ext_id = static_cast<int32_t>(payload << shift) >> shift;
  int32_t index = static_cast<uint32_t>(InvalidId) - static_cast<uint32_t>(ext_id);
  if (index < 0) {
    assert(ext_id > InvalidId);
    return ext_id;
  }
  return index;
}
```

---

An attempt at simplifying the test case produces somewhat bizarre results. Not sure what to make of these.

https://cpp.compiler-explorer.com/z/PGYb7vsGs

Here the C++ code is:
```cpp
#include <assert.h>
#include <stdint.h>
#include <sys/types.h>

constexpr int32_t InvalidId = 0b1111'1111'1110'0000'0000'0000'0000'0000;

int32_t make_id(int32_t* data, ssize_t i) {
  int32_t ext_id = data[i];
#ifdef __clang__
 __builtin_assume(ext_id <= 0b0000'0000'0011'1111'1111'1111'1111'1111);
#endif
  int32_t index = static_cast<uint32_t>(InvalidId) - static_cast<uint32_t>(ext_id);
  if (index < 0) {
    assert(ext_id > InvalidId);
    return ext_id;
 }
  return index;
}
```

And the LLVM IR (for x86-64) is:
```llvm
define dso_local noundef range(i32 -2097151, 2145386497) i32 @make_id(int*, long)(ptr nocapture noundef readonly %data, i64 noundef %i) local_unnamed_addr {
entry:
  %arrayidx = getelementptr inbounds i32, ptr %data, i64 %i
  %0 = load i32, ptr %arrayidx, align 4
  %cmp = icmp slt i32 %0, 4194304
  tail call void @llvm.assume(i1 %cmp)
  %cmp1 = icmp sgt i32 %0, -2097152
  %sub = sub nuw nsw i32 -2097152, %0
 %retval.0 = select i1 %cmp1, i32 %0, i32 %sub
  ret i32 %retval.0
}

declare void @llvm.assume(i1 noundef) #1
```

Without the `assume`, LLVM thinks that the `assert` above can fire... Which I think is correct? But weirdly, GCC deletes the assert...

But even setting that aside, I'm a bit surprised at the assume being enough for LLVM to infer both `nuw` and `nsw` on the `sub` here. Because `INT_MIN` seems like it isn't precluded by the assume and yet I feel like the subtract somewhat has to wrap if `%0` is `INT_MIN`. So that seems like it could be a miscompile unless I've misremembered how one of the no-wrap flags works.

And after all of that, LLVM still generates the redundant comparison on bath x86-64 and ARM64:
```
make_id(int*, long):
        ldr     w9, [x0, x1, lsl #2]
        mov     w8, #-2097152
        sub     w8, w8, w9
        cmn     w9, #512, lsl #12
        csel    w0, w9, w8, gt
        ret
```
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJzcWVtv4zYW_jXMy0EMifL1wQ-Jk8wG6MwWs0GLfTIo8chihyK1JOVLf_2C1MWS46Rz6bbADgaWKJ4bz40fGWat2CnENZndk9nDDatdoc06K5jiEk12k2p-Wj_VUgIeWVlJJMkdiR5IdFc4V1k_ok-EPmVVNcl0WQmJ5haPldQGjf9C6NPvhD59YD8_ZsWC_9YwN79P2oArEDLNEYQK750auoGffvrlI-xQoWEObZjNtZT6INQO7j5_nE-BWYtlKk-9VWQetf_DsGRfcCs4ocsXQu-8UKnVjtBVzwDtP8lNeB5WnorM7o-RfznGgclKIDShZPYw5ir1vuFaBi6a3NJotYhndEzG7FB4q4Imq5bK1ulASvu7GovISjW0jyazmA5Miy80ZhYlHKKzukbqzo3JDLqrfmt-fy2ERHiGg64lBzxWmDkvhSkOHzabc2z8R6tLdIUPjSuYA4O1bYNG5pGtUy_7-6PUhyd-IzzvxCb46SIwfUSCgOb3wuO2Tm0gabxIW8LXbj7TRGdhlfwWT78UCJaVCDkTsjYITFoNOstqY0ErOC7nt_NpkC5cMaoMoXY_nv2drzA7ejJ-0IZD5Yz3tOECCL2HKaF3xopXfrbMDHlXb0hmxythOLt6SOQlXSR_NVTRCulikZV6v8O3uf-sJP_zvMyDoQ-__vPzA_z88rn1MqH33r_0bvpmLr_vxS4QfBSIS-fyV87Ve3tt_o9TNuTh82fItWlTFAhd2iK4MsVQ_U1aa9N07KFberFS7sv2E51ZZ-rMTV6AJA_gThUCWdyDSCiQxcPQAI65UAjc6q3UGZOgdK045mCY2iGhS8_TpwrdwDReTZPIm9CIm0Zvh44uffYrnbHK-XrsZSPjWskTEDrjzDHPI-bTfp7QmfAKgkXbWilWIt8yzo1fRmM4KmfOO5ZnYcawk-DHsOYdOpRYonLeBKFSL9rCwDWhv_jiHNsQdLehJXSW5hOpGQ8yw4tI6ICz0xlyXYqdgumA2RYmMDL_ErzVCxwVOaEzX5yeVPgXu3MduS3MtVz1M3UaOPxT1QdQ9gCDYNFmhwsCehcZdHsmJ1HDiBIzByJu9QcPjLR2ozrt9BrsLetktdnUp9W1NP-kHTY7WruVqfpA5lFoD35kwyiXbGeBGQ9jcjQGORRoEHiN4HTH6p1J5tHksoy0ETuhmIQNofe-1XaA6D81Wie0el0yWVV1FZMIlcmaI5Bk4_GQcZOCJI_Xpq3jQr0zfbKEPvmas0OaNmnrEh6BJHdQC-WWW3dOaIBffIH1A9oPvG-T-6GcJofhZcj9CBgEW_E76pzQ5aMvIULvYNlzA6RaS0gDYTz4XKsAYTlU7NTm-x3Q5Kz2lQmZVtbhsfLF5RK6dfCs9kwK_twUS5TGcRwTujg_IkIXURS9_xhr6WSPewx0BWv9YrcOQrcY-KLu-M7LeWiYZvd-75203wceGK0HbCFyF7h8RQ09Af168ei2ohFtHXMi22bMOpJsWgIfe7o8W7AhyaYRHKxNHkny2I5fCxeK4_GK7HokvHe5l3j7Lmljbdg5zsp8q112qjYQXbgRoCmFnt1bDUOlA2GhOdRGtX4ZzPS9oScJKkfJ9XbvuL29HQ7vFDDnsKwcMAdWlJUU-alBzAgOrYOMWYTKaF5naAOkPvjWk4rfmTEIBm0tnZ3AJ-3A-n0pTDsdsgx07iVZHDWYbzun_fzh3-libz_YoYh_-FbmbRz3pyuI6C9vS39XLXcJ-hUVfaXoBvV81kQTkXsIsd1mkqnddtsK2G7TWkgn1JZZW5c4TOlNs8bLBVys-K3HaqgcFRf5_3Et_1gp3ykeKqBDvIQuz6A3IMor1XDGtd-MVWk8nSXL-XS1uApXhXJ_H2D9WsQ6xpvvINXo-zDqGHjKHt5FQ7DfkjsmJGRMSthrn0DTEJxJX1EDKHmBbeOr4Db6MWgbfSWwjQdYNvpuXNumYCY9QH1z_W1GNNArid87MgtX6NqdYW0QMo_6SztXCPXFjmBzW8IeOad6j5AxBbkwOJlM4NdCZAU8N2wgLGTaGH8MT57gvnZwQGG4PHnx_kTOUWJ3IdjuLJPRlud5cI8KLDrX30gxK3i4V3wmdFECg1SETbQywiKH1tRmMZCi50Ol610RTq_NunSD7yHVrnjzMKDV-NorHAUmcI8Zq234_vzpZfvx-ZOfs4ilBSm-IAgHwipCFw4qg2H745CehmZ5RSd08Aw5omzYwiG7Tp1hmTtDhoJZb-7BsCo0WB-dmQ-k9-_IhAn8SzceGtuSdad4BqWwLWiAWkm0Nvhwj37CYIlliuHIow-gVYdDQOnboL45HB20-WInlz2V5Q4N-LoMTMz1OWSdkPLi-tcgrxVnyhtXVswIq5V3d8pc0V1AeBc1Vw1_eF_zuov-pbfC_6tr3_N95Dfd_N7wdcJXyYrd4DpeJNF8OU9W05tizRcsWi7m83xBl4sVW8WL1TJezdLViqY0x_xGrGlEp3EcTSMaJ_F0gjSOc8anC8rZkk6RTCMsmZCT0HS02d0Ia2tcx_F0QRc3kqUobfc3CLP2VLdpvbO-TQnr7JnPCSdx_VFY6-sz15L7xOnT3wd_kBrpCepA6I-IIQ99trR_OlC8TZmb2sj1GCbvhCvqtMXFYRtvHreV0b-FC8KnsAIPTNtF7Nf0vwEAAP__v-YXkg">