Bug type: crash
Component: ConstantFold
Bug location: llvm/lib/Analysis/ConstantFolding.cpp: ConstantFoldIntrinsicCall2
Issue title: Misoptimization: `EarlyCSEPass` uses replaces `powi.f16` with `float` result
Labels: miscompilation
llvm:analysis
Reported by: tgross35

It looks like EarlyCSEPass is transformin the following:

```llvm
  %_6 = alloca [48 x i8], align 8
  %_3 = alloca [2 x i8], align 2
  %0 = call half @llvm.powi.f16.i32(half 0xH3C00, i32 1) ; 0xH3C00 = 1.0f16
  store half %0, ptr %_3, align 2
  %1 = load half, ptr %_3, align 2
  %_4 = fcmp oeq half %1, 0xH3C00
  br i1 %_4, label %bb1, label %bb2
```

Into this:

```llvm
  %_6 = alloca [48 x i8], align 8
  %_3 = alloca [2 x i8], align 2
  store float 1.000000e+00, ptr %_3, align 2
  %0 = load half, ptr %_3, align 2
  %_4 = fcmp oeq half %0, 0xH3C00
  br i1 %_4, label %bb1, label %bb2
```

And later InstCombine folds further into:

```llvm
  %_6 = alloca [48 x i8], align 8
  %_3 = alloca [2 x i8], align 2
  store float 1.000000e+00, ptr %_3, align 2
  br i1 false, label %bb1, label %bb2
```

EarlyCSE seems to be doing an incorrect transformation: the result of `powi.f16(1.0, 1)` should be `half` 1.0 (0x3c00), but it is returning `float` 1.0 (0x3f800000). This is incorrect and an OOB write.

This comes from the following rust code, which asserts only when optimizations are enabled:

```rust
#![feature(f16)]
#![allow(unused)]

#[inline(never)]
pub fn check_pow(a: f16) {
    assert_eq!(1.0f16.powi(1), 1.0);
}

pub fn main() {
    check_pow(1.0);
    println!("finished");
}
```

Link to compiler explorer: https://rust.godbolt.org/z/zsbzzxGvj

I'm not sure how to reduce to a llc example since the passes appear different. I have been testing on aarch64 since x86 has other f16 ABI bugs, but I don't think this is limited to aarch64.

Comments: author: dtcxzyw
body: Reproducer: https://godbolt.org/z/jhM834G3z
```
define half @test() {
    %x = call half @llvm.powi.f16.i32(half 0xH3C00, i32 1)
    ret half %x
}
```
```
opt: /root/llvm-project/llvm/lib/IR/Value.cpp:507: void llvm::Value::doRAUW(llvm::Value*, llvm::Value::ReplaceMetadataUses): Assertion `New->getType() == getType() && "replaceAllUses of value with new value of different type!"' failed.
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
0.	Program arguments: /opt/compiler-explorer/clang-assertions-trunk/bin/opt -o /app/output.s -S -passes=early-cse <source>
1.	Running pass "function(early-cse<>)" on module "<source>"
2.	Running pass "early-cse<>" on function "test"
 #0 0x0000000004e9b948 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x4e9b948)
 #1 0x0000000004e990bc SignalHandler(int) Signals.cpp:0:0
 #2 0x000078ad4b442520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
 #3 0x000078ad4b4969fc pthread_kill (/lib/x86_64-linux-gnu/libc.so.6+0x969fc)
 #4 0x000078ad4b442476 gsignal (/lib/x86_64-linux-gnu/libc.so.6+0x42476)
 #5 0x000078ad4b4287f3 abort (/lib/x86_64-linux-gnu/libc.so.6+0x287f3)
 #6 0x000078ad4b42871b (/lib/x86_64-linux-gnu/libc.so.6+0x2871b)
 #7 0x000078ad4b439e96 (/lib/x86_64-linux-gnu/libc.so.6+0x39e96)
 #8 0x0000000004cce094 llvm::Value::doRAUW(llvm::Value*, llvm::Value::ReplaceMetadataUses) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x4cce094)
 #9 0x0000000003ae3b58 (anonymous namespace)::EarlyCSE::processNode(llvm::DomTreeNodeBase<llvm::BasicBlock>*) EarlyCSE.cpp:0:0
