[llvm] [InstCombine] Add constant folding for hypot library calls (PR #171863)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Dec 11 08:59:49 PST 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: wentywenty (wentywenty)
<details>
<summary>Changes</summary>
This patch adds constant folding optimization for hypot(x, y) when both arguments are constant values.
Implementation details:
- Only supports float and double precision, as std::hypot lacks support for extended precision types (fp128, x86_fp80, ppc_fp128, fp16)
- Respects errno semantics: folds when the function is marked as not accessing memory (intrinsic), fast-math is enabled, or when the result is finite / inputs are non-finite (hypot only sets errno on overflow with finite inputs)
- Follows the same pattern as other LibCall optimizations like remquo
The optimization handles:
- Basic cases: hypot(3.0, 4.0) -> 5.0
- Special values: Inf and NaN propagation
- Overflow: preserves calls in strict mode, folds in fast-math mode
- Strict FP mode: preserves calls to maintain FP exception semantics
Tests cover all major scenarios including precision handling, special values, overflow behavior, and strictfp mode.
---
Full diff: https://github.com/llvm/llvm-project/pull/171863.diff
3 Files Affected:
- (modified) llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h (+1)
- (modified) llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp (+38)
- (added) llvm/test/Transforms/InstCombine/hypot.ll (+89)
``````````diff
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
``````````
</details>
https://github.com/llvm/llvm-project/pull/171863
More information about the llvm-commits
mailing list