Bug type: miscompilation
Component: InstCombine
Bug location: llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp: canEvaluateTruncated
Issue title: [InstCombine] Wrong value truncation due to incorrect analysis of iv known bits
Labels: miscompilation
llvm:instcombine
Reported by: aleks-tmb

```llvm
define void @src(i32 %0, ptr %p, i1 %cnd) {
entry:
  br i1 %cnd, label %header, label %exit

header:                                           ; preds = %latch, %entry
  %iv = phi i32 [ %iv.next, %latch ], [ 256, %entry ]
  %iv.trunc = trunc i32 %iv to i16
  %div = udiv i16 11, %iv.trunc
  br i1 %cnd, label %loop1, label %loop0

loop0:                                            ; preds = %header
  %load = load i32, ptr %p, align 4
  br label %loop1

loop1:                                            ; preds = %loop0, %header
  %v = phi i32 [ %load, %loop0 ], [ 0, %header ]
  store i32 %v, ptr %p, align 4
  %guard = icmp ugt i32 %iv, 10
  br i1 %guard, label %latch, label %loop2

loop2:                                            ; preds = %loop1
  %div.trunc = trunc i16 %div to i8
  store i8 %div.trunc, ptr %p, align 1
  br label %latch

latch:                                            ; preds = %loop2, %loop1
  %iv.next = add nuw nsw i32 %iv, 1
  %exitcond = icmp ugt i32 %iv, 300
  br i1 %exitcond, label %exit, label %header

exit:                                             ; preds = %latch
  ret void
}
```
If we run `instcombine` pass on this example, it will convert `trunc i32 %iv to i16` into `trunc i32 %iv to i8` to avoid cast:
https://godbolt.org/z/ze4E45ThT

But this transformation is wrong, because on the first iteration `%iv` is equal to 256, and the 8 bits trunc makes it equal to `0`. This give us a division by zero on the next instruction: `%div = udiv i16 11, %iv.trunc`.

It seems that the problematic patch is https://github.com/llvm/llvm-project/pull/73662

@nikic Could you please take a look?

Comments: author: antoniofrighetto
body: May be false positive in Alive2 as well?
author: dtcxzyw
body: > May be false positive in Alive2 as well?

You need `-src-unroll` and `-tgt-unroll` for loops :)
https://alive2.llvm.org/ce/z/Vh4zAU
author: antoniofrighetto
body: Just out of curiosity, did you fine-tune the iterations in a particular way, or some trial-and-error? (I had tried a few ones before and didn’t manage to have the UB triggered)
author: dtcxzyw
body: > Just out of curiosity, did you fine-tune the iterations in a particular way, or some trial-and-error? (I had tried a few ones before and didn’t manage to have the UB triggered)

I just full unrolled the loop :)
```
%iv.next = add nuw nsw i32 %iv, 1
%exitcond = icmp ugt i32 %iv, 300
```
author: antoniofrighetto
body: > I just full unrolled the loop :)

Definitely makes sense, thanks!
author: nikic
body: Reduced test case:
```llvm
define void @src(i32 %x) {
  %x.trunc = trunc i32 %x to i16
  %div = udiv i16 11, %x.trunc
  %cmp = icmp ult i32 %x, 256
  br i1 %cmp, label %loop, label %exit

loop:
  %trunc = trunc i16 %div to i8
  call void @use(i8 %trunc)
  br label %loop

exit:
  ret void
}

declare void @use(i8)
```

The issue is that canEvaluateTruncated() preserves context instruction across non-speculatable operations.

Fix patch:
commit 534f8569a3c9fccfd5cbc5f632b63ad0cf711098
Author: Nikita Popov <npopov@redhat.com>
Date:   Mon Jun 17 15:36:55 2024 +0200

    [InstCombine] Don't preserve context across div
    
    We can't preserve the context across a non-speculatable instruction,
    as this might introduce a trap. Alternatively, we could also
    insert all the replacement instruction at the use-site, but that
    would be a more intrusive change for the sake of this edge case.
    
    Fixes https://github.com/llvm/llvm-project/issues/95547.

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
index 1b4c319032ca..6a6bdba3fa70 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
@@ -291,10 +291,12 @@ static bool canEvaluateTruncated(Value *V, Type *Ty, InstCombinerImpl &IC,
     uint32_t BitWidth = Ty->getScalarSizeInBits();
     assert(BitWidth < OrigBitWidth && "Unexpected bitwidths!");
     APInt Mask = APInt::getBitsSetFrom(OrigBitWidth, BitWidth);
-    if (IC.MaskedValueIsZero(I->getOperand(0), Mask, 0, CxtI) &&
-        IC.MaskedValueIsZero(I->getOperand(1), Mask, 0, CxtI)) {
-      return canEvaluateTruncated(I->getOperand(0), Ty, IC, CxtI) &&
-             canEvaluateTruncated(I->getOperand(1), Ty, IC, CxtI);
+    // Do not preserve the original context instruction. Simplifying div/rem
+    // based on later context may introduce a trap.
+    if (IC.MaskedValueIsZero(I->getOperand(0), Mask, 0, I) &&
+        IC.MaskedValueIsZero(I->getOperand(1), Mask, 0, I)) {
+      return canEvaluateTruncated(I->getOperand(0), Ty, IC, I) &&
+             canEvaluateTruncated(I->getOperand(1), Ty, IC, I);
     }
     break;
   }


Regression tests:
file: llvm/test/Transforms/InstCombine/trunc.ll
commands: opt < %s -passes=instcombine -S
tests: test_name: trunc_nuw_xor_vector
test_body: target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"

define <2 x i1> @trunc_nuw_xor_vector(<2 x i8> %x, <2 x i8> %y) {
  %xor = xor <2 x i8> %x, %y
  %r = trunc nuw <2 x i8> %xor to <2 x i1>
  ret <2 x i1> %r
}

test_name: pr95547
test_body: target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"

declare void @use.i8(i8)

define void @pr95547(i32 %x) {
  %x.trunc = trunc i32 %x to i16
  %div = udiv i16 11, %x.trunc
  %cmp = icmp ult i32 %x, 256
  br i1 %cmp, label %loop, label %exit

loop:                                             ; preds = %loop, %0
  %trunc = trunc i16 %div to i8
  call void @use.i8(i8 %trunc)
  br label %loop

exit:                                             ; preds = %0
  ret void
}