diff --git a/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h b/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h index 64d2512308935..7de7ff52c6ac2 100644 --- a/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h +++ b/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h @@ -215,6 +215,7 @@ class LibCallSimplifier { Value *optimizeSymmetric(CallInst *CI, LibFunc Func, IRBuilderBase &B); Value *optimizeRemquo(CallInst *CI, IRBuilderBase &B); Value *optimizeFdim(CallInst *CI, IRBuilderBase &B); + Value *optimizeHypot(CallInst *CI, IRBuilderBase &B); // Wrapper for all floating point library call optimizations Value *optimizeFloatingPointLibCall(CallInst *CI, LibFunc Func, IRBuilderBase &B); diff --git a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp index c3537f544c432..ee55296863a95 100644 --- a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp @@ -3215,6 +3215,40 @@ Value *LibCallSimplifier::optimizeFdim(CallInst *CI, IRBuilderBase &B) { return ConstantFP::get(CI->getType(), MaxVal); } +/// Constant folds hypot +Value *LibCallSimplifier::optimizeHypot(CallInst *CI, IRBuilderBase &B) { + + const APFloat *X, *Y; + if (!match(CI->getArgOperand(0), m_APFloat(X)) || + !match(CI->getArgOperand(1), m_APFloat(Y))) { + return nullptr; + } + Type *Ty = CI->getType(); + APFloat Res(X->getSemantics()); + + // Only support float and double, as std::hypot doesn't support extended precision types. + if (Ty->isFloatTy()) + Res = APFloat(std::hypot(X->convertToFloat(), Y->convertToFloat())); + else if(Ty->isDoubleTy()) + Res = APFloat(std::hypot(X->convertToDouble(), Y->convertToDouble())); + else { + return nullptr; + } + + // We can fold in the following cases: + // 1. Function doesn't access memory (intrinsic, no errno). + // 2. Fast-math is enabled (ignore errno). + if (CI->doesNotAccessMemory() || CI->isFast()) + return ConstantFP::get(Ty, Res); + + // 3. Result is finite OR inputs are non-finite (won't set errno). + // hypot only sets errno on overflow with finite inputs. + if (Res.isFinite() || !X->isFinite() || !Y->isFinite()) + return ConstantFP::get(Ty, Res); + + return nullptr; +} + //===----------------------------------------------------------------------===// // Integer Library Call Optimizations //===----------------------------------------------------------------------===// @@ -4140,6 +4174,10 @@ Value *LibCallSimplifier::optimizeFloatingPointLibCall(CallInst *CI, case LibFunc_fdimf: case LibFunc_fdiml: return optimizeFdim(CI, Builder); + case LibFunc_hypot: + case LibFunc_hypotf: + case LibFunc_hypotl: + return optimizeHypot(CI, Builder); case LibFunc_fminf: case LibFunc_fmin: case LibFunc_fminl: diff --git a/llvm/test/Transforms/InstCombine/hypot.ll b/llvm/test/Transforms/InstCombine/hypot.ll new file mode 100644 index 0000000000000..4c0c5c82d89c0 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/hypot.ll @@ -0,0 +1,89 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -passes=instcombine -S | FileCheck %s + +declare double @hypot(double, double) +declare float @hypotf(float, float) + +; Basic folding: Double precision +define double @test_hypot_double_basic() { +; CHECK-LABEL: @test_hypot_double_basic( +; CHECK-NEXT: ret double 5.000000e+00 +; + %call = call double @hypot(double 3.000000e+00, double 4.000000e+00) + ret double %call +} + +; Basic folding: Float precision +define float @test_hypot_float_basic() { +; CHECK-LABEL: @test_hypot_float_basic( +; CHECK-NEXT: ret float 5.000000e+00 +; + %call = call float @hypotf(float 3.000000e+00, float 4.000000e+00) + ret float %call +} + +; Special value: Infinity (doesn't set errno, should fold) +define double @test_hypot_inf() { +; CHECK-LABEL: @test_hypot_inf( +; CHECK-NEXT: ret double 0x7FF0000000000000 +; + %call = call double @hypot(double 0x7FF0000000000000, double 3.000000e+00) + ret double %call +} + +; Special value: NaN (should propagate NaN) +define double @test_hypot_nan() { +; CHECK-LABEL: @test_hypot_nan( +; CHECK-NEXT: ret double 0x7FF8000000000000 +; + %call = call double @hypot(double 0x7FF8000000000000, double 3.000000e+00) + ret double %call +} + +; Overflow: Strict mode (errno-sensitive, should not fold) +define double @test_hypot_overflow_strict() { +; CHECK-LABEL: @test_hypot_overflow_strict( +; CHECK-NEXT: [[CALL:%.*]] = call double @hypot(double 0x7FEFFFFFFFFFFFFF, double 0x7FEFFFFFFFFFFFFF) +; CHECK-NEXT: ret double [[CALL]] +; + %call = call double @hypot(double 0x7FEFFFFFFFFFFFFF, double 0x7FEFFFFFFFFFFFFF) + ret double %call +} + +; Overflow: Fast-math mode (ignore errno, force folding) +define double @test_hypot_overflow_fast() { +; CHECK-LABEL: @test_hypot_overflow_fast( +; CHECK-NEXT: ret double 0x7FF0000000000000 +; + %call = call fast double @hypot(double 0x7FEFFFFFFFFFFFFF, double 0x7FEFFFFFFFFFFFFF) + ret double %call +} + +; Non-constant argument (should not fold) +define double @test_hypot_non_constant(double %x) { +; CHECK-LABEL: @test_hypot_non_constant( +; CHECK-NEXT: [[CALL:%.*]] = call double @hypot(double [[X:%.*]], double 4.000000e+00) +; CHECK-NEXT: ret double [[CALL]] +; + %call = call double @hypot(double %x, double 4.000000e+00) + ret double %call +} + +; Strict FP mode (should not fold even for valid constants) +define double @test_hypot_strictfp() strictfp { +; CHECK-LABEL: @test_hypot_strictfp( +; CHECK-NEXT: [[CALL:%.*]] = call double @hypot(double 3.000000e+00, double 4.000000e+00) #[[ATTR0:[0-9]+]] +; CHECK-NEXT: ret double [[CALL]] +; + %call = call double @hypot(double 3.000000e+00, double 4.000000e+00) strictfp + ret double %call +} + +; Zero values (should fold: hypot(0,0) = 0) +define double @test_hypot_zero() { +; CHECK-LABEL: @test_hypot_zero( +; CHECK-NEXT: ret double 0.000000e+00 +; + %call = call double @hypot(double 0.000000e+00, double 0.000000e+00) + ret double %call +} \ No newline at end of file