Bug type: miscompilation
Component: InstCombine
Bug location: llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp: InstCombinerImpl::visitCallInst
Issue title: instcombine is wrong about a vector fshr->shl transformation
Labels: miscompilation
llvm:instcombine
Reported by: regehr

Instcombine seems to be mis-optimizing this function:
```llvm
define i32 @f(<4 x i8> %0) {
  %2 = call <4 x i8> @llvm.fshr.v4i8(<4 x i8> %0, <4 x i8> zeroinitializer, <4 x i8> <i8 -12, i8 -80, i8 35, i8 1>)
  %3 = bitcast <4 x i8> %2 to i32
  ret i32 %3
}

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare <4 x i8> @llvm.fshr.v4i8(<4 x i8>, <4 x i8>, <4 x i8>) #0

attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
```
the result is:
```llvm
define i32 @f(<4 x i8> %0) {
  %2 = shl <4 x i8> %0, <i8 4, i8 0, i8 5, i8 7>
  %3 = bitcast <4 x i8> %2 to i32
  ret i32 %3
}

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare <4 x i8> @llvm.fshr.v4i8(<4 x i8>, <4 x i8>, <4 x i8>) #0

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare <4 x i8> @llvm.fshl.v4i8(<4 x i8>, <4 x i8>, <4 x i8>) #0

attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
```

Alive says:
```
ERROR: Value mismatch

Example:
<4 x i8> %#0 = < #x00 (0), #x01 (1), #x00 (0), #x00 (0) >

Source:
<4 x i8> %#2 = < #x00 (0), #x00 (0), #x00 (0), #x00 (0) >
i32 %#3 = #x00000000 (0)

Target:
<4 x i8> %#2 = < #x00 (0), #x01 (1), #x00 (0), #x00 (0) >
i32 %#3 = #x00000100 (256)
Source value: #x00000000 (0)
Target value: #x00000100 (256)
```

https://alive2.llvm.org/ce/z/s4UpPe

cc @hatsunespica



Comments: author: nikic
body: Smaller test case: https://alive2.llvm.org/ce/z/hScyGk
author: nikic
body: I believe the root cause is the fshr to fshl canonicalization: https://alive2.llvm.org/ce/z/NQM3tR

We're performing this transform: https://alive2.llvm.org/ce/z/6UUEsS But we don't verify the pre-condition that the (modular) shift amount is not zero!

For non-vectors this is a non-issue because we'd fold a zero shift earlier, but for vectors it is an issue.

Fix patch:
commit 46957a138dea339799059ea5bf032e40042ff185
Author: Nikita Popov <npopov@redhat.com>
Date:   Fri Apr 19 14:00:49 2024 +0900

    [InstCombine] Fix incorrect fshr to fshl transform
    
    This transform is only valid if the (modular) shift amount is not
    zero.
    
    Proof: https://alive2.llvm.org/ce/z/WBxn-x
    
    Fixes https://github.com/llvm/llvm-project/issues/89338.

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index aa547565bd08..e7a2f54f8680 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -1988,7 +1988,10 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
       // is not entirely arbitrary. For historical reasons, the backend may
       // recognize rotate left patterns but miss rotate right patterns.
       if (IID == Intrinsic::fshr) {
-        // fshr X, Y, C --> fshl X, Y, (BitWidth - C)
+        // fshr X, Y, C --> fshl X, Y, (BitWidth - C) if C is not zero.
+        if (!isKnownNonZero(ShAmtC, SQ.getWithInstruction(II)))
+          return nullptr;
+
         Constant *LeftShiftC = ConstantExpr::getSub(WidthC, ShAmtC);
         Module *Mod = II->getModule();
         Function *Fshl = Intrinsic::getDeclaration(Mod, Intrinsic::fshl, Ty);


Regression tests:
file: llvm/test/Transforms/InstCombine/fsh.ll
commands: opt < %s -passes=instcombine -S
tests: test_name: fsh_unary_shuffle_ops_partial_widening
test_body: ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare <2 x i32> @llvm.fshr.v2i32(<2 x i32>, <2 x i32>, <2 x i32>) #0

define <2 x i32> @fsh_unary_shuffle_ops_partial_widening(<3 x i32> %x, <2 x i32> %y, <2 x i32> %z) {
  %a = shufflevector <3 x i32> %x, <3 x i32> poison, <2 x i32> <i32 1, i32 0>
  %b = shufflevector <2 x i32> %y, <2 x i32> poison, <2 x i32> <i32 1, i32 0>
  %c = shufflevector <2 x i32> %z, <2 x i32> poison, <2 x i32> <i32 1, i32 0>
  %r = call <2 x i32> @llvm.fshr.v2i32(<2 x i32> %a, <2 x i32> %b, <2 x i32> %c)
  ret <2 x i32> %r
}

attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }

test_name: fshr_vec_zero_elem
test_body: ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare <2 x i32> @llvm.fshr.v2i32(<2 x i32>, <2 x i32>, <2 x i32>) #0

define <2 x i32> @fshr_vec_zero_elem(<2 x i32> %x, <2 x i32> %y) {
  %fsh = call <2 x i32> @llvm.fshr.v2i32(<2 x i32> %x, <2 x i32> %y, <2 x i32> <i32 2, i32 0>)
  ret <2 x i32> %fsh
}

attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }