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

    <tr>
        <th>Summary</th>
        <td>
            #108985 optimization doesn't remove strlen like loop body in some cases
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
            clang:bounds-safety
      </td>
    </tr>

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

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

<pre>
    In #108985 an optimization was added that tries to replace `strlen` like coding idioms with a call to `strlen`.

We ran into problems with this when this is optimization is used in `-fbounds-safety`.

For a test case like:

```
#include <ptrcheck.h>
int *__indexable convert_null_terminated_to_bounded_ptr(int *__null_terminated p) {
 return __unsafe_terminated_by_to_indexable(p);
}
```

the unoptimized IR looks like:

```
; Function Attrs: noinline nounwind optnone ssp uwtable(sync)
define [2 x i64] @convert_null_terminated_to_bounded_ptr(ptr noundef %p) #0 {
entry:
  %retval = alloca %"__bounds_safety::wide_ptr.indexable", align 8
  %p.addr = alloca ptr, align 8
  %terminated_by.len = alloca i64, align 8
  store ptr %p, ptr %p.addr, align 8
  %0 = load ptr, ptr %p.addr, align 8
  store i64 0, ptr %terminated_by.len, align 8
  br label %terminated_by.loop_cond

terminated_by.loop_cond: ; preds = %terminated_by.loop_cont, %entry
  %terminated_by.len1 = load i64, ptr %terminated_by.len, align 8
  %1 = getelementptr inbounds i32, ptr %0, i64 %terminated_by.len1
  %terminated_by.elem = load i32, ptr %1, align 4
 %terminted_by.check_terminator = icmp eq i32 %terminated_by.elem, 0
  br i1 %terminted_by.check_terminator, label %terminated_by.loop_end, label %terminated_by.loop_cont

terminated_by.loop_cont: ; preds = %terminated_by.loop_cond
  %terminated_by.new_len = add i64 %terminated_by.len1, 1
  store i64 %terminated_by.new_len, ptr %terminated_by.len, align 8
  br label %terminated_by.loop_cond

terminated_by.loop_end: ; preds = %terminated_by.loop_cond
  %2 = load i64, ptr %terminated_by.len, align 8
  %3 = add i64 %2, 1
  %terminated_by.upper = getelementptr inbounds i32, ptr %0, i64 %3
  %4 = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr %retval, i32 0, i32 0
  store ptr %0, ptr %4, align 8
  %5 = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr %retval, i32 0, i32 1
  store ptr %terminated_by.upper, ptr %5, align 8
  %6 = load [2 x i64], ptr %retval, align 8
  ret [2 x i64] %6
}
```

If we optimize this with `-mllvm  -disable-loop-idiom-wcslen` the IR looks like

```
define [2 x i64] @convert_null_terminated_to_bounded_ptr(ptr noundef %p) local_unnamed_addr #0 {
entry:
  br label %terminated_by.loop_cond

terminated_by.loop_cond: ; preds = %terminated_by.loop_cond, %entry
  %terminated_by.len.0 = phi i64 [ 0, %entry ], [ %terminated_by.new_len, %terminated_by.loop_cond ]
  %0 = getelementptr inbounds i32, ptr %p, i64 %terminated_by.len.0
 %terminated_by.elem = load i32, ptr %0, align 4
 %terminted_by.check_terminator = icmp eq i32 %terminated_by.elem, 0
 %terminated_by.new_len = add i64 %terminated_by.len.0, 1
  br i1 %terminted_by.check_terminator, label %terminated_by.loop_end, label %terminated_by.loop_cond

terminated_by.loop_end: ; preds = %terminated_by.loop_cond
  %1 = getelementptr inbounds i32, ptr %p, i64 %terminated_by.len.0
  %terminated_by.upper = getelementptr i8, ptr %1, i64 4
  %2 = ptrtoint ptr %p to i64
  %.fca.0.insert = insertvalue [2 x i64] poison, i64 %2, 0
  %3 = ptrtoint ptr %terminated_by.upper to i64
 %.fca.1.insert = insertvalue [2 x i64] %.fca.0.insert, i64 %3, 1
  ret [2 x i64] %.fca.1.insert
}
```

Now if we optimize with the pass enabled the result looks like

```
; Function Attrs: nofree nounwind ssp memory(argmem: read) uwtable(sync)
define [2 x i64] @convert_null_terminated_to_bounded_ptr(ptr noundef %p) local_unnamed_addr #0 {
entry:
  %wcslen = tail call i64 @wcslen(ptr %p)
  br label %terminated_by.loop_cond

terminated_by.loop_cond: ; preds = %terminated_by.loop_cond, %entry
  %terminated_by.len.0 = phi i64 [ 0, %entry ], [ %terminated_by.new_len, %terminated_by.loop_cond ]
  %0 = getelementptr inbounds i32, ptr %p, i64 %terminated_by.len.0
 %terminated_by.elem = load i32, ptr %0, align 4
 %terminted_by.check_terminator = icmp eq i32 %terminated_by.elem, 0
 %terminated_by.new_len = add i64 %terminated_by.len.0, 1
  br i1 %terminted_by.check_terminator, label %terminated_by.loop_end, label %terminated_by.loop_cond

terminated_by.loop_end: ; preds = %terminated_by.loop_cond
  %1 = getelementptr inbounds i32, ptr %p, i64 %wcslen
  %terminated_by.upper = getelementptr i8, ptr %1, i64 4
 %2 = ptrtoint ptr %p to i64
  %.fca.0.insert = insertvalue [2 x i64] poison, i64 %2, 0
  %3 = ptrtoint ptr %terminated_by.upper to i64
  %.fca.1.insert = insertvalue [2 x i64] %.fca.0.insert, i64 %3, 1
  ret [2 x i64] %.fca.1.insert
}
```