#10 0x0000000003ae5a85 (anonymous namespace)::EarlyCSE::run() EarlyCSE.cpp:0:0
#11 0x0000000003ae81f8 llvm::EarlyCSEPass::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x3ae81f8)
#12 0x0000000002ac11be llvm::detail::PassModel<llvm::Function, llvm::EarlyCSEPass, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x2ac11be)
#13 0x0000000004c9f2d8 llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x4c9f2d8)
#14 0x0000000000dc9bee llvm::detail::PassModel<llvm::Function, llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0xdc9bee)
#15 0x0000000004c9dd46 llvm::ModuleToFunctionPassAdaptor::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x4c9dd46)
#16 0x0000000000dca1fe llvm::detail::PassModel<llvm::Module, llvm::ModuleToFunctionPassAdaptor, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0xdca1fe)
#17 0x0000000004c9bf10 llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x4c9bf10)
#18 0x00000000008db1d2 llvm::runPassPipeline(llvm::StringRef, llvm::Module&, llvm::TargetMachine*, llvm::TargetLibraryInfoImpl*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::StringRef, llvm::ArrayRef<llvm::PassPlugin>, llvm::ArrayRef<std::function<void (llvm::PassBuilder&)>>, llvm::opt_tool::OutputKind, llvm::opt_tool::VerifierKind, bool, bool, bool, bool, bool, bool, bool) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x8db1d2)
#19 0x00000000008ce0fc optMain (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x8ce0fc)
#20 0x000078ad4b429d90 (/lib/x86_64-linux-gnu/libc.so.6+0x29d90)
#21 0x000078ad4b429e40 __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e40)
#22 0x00000000008c556e _start (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x8c556e)
Program terminated with signal: SIGSEGV
Compiler returned: 139
```

There is a bug in constant folding.



Fix patch:
commit 34bfed63313d1340378fc1be931253333db8c36c
Author: Yingwei Zheng <dtcxzyw2333@gmail.com>
Date:   Mon Jul 15 13:00:25 2024 +0800

    [ConstantFold] Fix result type when folding powi.f16 (#98681)
    
    Fixes #98665.

diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index 962880f68f07..6c52091cd5d7 100644
--- a/llvm/lib/Analysis/ConstantFolding.cpp
+++ b/llvm/lib/Analysis/ConstantFolding.cpp
@@ -2754,27 +2754,28 @@ static Constant *ConstantFoldIntrinsicCall2(Intrinsic::ID IntrinsicID, Type *Ty,
           ((Mask & fcPosInf) && Op1V.isPosInfinity());
         return ConstantInt::get(Ty, Result);
       }
+      case Intrinsic::powi: {
+        int Exp = static_cast<int>(Op2C->getSExtValue());
+        switch (Ty->getTypeID()) {
+        case Type::HalfTyID:
+        case Type::FloatTyID: {
+          APFloat Res(std::pow(Op1V.convertToFloat(), Exp));
+          if (Ty->isHalfTy()) {
+            bool Unused;
+            Res.convert(APFloat::IEEEhalf(), APFloat::rmNearestTiesToEven,
+                        &Unused);
+          }
+          return ConstantFP::get(Ty->getContext(), Res);
+        }
+        case Type::DoubleTyID:
+          return ConstantFP::get(Ty, std::pow(Op1V.convertToDouble(), Exp));
+        default:
+          return nullptr;
+        }
+      }
       default:
         break;
       }
-
-      if (!Ty->isHalfTy() && !Ty->isFloatTy() && !Ty->isDoubleTy())
-        return nullptr;
-      if (IntrinsicID == Intrinsic::powi && Ty->isHalfTy())
-        return ConstantFP::get(
-            Ty->getContext(),
-            APFloat((float)std::pow((float)Op1V.convertToDouble(),
-                                    (int)Op2C->getZExtValue())));
-      if (IntrinsicID == Intrinsic::powi && Ty->isFloatTy())
-        return ConstantFP::get(
-            Ty->getContext(),
-            APFloat((float)std::pow((float)Op1V.convertToDouble(),
-                                    (int)Op2C->getZExtValue())));
-      if (IntrinsicID == Intrinsic::powi && Ty->isDoubleTy())
-        return ConstantFP::get(
-            Ty->getContext(),
-            APFloat((double)std::pow(Op1V.convertToDouble(),
-                                     (int)Op2C->getZExtValue())));
     }
     return nullptr;
   }


Regression tests:
file: llvm/test/Transforms/EarlyCSE/math-2.ll
commands: opt -passes=early-cse -earlycse-debug-hash -S -o - %s
tests: test_name: i_powi
test_body: ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare double @llvm.powi.f64.i32(double, i32) #0

define double @i_powi() {
  %res = tail call fast double @llvm.powi.f64.i32(double 1.000000e+00, i32 2)
  ret double %res
}

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

test_name: pr98665
test_body: define half @pr98665() {
  %x = call half @llvm.powi.f16.i32(half 0xH3C00, i32 1)
  ret half %x
}

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare half @llvm.powi.f16.i32(half, i32) #0

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