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

    <tr>
        <th>Summary</th>
        <td>
            Integer range checks should lift outside of lossless truncations
        </td>
    </tr>

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

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

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

<pre>
    I was experimenting in Rust with implementing the ASCII versions of certain unicode predicates in terms of an implementation on ASCII bytes, something like this <https://rust.godbolt.org/z/c495YKTsM>
```rust
pub fn demo_is_ascii_digit(c: char) -> bool {
    if let Some(c) = AsciiChar::new(c) {
        c.is_digit()
    } else {
 false
    }
}
```

Unfortunately, that optimizes much worse than checking against the 32-bit type directly:
```llvm
define noundef zeroext i1 @_ZN10playground19demo_is_ascii_digit17h37e7c57418c2459fE(i32 noundef %c) unnamed_addr #0 {
start:
  %_4.i = icmp ult i32 %c, 128
 %_7.i = trunc i32 %c to i8
  %0 = add i8 %_7.i, -48
  %1 = icmp ult i8 %0, 10
  %.0 = and i1 %_4.i, %1
  ret i1 %.0
}

define noundef zeroext i1 @_ZN10playground18std_is_ascii_digit17he953dd4cafd53a1eE(i32 noundef %0) unnamed_addr #0 {
start:
  %1 = add i32 %0, -48
  %2 = icmp ult i32 %1, 10
  ret i1 %2
}
```

Alive2 confirms that converting the former there into the latter is legal: <https://alive2.llvm.org/ce/z/_vhQJE>

But opt seemingly can't do it today: <https://llvm.godbolt.org/z/zdsEsvrsP>

Perhaps the way to get there would be to do something like this:
```diff
 start:
   %_4.i = icmp ult i32 %c, 128
-  %_7.i = trunc i32 %c to i8
-  %0 = add i8 %_7.i, -48
+  %0 = add i32 %c, -48
-  %1 = icmp ult i8 %0, 10
+  %1 = icmp ult i32 %0, 10
   %.0 = and i1 %_4.i, %1
   ret i1 %.0
```
Since alive2 says that's legal <https://alive2.llvm.org/ce/z/bETvXP> and after that other existing optimizations can get the rest of the way: <https://llvm.godbolt.org/z/aEhThW96M>

I tried to make a C++ repro as well, which came out with a slightly different, but still suboptimal, output: <https://cpp.godbolt.org/z/r5xGxa8bc>
```cpp
bool is_ascii_char(uint32_t c) {
    if (c < 128) {
        auto b = (uint8_t)c;
        return (b >= (uint8_t)'0') && (b <= (uint8_t)'9');
 } else {
        return false;
 }
}
```
```llvm
define dso_local noundef zeroext i1 @_Z13is_ascii_charj(i32 noundef %0) local_unnamed_addr #0 {
  %2 = icmp ult i32 %0, 128
  %3 = and i32 %0, 254    ; <-- the comparisons got widened, but the `and` didn't disappear
  %4 = add nsw i32 %3, -48
  %5 = icmp ult i32 %4, 10
  %6 = select i1 %2, i1 %5, i1 false
  ret i1 %6
}
```
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJyUV1tv47oR_jX0y8CGREm-PPgh8TpFWpzitLtFLy8GRY4knqVIgaTieH99QUq-e_ckQeAo1Dcz5PCbb8bMOVlrxDUpnknxZcJ63xi7dtx43_J2UhpxWL_CnjnA9w6tbFF7qWuQGv7ZOw976RuQbafw-MY3CE9fN6-v8IbWSaMdmAo4Ws-khl5LbgRCZ1FIzjy64MqjbSOM6bMz5qXRYPTorTx4dIRuwJkWfRNCKfkdwTfSAck2jfedI9kToS-Evtje-VltRGmUnxlbE_ryg9AXnq-K__7tm_uNZFuSfCHJE5knw2-wGJa6voRKg8DW7KTbMcel3AlZS0_okpPsCXjDLKErmJJsC6UxCsjieTAGAJAVKPTw1bQYLegKSPYFnoKjTTDNnkj2pHF_entpHX74TLpTSEJX57dk8QVQObywqZhyeIUYj3Z6OJ5x_Dd-_ktXxvpeM4_qEPLqG-bBdF628gc6aHvewN5YF1LMNPAG-feQdVYzqZ2PF53RaSk9-EOHIKRF7tUhnO46rFJv7bAksJIaQZteC6zgB1qD7x5kCiRPdv_7e5p0ih1qG96nqwc3kC6abIELXizydMlpXqyqLaFLmdGTU0KLmNRea9ai2DEhLBCaJeeUOc-sP-0Tgskun8l4S5K3HfTKQ_A5-NpASpcjNkAXI9TbXvMTDrwBubxwmUQQEwLk8mgXnE3zS1R6EzVCkxg0uYDNRm9axGwNGw6o4OKIs-jHt7PklgOfz__SeXGfflwVmRA5Z5UoMpbig_Qnn09_es7VkM7kPlP04f2k16k6p4B-pAqelHxDCtzoSgYNikXAjX5De1KzytgWbXi0CFJ7E1cV8x4tSAcKa6aCLNypEIveZ4H_owhxHJVo99b846_bswzFz-c-ViA4xFbqWh2AM03owoMwEOrMCHZ4GCmGuNe7H8Jt3Zt1v98E-h1twzoXD7Jnh0DdGv14xL3plYASw6owj_T2vsSFrKrxCm5v9xPVNYUP1tf0YwVG6PMt8CLsCTb9YCEe3d0iLxh7JuKHi_ZR1V5T9avUHGEgEzh2GGhK6GLk3qeIV26_vf0n8CFui1U-UjtIf7h9wHfpIvPHThC7sAs8PDIELDofmvVIns_wkW2bb82_V_Pfbvj4Ct5KFOGKW_YdgcGG0OeQb4udNcAc7FGpkLp9I3kDnLUIph8HEAZOybrx6gCBiGhR-4Atew_OS6XA9WU8EIs-TO-73j_cOO-6B_u2xftf3tmy5PdzA--6YSXOASe5HEaEZS-1z-jOw32fl0ErlzxsIVbAozmA9d5AGUk0OlvuPKErTrIbpEXfWx1QAb69syB0kRC6iFHonND5Ebp5BF0N0HOUB2PHdeBhCLnE_4n8_nQ4EM7slOFM_bRNpdlVmv_4SQeKTna_6EO_6CrJddcPS9m5li8wtMjj0JU9h1xOp7EquGk7ZqULlVObQFKBGsWRkgFC5gnTgswTEFKMGi8d6zpk9iJoflIu7fbHwNl9byweniK_GyPmEedQIT_3SboZH4vx8WqiPMvT_GdXOhHrTKyyFZvgOp0v5ul8tUiWk2bNKrritOTVXJQ0yUUqqqKqSiHKghVFlkzkmiY0S2hKE5qn-XLGVshZWeQUyyKZp5zkCbZMqpOWTaRzPa7nyXyZTRQrUbnjtxe7DqBp2deO5ImSzruzmZde4fpVe6zRgmW6xmGmdeCa2PGUrHyQBicFBn1TxjmFzg19aBDCSW_V-loxaumbvpxx0466N_6Zdtb8gdwT-hK37Ah9ibv-fwAAAP__9hrh0A">