This IR doesn't seem great because the call to `wcslen` is pointless because the loop body is still present. I would expect the perf of this code to be worse.
</pre>
<img width="1" height="1" alt="" src="http://email.email.llvm.org/o/eJzsWM1u4zgSfhr6UrBAUZLtHHxQkg2Qyx4aC-xRoMSyzW2K1JJU3J6nH1CSbfm3k57uYAZoIEAEq-qrX9VXJHdOrjXikmSPJHue8NZvjF0KVNWu2aCdlEbslq8aCEtiunhYZMA1mMbLWv7BvTQattwBFwIF-A334K1EB96AxUbxCoHMqPNWoSYzCkp-RaiMkHoNUkhTO9hKvwEOFVcqqI3FI0JzQvP_IliuQWpvoLGmVLhX8xvpYLtB3T9Jd-qadNA6FCB1gJ2uStNq4aaOr9DvjvgvxgIHj85DxR12TpIk71-SGR3-aE5YInWlWoFAkqfG22qD1ddoQ5J_EZpL7YGwvCikFviNlypEqt_Q-kK3ShUebS019ygKb4rOFxRF4y1hi4PumSQ0hD0AmT8SmoNF31oNRdHqEMIYsNwFzINhwhZBkSRBj8yfz-Ogud8gtHrIFgp4_QLKmK_uTvDJI7y0uuoym3tvHUly0EZqJTWCNq3eSi1CBbTRCM410G794I7b6Sp4RHOBqyBPskcG30DOUpI9A0npu3PVeNtZE7gCwrI-QyyhQ5pQe7vrI4Dw3qJ_4wpI8gxcKVPx8CNhrOhhXTG0Q5KTJN9KgcFMNEolI-wJuJJrDYs9aBNxIewYtPPtQu6kRJFCPVYJsZ-pOG8sBqwhsqfDc2fwigXaISrDxd6Fuxq9ATlLgY5kL9w8VystKF6iuiJsTFNURouhr268THIIDdRYFK7z-CaQD7YJy_o63spjfAx7SON7QyEs65XX6FFhjdoHVan7dgCZsBFcl6WQrqtOXHcvoI7cO8GLj-6kQfugPOh2E-XQ_qZvMVnVDeD_A9R1awGU7gsl4--iBvl79UQtvifSVepOyf0HSi6u51Hjtjh8MkLcKQN7gvisvW-hfVbX44ea_pAB9lf6OjlPFTtm5gKibRq0P_YhJHvE9J6-brc_NGoHg_3c7qwmrJ9W_cPFnBxPsouJSliWfb6X8aWXV7I_wsmu-D079sIJW141P9a16M_5lWWzW6vA6wq2uF-ccNipwnYVdqZaqbcaYCqkC8FPQ8NOu81tuq3csNWFZeJ0gbjYHn4J6wcWVUWrNa9RFD0j31gEPpHAxLsILOqJu9nI_qvKHvvu2SvCUOjw4t4su-1Gh3CyJbzrQ2_uMV5ET0jrPYxHfy3j_TBrRPQ4HD-TNX86ZXxgm3lHbT9AFIvzvSYgp6dM1njrTTjd7D0IZ7zw-Q9S0ariEY2kdmh9X_ru8Y2r9nxcNEY6o0chsOPec2C_c4PXYjm6sPcgfp8H5w6fcOKhm66O3xMzt0bxv80W5Ok4Hs65CA13DlCHKSy6Hyy6Vvm7U_fWmW1lcXRiCye1Gmtjd4QtuF3XWAcxi1yEKftph7gPjHPCsp5-uoJ5LlV_gdAVI6UDNfVmBvjfNPCbBn7TwEVth0_lZ03-f8rg_7tN_v-Erfv1CwiDThM29-AQa1hb5B5KrHjrsJv6o3vS4wIuXciR9gqdO5EOXQKlEbsg4rxUKvSUQ-0jeIWtaZUA_NZg5XuOQbsCs-rPAJURGCyVCFtjHUYTsUzEQ_LAJ7iM52lKZzFdxJPNsoppNl_xJHlIacrKebpIGJ-VD7hKZowt4olcMsoymtI5i1maxlFJxYLNstlslZa44JykFGsuVRSOG5Gx64l0rsVlnKTzZDbpviXXXVIzVimu1yTJT29zGSPZ88QuA8C0bNeOpFRJ590R0kuvcHm8yT65Kz4m3mJt3hD6W-j-xnqURg3O1NhdFLtJa9Vy430TaJWwF8Je1tJv2jKqTE3YS7A8_Js21vwPK0_YSxeZI-xlCO5tyf4MAAD__0DYfOk">