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

    <tr>
        <th>Summary</th>
        <td>
            `assume`-`nonnull` operand bundles should optimize away more
        </td>
    </tr>

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

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

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

<pre>
    Today in rust we emit certain conversions using `assume(icmp ne %p, null)`: <https://rust.godbolt.org/z/WTGj3Ks9q> <https://github.com/rust-lang/rust/blob/63f6845e570305a92eaf855897768617366164d6/tests/codegen-llvm/intrinsics/transmute.rs#L380-L388>
```llvm
define { ptr, i64 } @check_pair_to_dst_ref(i64 noundef %x.0, i64 noundef %x.1) unnamed_addr {
start:
  %_0.0 = getelementptr i8, ptr null, i64 %x.0
  %0 = icmp ne ptr %_0.0, null
  call void @llvm.assume(i1 %0)
  %1 = insertvalue { ptr, i64 } poison, ptr %_0.0, 0
  %2 = insertvalue { ptr, i64 } %1, i64 %x.1, 1
  ret { ptr, i64 } %2
}
```

But since I hear extra uses from such `icmp`s can make optimization worse sometimes, I wanted to move to [assume operand bundles](https://llvm.org/docs/LangRef.html#assume-operand-bundles) instead, so made that change and now get what I think is correct from that,

```llvm
define { ptr, i64 } @check_pair_to_dst_ref(i64 noundef %x.0, i64 noundef %x.1) unnamed_addr #0 {
start:
  %_0.0 = getelementptr i8, ptr null, i64 %x.0
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %_0.0) ]
  %0 = insertvalue { ptr, i64 } poison, ptr %_0.0, 0
  %1 = insertvalue { ptr, i64 } %0, i64 %x.1, 1
  ret { ptr, i64 } %1
}
```

But that turned out to give bad consequences.  For example, what used to be a quite-good <https://rust.godbolt.org/z/vhY16Kavc>

```llvm
define void @long_integer_map(ptr dead_on_unwind noalias nocapture noundef writable writeonly sret([2048 x i8]) align 4 dereferenceable(2048) %_0, ptr noalias nocapture noundef readonly align 4 dereferenceable(2048) %x) unnamed_addr personality ptr @rust_eh_personality {
start:
  %array.i.i.i.i = alloca [2048 x i8], align 4
  br label %vector.body

vector.body:
  %index = phi i64 [ 0, %start ], [ %index.next, %vector.body ]
  %offset.idx = shl i64 %index, 2
  %next.gep = getelementptr i8, ptr %x, i64 %offset.idx
  %0 = getelementptr i8, ptr %next.gep, i64 16
  %wide.load = load <4 x i32>, ptr %next.gep, align 4
  %wide.load1 = load <4 x i32>, ptr %0, align 4
  %1 = mul <4 x i32> %wide.load, splat (i32 13)
  %2 = mul <4 x i32> %wide.load1, splat (i32 13)
  %3 = add <4 x i32> %1, splat (i32 7)
  %4 = add <4 x i32> %2, splat (i32 7)
  %5 = getelementptr inbounds nuw i32, ptr %array.i.i.i.i, i64 %index
  %6 = getelementptr inbounds nuw i8, ptr %5, i64 16
  store <4 x i32> %3, ptr %5, align 4
  store <4 x i32> %4, ptr %6, align 4
 %index.next = add nuw i64 %index, 8
  %7 = icmp eq i64 %index.next, 512
 br i1 %7, label %core::array::drain::drain_array_with::h75d8f8b0fda7bb41.exit, label %vector.body

core::array::drain::drain_array_with::h75d8f8b0fda7bb41.exit:
 call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 4 dereferenceable(2048) %_0, ptr noundef nonnull align 4 dereferenceable(2048) %array.i.i.i.i, i64 2048, i1 false)
  ret void
}
```

Never removes *any* of the superfluous-after-inlining `assume`s, giving this obviously-silly IR:

```llvm
; Function Attrs: nofree norecurse nosync nounwind memory(argmem: readwrite, inaccessiblemem: write) uwtable
define void @long_integer_map(ptr dead_on_unwind noalias nocapture noundef writable writeonly sret([2048 x i8]) align 4 dereferenceable(2048) %_0, ptr noalias nocapture noundef readonly align 4 dereferenceable(2048) %x) unnamed_addr #1 personality ptr @__CxxFrameHandler3 {
start:
  %array.i.i.i.i = alloca [2048 x i8], align 4
  %array1.i = alloca [2048 x i8], align 4
  call void @llvm.lifetime.start.p0(i64 2048, ptr nonnull %array1.i), !noalias !7
  call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 4 dereferenceable(2048) %array1.i, ptr noundef nonnull readonly align 4 dereferenceable(2048) %x, i64 2048, i1 false), !noalias !11
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %array1.i) ]
  %0 = getelementptr inbounds nuw i8, ptr %array1.i, i64 2048
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %0) ]
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %0) ]
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %array1.i) ]
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %0) ]
  %1 = getelementptr inbounds nuw i8, ptr %array1.i, i64 2048
  br label %vector.body

vector.body: ; preds = %vector.body, %start
  %index = phi i64 [ 0, %start ], [ %index.next, %vector.body ]
  %offset.idx = shl i64 %index, 2
  %2 = or disjoint i64 %offset.idx, 4
  %3 = or disjoint i64 %offset.idx, 8
  %4 = or disjoint i64 %offset.idx, 12
  %5 = or disjoint i64 %offset.idx, 16
  %6 = or disjoint i64 %offset.idx, 20
  %7 = or disjoint i64 %offset.idx, 24
  %8 = or disjoint i64 %offset.idx, 28
 %next.gep = getelementptr i8, ptr %array1.i, i64 %offset.idx
  %next.gep1 = getelementptr i8, ptr %array1.i, i64 %2
  %next.gep2 = getelementptr i8, ptr %array1.i, i64 %3
  %next.gep3 = getelementptr i8, ptr %array1.i, i64 %4
 %next.gep4 = getelementptr i8, ptr %array1.i, i64 %5
  %next.gep5 = getelementptr i8, ptr %array1.i, i64 %6
  %next.gep6 = getelementptr i8, ptr %array1.i, i64 %7
  %next.gep7 = getelementptr i8, ptr %array1.i, i64 %8
 call void @llvm.assume(i1 true) [ "nonnull"(ptr %next.gep) ]
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %next.gep1) ]
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %next.gep2) ]
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %next.gep3) ]
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %next.gep4) ]
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %next.gep5) ]
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %next.gep6) ]
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %next.gep7) ]
  %9 = getelementptr inbounds nuw i8, ptr %next.gep, i64 4
  %10 = getelementptr inbounds nuw i8, ptr %next.gep1, i64 4
  %11 = getelementptr inbounds nuw i8, ptr %next.gep2, i64 4
  %12 = getelementptr inbounds nuw i8, ptr %next.gep3, i64 4
  %13 = getelementptr inbounds nuw i8, ptr %next.gep4, i64 4
  %14 = getelementptr inbounds nuw i8, ptr %next.gep5, i64 4
  %15 = getelementptr inbounds nuw i8, ptr %next.gep6, i64 4
  %16 = getelementptr inbounds nuw i8, ptr %next.gep7, i64 4
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %9) ]
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %10) ]
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %11) ]
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %12) ]
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %13) ]
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %14) ]
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %15) ]
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %16) ]
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %next.gep) ]
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %next.gep1) ]
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %next.gep2) ]
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %next.gep3) ]
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %next.gep4) ]
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %next.gep5) ]
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %next.gep6) ]
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %next.gep7) ]
  %17 = getelementptr i8, ptr %next.gep, i64 16
  %wide.load = load <4 x i32>, ptr %next.gep, align 4, !noalias !12
  %wide.load8 = load <4 x i32>, ptr %17, align 4, !noalias !12
  %18 = mul <4 x i32> %wide.load, splat (i32 13)
  %19 = mul <4 x i32> %wide.load8, splat (i32 13)
  %20 = add <4 x i32> %18, splat (i32 7)
  %21 = add <4 x i32> %19, splat (i32 7)
  %22 = getelementptr inbounds nuw i32, ptr %array.i.i.i.i, i64 %index
  %23 = getelementptr inbounds nuw i8, ptr %22, i64 16
  store <4 x i32> %20, ptr %22, align 4
  store <4 x i32> %21, ptr %23, align 4
  %index.next = add nuw i64 %index, 8
  %24 = icmp eq i64 %index.next, 512
  br i1 %24, label %_ZN4core5array5drain16drain_array_with17hdab83ed713860683E.exit, label %vector.body, !llvm.loop !27

_ZN4core5array5drain16drain_array_with17hdab83ed713860683E.exit: ; preds = %vector.body
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %1) ]
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %0) ]
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %0) ]
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %1) ]
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %0) ]
  call void @llvm.lifetime.end.p0(i64 2048, ptr nonnull %array1.i), !noalias !7
  call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 4 dereferenceable(2048) %_0, ptr noundef nonnull align 4 dereferenceable(2048) %array.i.i.i.i, i64 2048, i1 false)
  ret void
}
```

Trunk and clean that up a bit, but it's still full of unnecessary `assume`s: <https://llvm.godbolt.org/z/b8oM1vxvT>

At a minimum this at least ought to be treated idempotently, since there's no need for 
```llvm
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %0) ]
  call void @llvm.assume(i1 true) [ "nonnull"(ptr %0) ]
```
repeated in a row like that.  But it'd also be nice to optimize out all the ones that came from `GEP nuw`, for example.

---

(Or if this is the wrong way to do this, that'd be good to know and reflect in the langref too.)

</pre>
<img width="1" height="1" alt="" src="http://email.email.llvm.org/o/eJzsWltv47iS_jXMS8GGRF39kIdcOrONmZ1dDBpY7HkxKLFkc1oi1SRlJ_PrD0jJjnxJbHc7c_ph0AFallhfkXUnWcwYsZCItyS5J8njDevsUulbUyprm7K5KRR_uf2iOHsBIUF3xsIaARthoURtmZBQKrlCbYSSBjoj5AJIGjBjugYJzUXZtCARCE1aQh9AdnVN6IykAYnugEQPS2tbQ6I7Qp8IfXIcpgvFC1XbqdILQp_-IvTp_7788mf0q5l9I9GnQ6KFsMuumJaqGRAmNZOL4ZnQp6JWBaFPaVSleZxgkgVRkLAZRVblSZLPsizN0zCL0jRMY54S-mTRWEPoU6k4LlBO6nrlsIW0WkgjSvfNaiZN01mcakNo9FuUB5Pfojwn0ScS3LkF-j9PGtxxrIQTQ3YPrdVOEiKNgWSPQOKgXGL5dd4yoedWzbmxc42VE14ag1Sd5Fg5AT5Pgw3hztuQ0Bl0UrIG-Zxxrh0bEtwZy7R1UgruwI2cB9MASPQIC7RYY4PStlaDyB2qe-qVM0yt5zeQ9nQbZbqxA95Wp25gyeoaVkpwtyi38OmrHYQexql-gAx7SGlQ2xWru6PCaZUwSm7mN-K5nRk9B8ax21mY_xV6DI32DRLq9Jg9jrXpnoO7-86CEbJE-AxLZBrw2WoGnUEDlVYNmK5cOjdwAiNpYKBkEhr2FUG1VjTiL2aFkrBW2iAY1aAVDRrH_jOsmbTIwSpo1Ard_yS57-UIqkXNJIeik7xGQ5JHQvNdZ_Bi7z2HK2-ovzG5-AOr6dI2NaFRDzUZoCYbKDpzQrTIuJuGUdAwjmCXzEK5ZHKB4BhLtXbWA2v3_jPYpZBfQRgoldZY2n71jojQh15W_2FHoFFwZW9438it7tDNgiT3QCiVSvYwlNB8x4TdkMc99_pRXzjLpXo3vNQXwvd8wVuJ7bREDsr9VrAQK4SCcZceDH7rUJZopgBPynkLa9oaHQdvR53p7b1AYPCtExYnC6X42dlhtfz_MP2Vrcoh9L5pdFutKbmYC2lxgXresHbQDUfG50rOO7kW3tZZLZgBqUrW2k7j1tjWWlhW1OgfUMn6BYxGS2hOknsaxDk8Ozty3jkDVouFhBg4aqxQO0E4WkJzN9IbglPl1ure5KqRcc_rLMTnA19oURslWS3sS29BceAEOsflfPzpuLcwrdnLVPT_vJmxulYlg4MVP2zm50kLDTUrsHYYKyyt0lNXU_RqGr94ZSUkx2fPol2K3gaTe_ACIjTxE4OBU-9lPcVU4rMdBo2Ax26mqsqgnQrew5tlvfECj-CI6WasQ5susH03RPRy3vrSK_6uY79DvmGzQQnTDelacJzWinEPMTw8xE7QEXWWfhxkLPwxSngaJjhC35M1Xb1LtQPtE0ZbMwsuBkYUwmiU5-kZCOH7EFFvcJwfQhxSZiPC-G1C-i5hckxvsnCeaEB2aw_0Krkd7xgZRG9WA2Z6EnNsGMmeRRirNB4uI9onGevvDZp4RJPu0ex401Z6fnZ7npJv1pW91ob4bWfY1iWT0PtVoaGvAzP3chsXSqXR-X905-XYP3LNhBw9zv23-VrYZf92mSU8r_IiqDjLiiIOp_gs7A7wQcC5Kqc-Yh2WAw02ZfsybQP3J9J4yC6bMD6UBJfnhe8gP2qX_fcHp4qK1QY3Zu-Sv1vIW5n-d1yhBo2uLDVA6B2TL4TegarALhFM16Ku6k51ZsIqi3oiZC3k7oYwDXyVuxAr994uhQFVrITqTP0yMaKuX-DzH71kj6RxEt3DUydLXzzfWatdaQBSVRpdmtRYdq6glsq8yNJLzCfyBhulXwjNmV402Dgal0t98vaCkKws0RhROMf034dvM-jWPtP_U0Ns6-nwWCExnz88Pz9p1uB_Mbef0NGV64gNaXgR1aFz1qLym62pn9i0DYbdxcYpevH1Ljbi6XzElxbhRraEhtkbPK4ZAF4ncDwMXKzQt2PAwfrC8DobnpEQDzc95-bDsSC2K7jK9HY2Yz8Z2jHRXXeK2xrvhxVxQakPLpK3GrnxrPcIRoX-z7Un6EtZpYEL86cS0h4p_OnDa8SKzhuf75arJ8eHdLdMPU2Q7tagJwlosFvcnSbYrjk_kyAfqs1z91n7Znd0u7UBO2rRJ9AOdn70O0CifZDoO0DiPdHE34GR7E_k6I7mBEi6D3J0C3MCJNsHyb4DJD9eal8Y-F73yVeMplub-xBU-iGo0Yegxh-CmnwIavohqNleZp1dlFn3z4JeD2EuK5VGFrkPdFmqHxnhPtDR4HgSKDoEOhogTwLFh0BHo-RJoOQQ6PTZzzGg9BDosgOfkQ2NgX7YNmdXtfTwuvVteN2oGV43XIbXjZPhdQNkeN3IGH5MSPwn1_6Ta_-WXBuerCs_7K7l8OSEHiDnp6HD7EzQMP_hW5lwdgZEfuJmJ3jnXuaQdny_QsN3SGfvk54uPS6_m6GXlSGUnnc7Q4MDojPuZ2g4poqOHIVeekVD47PvaF4vaWi8c5ky_9fvcak09gJN_FVJmO7fmITZkrMij5BnYZSnQZpHn96_mOntvD-cVap1P2jWHxr9KMtTB03XyZs_8fHhlYu1v3Nu24N6lPznP6b_me7pvuhOfvXdWmWNTPbdOV0LDIreDYvOgnvKDBgr6hoqN1FVQScllmgM0y97d3bHejW9EA-7cYpc_Xe4el592Xbj3Flg0Agpmq7p7_yYhRqZsaC6xdIOvT9WI7PIQXBsWmVR2tpHh77Tzi5Ro5-zVCAROVRKw5FLwp_ObUZoY01pbIf1SmCg1Rpq8bXvuJsC3G90xIHVxstHitK3Aw4thOhbrdzk7BJBSTRDtx5rsO_DI2nwy6f_dZnBcaQPXmJD89W0V81kMhmuWmn-PxpE1etHGA-61kouYM1eHFuu_DeH0zf4ZdzNyndqWQVfpVp7o9NY1VhatyyHUTO50FiBVWraW_ANv434LJqxG7wNsyQOgywLgpvlbcozlqQVZTHjWR4Wszgsy4zN0pgVCQ-KG3FLA5oEeUCDOMhpMC0yVhVhEZZBlEYsKEgcYMNEPd10Qd4IYzq8DZMwm4U3Pv8Y3-tMqcQ1-K9OWcnjjb51RJOiWxinaGGseYWxwtZ4O_aJCUmDjbbTYL8zE8xSdTV_1RVzUmyUxptO17fv9C8PrcZ-Lq1Wf2JpCX3yEzWEPg0rWd3SfwcAAP__B-aoCw">