[llvm] [X86][SelectionDAG] - Extend dag combiner to handle pow(1.5f), pow(x,2/3f) (PR #214202)

via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 5 05:07:09 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-x86

Author: Rohit Aggarwal (rohitaggarwal007)

<details>
<summary>Changes</summary>



Fold pow(X, 2/3) -> cbrt(X) * cbrt(X) and pow(X, 1.5) -> X * sqrt(X) in addition to the existing cbrt and sqrt-based fractional exponent folds.


Microbenchmarks Runtime | Baseline gnu libm(A) | PowFold gnu libm(B) | Baseline amdlibm(C) | PowFold amdlibm(D)
-- | -- | -- | -- | --
pow_exp_1_5_ir_pow itr=100000 | 6.784 | 3.989 | 7.255 | 3.981
pow_exp_2_3 itr=100000 | 6.835 | 6.528 | 7.217 | 3.453



---
Full diff: https://github.com/llvm/llvm-project/pull/214202.diff


2 Files Affected:

- (modified) llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp (+20-6) 
- (added) llvm/test/CodeGen/X86/pow-fold.ll (+239) 


``````````diff
diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index 418ef38daac29..978e92c1dbcfd 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -20329,10 +20329,15 @@ SDValue DAGCombiner::visitFPOW(SDNode *N) {
   //       Some range near 1/3 should be fine.
   EVT VT = N->getValueType(0);
   EVT ScalarVT = VT.getScalarType();
-  if ((ScalarVT == MVT::f32 &&
-       ExponentC->getValueAPF().isExactlyValue(1.0f / 3.0f)) ||
-      (ScalarVT == MVT::f64 &&
-       ExponentC->getValueAPF().isExactlyValue(1.0 / 3.0))) {
+  bool Exponent1by3 = (ScalarVT == MVT::f32 &&
+                       ExponentC->getValueAPF().isExactlyValue(1.0f / 3.0f)) ||
+                      (ScalarVT == MVT::f64 &&
+                       ExponentC->getValueAPF().isExactlyValue(1.0 / 3.0));
+  bool Exponent2by3 = (ScalarVT == MVT::f32 &&
+                       ExponentC->getValueAPF().isExactlyValue(2.0f / 3.0f)) ||
+                      (ScalarVT == MVT::f64 &&
+                       ExponentC->getValueAPF().isExactlyValue(2.0 / 3.0));
+  if (Exponent1by3 || Exponent2by3) {
     // pow(-0.0, 1/3) = +0.0; cbrt(-0.0) = -0.0.
     // pow(-inf, 1/3) = +inf; cbrt(-inf) = -inf.
     // pow(-val, 1/3) =  nan; cbrt(-val) = -num.
@@ -20354,7 +20359,12 @@ SDValue DAGCombiner::visitFPOW(SDNode *N) {
          DAG.getTargetLoweringInfo().isOperationExpand(ISD::FCBRT, VT)))
       return SDValue();
 
-    return DAG.getNode(ISD::FCBRT, SDLoc(N), VT, N->getOperand(0));
+    SDLoc DL(N);
+    SDValue Cbrt = DAG.getNode(ISD::FCBRT, DL, VT, N->getOperand(0));
+    if (Exponent1by3)
+      return Cbrt;
+    // pow(X, 2/3) --> cbrt(X) * cbrt(X)
+    return DAG.getNode(ISD::FMUL, DL, VT, Cbrt, Cbrt);
   }
 
   // Try to convert x ** (1/4) and x ** (3/4) into square roots.
@@ -20363,7 +20373,8 @@ SDValue DAGCombiner::visitFPOW(SDNode *N) {
   // power-of-2 fractional exponents.
   bool ExponentIs025 = ExponentC->getValueAPF().isExactlyValue(0.25);
   bool ExponentIs075 = ExponentC->getValueAPF().isExactlyValue(0.75);
-  if (ExponentIs025 || ExponentIs075) {
+  bool ExponentIs150 = ExponentC->getValueAPF().isExactlyValue(1.50);
+  if (ExponentIs025 || ExponentIs075 || ExponentIs150) {
     // pow(-0.0, 0.25) = +0.0; sqrt(sqrt(-0.0)) = -0.0.
     // pow(-inf, 0.25) = +inf; sqrt(sqrt(-inf)) =  NaN.
     // pow(-0.0, 0.75) = +0.0; sqrt(-0.0) * sqrt(sqrt(-0.0)) = +0.0.
@@ -20390,6 +20401,9 @@ SDValue DAGCombiner::visitFPOW(SDNode *N) {
     // pow(X, 0.25) --> sqrt(sqrt(X))
     SDLoc DL(N);
     SDValue Sqrt = DAG.getNode(ISD::FSQRT, DL, VT, N->getOperand(0));
+    // pow(X, 1.50) --> X * sqrt(X)
+    if (ExponentIs150)
+      return DAG.getNode(ISD::FMUL, DL, VT, N->getOperand(0), Sqrt);
     SDValue SqrtSqrt = DAG.getNode(ISD::FSQRT, DL, VT, Sqrt);
     if (ExponentIs025)
       return SqrtSqrt;
diff --git a/llvm/test/CodeGen/X86/pow-fold.ll b/llvm/test/CodeGen/X86/pow-fold.ll
new file mode 100644
index 0000000000000..2bdac03d2b956
--- /dev/null
+++ b/llvm/test/CodeGen/X86/pow-fold.ll
@@ -0,0 +1,239 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc < %s -mtriple=x86_64-- -mcpu=x86-64 -mattr=+avx | FileCheck %s
+; RUN: FileCheck %s --check-prefix=IR < %s
+; RUN: opt -passes=instcombine -S %s | FileCheck %s --check-prefix=IR-OPT
+
+; Verify the fractional-exponent pow folds, including the cases added by this
+; change:
+;   pow(X, 2/3) --> cbrt(X) * cbrt(X)
+;   pow(X, 1.5) --> X * sqrt(X)
+; The pre-existing folds (1/3, 0.25, 0.75) are included to guard against
+; regressions.
+
+declare float @llvm.pow.f32(float, float)
+declare double @llvm.pow.f64(double, double)
+
+; pow(X, 1.5) with @llvm.pow.f64 still in IR: InstCombine rewrites this to
+; X * sqrt(X) when run through the IR optimizer (see IR-OPT checks below), but
+; when llc sees the unoptimized IR the DAG combiner performs the fold at
+; codegen. This is the path that matters for -O0 IR / llc-only
+; tests and complements the existing InstCombine rewrite used by clang -O3.
+define double @pow_f64_1_5_ir_pow(double %x) {
+; IR-LABEL: @pow_f64_1_5_ir_pow(
+; IR:         call{{.*}}@llvm.pow.f64
+;
+; IR-OPT-LABEL: @pow_f64_1_5_ir_pow(
+; IR-OPT:       call{{.*}}@llvm.sqrt.f64
+; IR-OPT:       fmul
+; IR-OPT-NOT:   call{{.*}}@llvm.pow.f64
+; IR-OPT-LABEL: @pow_f64_1_5(
+;
+; CHECK-LABEL: pow_f64_1_5_ir_pow:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vsqrtsd %xmm0, %xmm0, %xmm1
+; CHECK-NEXT:    vmulsd %xmm1, %xmm0, %xmm0
+; CHECK-NEXT:    retq
+  %r = call afn ninf nsz nnan double @llvm.pow.f64(double %x, double 1.500000e+00)
+  ret double %r
+}
+
+; pow(X, 1.5) --> X * sqrt(X)
+define double @pow_f64_1_5(double %x) {
+; CHECK-LABEL: pow_f64_1_5:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vsqrtsd %xmm0, %xmm0, %xmm1
+; CHECK-NEXT:    vmulsd %xmm1, %xmm0, %xmm0
+; CHECK-NEXT:    retq
+  %r = call afn ninf nsz nnan double @llvm.pow.f64(double %x, double 1.500000e+00)
+  ret double %r
+}
+
+define float @pow_f32_1_5(float %x) {
+; CHECK-LABEL: pow_f32_1_5:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vsqrtss %xmm0, %xmm0, %xmm1
+; CHECK-NEXT:    vmulss %xmm1, %xmm0, %xmm0
+; CHECK-NEXT:    retq
+  %r = call afn ninf nsz nnan float @llvm.pow.f32(float %x, float 1.500000e+00)
+  ret float %r
+}
+
+; pow(X, 2/3) --> cbrt(X) * cbrt(X)
+define double @pow_f64_2_3(double %x) {
+; CHECK-LABEL: pow_f64_2_3:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    pushq %rax
+; CHECK-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-NEXT:    callq cbrt at PLT
+; CHECK-NEXT:    vmulsd %xmm0, %xmm0, %xmm0
+; CHECK-NEXT:    popq %rax
+; CHECK-NEXT:    .cfi_def_cfa_offset 8
+; CHECK-NEXT:    retq
+  %r = call afn ninf nsz nnan double @llvm.pow.f64(double %x, double 0x3FE5555555555555)
+  ret double %r
+}
+
+define float @pow_f32_2_3(float %x) {
+; CHECK-LABEL: pow_f32_2_3:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    pushq %rax
+; CHECK-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-NEXT:    callq cbrtf at PLT
+; CHECK-NEXT:    vmulss %xmm0, %xmm0, %xmm0
+; CHECK-NEXT:    popq %rax
+; CHECK-NEXT:    .cfi_def_cfa_offset 8
+; CHECK-NEXT:    retq
+  %r = call afn ninf nsz nnan float @llvm.pow.f32(float %x, float 0x3FE5555560000000)
+  ret float %r
+}
+
+; pow(X, 1/3) --> cbrt(X)  (pre-existing)
+define double @pow_f64_1_3(double %x) {
+; CHECK-LABEL: pow_f64_1_3:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    jmp cbrt at PLT # TAILCALL
+  %r = call afn ninf nsz nnan double @llvm.pow.f64(double %x, double 0x3FD5555555555555)
+  ret double %r
+}
+
+; pow(X, 0.25) --> sqrt(sqrt(X))  (pre-existing)
+define double @pow_f64_0_25(double %x) {
+; CHECK-LABEL: pow_f64_0_25:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vsqrtsd %xmm0, %xmm0, %xmm0
+; CHECK-NEXT:    vsqrtsd %xmm0, %xmm0, %xmm0
+; CHECK-NEXT:    retq
+  %r = call afn ninf nsz nnan double @llvm.pow.f64(double %x, double 2.500000e-01)
+  ret double %r
+}
+
+; pow(X, 0.75) --> sqrt(X) * sqrt(sqrt(X))  (pre-existing)
+define double @pow_f64_0_75(double %x) {
+; CHECK-LABEL: pow_f64_0_75:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vsqrtsd %xmm0, %xmm0, %xmm0
+; CHECK-NEXT:    vsqrtsd %xmm0, %xmm0, %xmm1
+; CHECK-NEXT:    vmulsd %xmm1, %xmm0, %xmm0
+; CHECK-NEXT:    retq
+  %r = call afn ninf nsz nnan double @llvm.pow.f64(double %x, double 7.500000e-01)
+  ret double %r
+}
+
+; Negative tests: without the required fast-math flags the fold must not happen
+; and we must retain a libm pow call.
+;
+; Cbrt path (1/3, 2/3): requires { afn, ninf, nsz, nnan }.
+; Sqrt path (0.25): requires { afn, ninf, nsz }.
+; Sqrt path (0.75, 1.5): requires { afn, ninf }.
+
+define double @pow_f64_1_5_no_afn(double %x) {
+; CHECK-LABEL: pow_f64_1_5_no_afn:
+; CHECK:         jmp pow at PLT
+; CHECK-NOT:     vsqrtsd
+  %r = call ninf nsz nnan double @llvm.pow.f64(double %x, double 1.500000e+00)
+  ret double %r
+}
+
+define double @pow_f64_1_5_no_ninf(double %x) {
+; CHECK-LABEL: pow_f64_1_5_no_ninf:
+; CHECK:         jmp pow at PLT
+; CHECK-NOT:     vsqrtsd
+  %r = call afn nsz nnan double @llvm.pow.f64(double %x, double 1.500000e+00)
+  ret double %r
+}
+
+define double @pow_f64_2_3_no_afn(double %x) {
+; CHECK-LABEL: pow_f64_2_3_no_afn:
+; CHECK:         jmp pow at PLT
+; CHECK-NOT:     cbrt
+  %r = call ninf nsz nnan double @llvm.pow.f64(double %x, double 0x3FE5555555555555)
+  ret double %r
+}
+
+define double @pow_f64_2_3_no_nnan(double %x) {
+; CHECK-LABEL: pow_f64_2_3_no_nnan:
+; CHECK:         jmp pow at PLT
+; CHECK-NOT:     cbrt
+  %r = call afn ninf nsz double @llvm.pow.f64(double %x, double 0x3FE5555555555555)
+  ret double %r
+}
+
+define double @pow_f64_1_3_no_nsz(double %x) {
+; CHECK-LABEL: pow_f64_1_3_no_nsz:
+; CHECK:         jmp pow at PLT
+; CHECK-NOT:     cbrt
+  %r = call afn ninf nnan double @llvm.pow.f64(double %x, double 0x3FD5555555555555)
+  ret double %r
+}
+
+define double @pow_f64_0_25_no_nsz(double %x) {
+; CHECK-LABEL: pow_f64_0_25_no_nsz:
+; CHECK:         jmp pow at PLT
+; CHECK-NOT:     vsqrtsd
+  %r = call afn ninf double @llvm.pow.f64(double %x, double 2.500000e-01)
+  ret double %r
+}
+
+define double @pow_f64_0_75_no_afn(double %x) {
+; CHECK-LABEL: pow_f64_0_75_no_afn:
+; CHECK:         jmp pow at PLT
+; CHECK-NOT:     vsqrtsd
+  %r = call ninf nsz nnan double @llvm.pow.f64(double %x, double 7.500000e-01)
+  ret double %r
+}
+
+define double @pow_f64_no_flags(double %x) {
+; CHECK-LABEL: pow_f64_no_flags:
+; CHECK:         jmp pow at PLT
+; CHECK-NOT:     vsqrtsd
+; CHECK-NOT:     cbrt
+  %r = call double @llvm.pow.f64(double %x, double 1.500000e+00)
+  ret double %r
+}
+
+; Negative tests: all required fast-math flags are present, but the fold must
+; still not happen for other reasons:
+;   - optsize/minimize size: sqrt-path folds are disabled (ForCodeSize).
+;   - non-exact constant exponent: exponent does not match exactly.
+;   - variable exponent: exponent is not a compile-time constant.
+
+define double @pow_f64_1_5_optsize(double %x) optsize {
+; CHECK-LABEL: pow_f64_1_5_optsize:
+; CHECK:         jmp pow at PLT
+; CHECK-NOT:     vsqrtsd
+  %r = call afn ninf nsz nnan double @llvm.pow.f64(double %x, double 1.500000e+00)
+  ret double %r
+}
+
+define double @pow_f64_0_25_optsize(double %x) optsize {
+; CHECK-LABEL: pow_f64_0_25_optsize:
+; CHECK:         jmp pow at PLT
+; CHECK-NOT:     vsqrtsd
+  %r = call afn ninf nsz nnan double @llvm.pow.f64(double %x, double 2.500000e-01)
+  ret double %r
+}
+
+define double @pow_f64_1_5_nonexact_exp(double %x) {
+; CHECK-LABEL: pow_f64_1_5_nonexact_exp:
+; CHECK:         jmp pow at PLT
+; CHECK-NOT:     vsqrtsd
+  %r = call afn ninf nsz nnan double @llvm.pow.f64(double %x, double 0x3FF8000000000001)
+  ret double %r
+}
+
+define double @pow_f64_2_3_nonexact_exp(double %x) {
+; CHECK-LABEL: pow_f64_2_3_nonexact_exp:
+; CHECK:         jmp pow at PLT
+; CHECK-NOT:     cbrt
+  %r = call afn ninf nsz nnan double @llvm.pow.f64(double %x, double 0x3FE5555555555556)
+  ret double %r
+}
+
+define double @pow_f64_var_exp(double %x, double %e) {
+; CHECK-LABEL: pow_f64_var_exp:
+; CHECK:         jmp pow at PLT
+; CHECK-NOT:     vsqrtsd
+; CHECK-NOT:     cbrt
+  %r = call afn ninf nsz nnan double @llvm.pow.f64(double %x, double %e)
+  ret double %r
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/214202


More information about the llvm-commits mailing list