[llvm] [AArch64] Sanitise pow inputs using a target DAG combine (PR #192958)

David Sherwood via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 23 04:46:49 PDT 2026


https://github.com/david-arm updated https://github.com/llvm/llvm-project/pull/192958

>From ee3ee8987be2c6c48f6331b6df9a31432cacbdf7 Mon Sep 17 00:00:00 2001
From: David Sherwood <david.sherwood at arm.com>
Date: Mon, 20 Apr 2026 13:42:20 +0000
Subject: [PATCH 1/3] Add tests

---
 llvm/test/CodeGen/AArch64/sanitize_vec_pow.ll | 243 ++++++++++++++++++
 1 file changed, 243 insertions(+)
 create mode 100644 llvm/test/CodeGen/AArch64/sanitize_vec_pow.ll

diff --git a/llvm/test/CodeGen/AArch64/sanitize_vec_pow.ll b/llvm/test/CodeGen/AArch64/sanitize_vec_pow.ll
new file mode 100644
index 0000000000000..c4e6a0c74e92a
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/sanitize_vec_pow.ll
@@ -0,0 +1,243 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mattr=+neon,+sve -mtriple=aarch64-linux-gnu --vector-library=ArmPL < %s -o - | FileCheck --check-prefix=CHECK-ARMPL %s
+
+define <4 x float> @foo_false_is_pow_v4f32(<4 x float> %a, <4 x float> %b, <4 x float> %c, <4 x float> %d) {
+; CHECK-ARMPL-LABEL: foo_false_is_pow_v4f32:
+; CHECK-ARMPL:       // %bb.0:
+; CHECK-ARMPL-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
+; CHECK-ARMPL-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-ARMPL-NEXT:    .cfi_offset w30, -16
+; CHECK-ARMPL-NEXT:    mov v16.16b, v2.16b
+; CHECK-ARMPL-NEXT:    bl armpl_vpowq_f32
+; CHECK-ARMPL-NEXT:    fmov v1.4s, #5.00000000
+; CHECK-ARMPL-NEXT:    fcmge v1.4s, v1.4s, v16.4s
+; CHECK-ARMPL-NEXT:    bit v0.16b, v16.16b, v1.16b
+; CHECK-ARMPL-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
+; CHECK-ARMPL-NEXT:    ret
+  %pow = call fast <4 x float> @llvm.pow.v4f32(<4 x float> %a, <4 x float> %b)
+  %fcmp = fcmp fast ole <4 x float> %c, splat (float 5.0e+0)
+  %res = select <4 x i1> %fcmp, <4 x float> %c, <4 x float> %pow
+  ret <4 x float> %res
+}
+
+define <2 x double> @foo_false_is_pow_v2f64(<2 x double> %a, <2 x double> %b, <2 x double> %c, <2 x double> %d) {
+; CHECK-ARMPL-LABEL: foo_false_is_pow_v2f64:
+; CHECK-ARMPL:       // %bb.0:
+; CHECK-ARMPL-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
+; CHECK-ARMPL-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-ARMPL-NEXT:    .cfi_offset w30, -16
+; CHECK-ARMPL-NEXT:    mov v16.16b, v3.16b
+; CHECK-ARMPL-NEXT:    mov v17.16b, v2.16b
+; CHECK-ARMPL-NEXT:    bl armpl_vpowq_f64
+; CHECK-ARMPL-NEXT:    fmov v1.2d, #5.00000000
+; CHECK-ARMPL-NEXT:    fcmge v1.2d, v1.2d, v17.2d
+; CHECK-ARMPL-NEXT:    bit v0.16b, v16.16b, v1.16b
+; CHECK-ARMPL-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
+; CHECK-ARMPL-NEXT:    ret
+  %pow = call fast <2 x double> @llvm.pow.v2f64(<2 x double> %a, <2 x double> %b)
+  %fcmp = fcmp fast ole <2 x double> %c, splat (double 5.0e+0)
+  %res = select <2 x i1> %fcmp, <2 x double> %d, <2 x double> %pow
+  ret <2 x double> %res
+}
+
+define <vscale x 4 x float> @foo_false_is_pow_nxv4f32(<vscale x 4 x float> %a, <vscale x 4 x float> %b, <vscale x 4 x float> %c, <vscale x 4 x float> %d) {
+; CHECK-ARMPL-LABEL: foo_false_is_pow_nxv4f32:
+; CHECK-ARMPL:       // %bb.0:
+; CHECK-ARMPL-NEXT:    stp x29, x30, [sp, #-16]! // 16-byte Folded Spill
+; CHECK-ARMPL-NEXT:    addvl sp, sp, #-2
+; CHECK-ARMPL-NEXT:    str p4, [sp, #7, mul vl] // 2-byte Spill
+; CHECK-ARMPL-NEXT:    str z8, [sp, #1, mul vl] // 16-byte Folded Spill
+; CHECK-ARMPL-NEXT:    .cfi_escape 0x0f, 0x08, 0x8f, 0x10, 0x92, 0x2e, 0x00, 0x40, 0x1e, 0x22 // sp + 16 + 16 * VG
+; CHECK-ARMPL-NEXT:    .cfi_offset w30, -8
+; CHECK-ARMPL-NEXT:    .cfi_offset w29, -16
+; CHECK-ARMPL-NEXT:    .cfi_escape 0x10, 0x48, 0x09, 0x92, 0x2e, 0x00, 0x11, 0x78, 0x1e, 0x22, 0x40, 0x1c // $d8 @ cfa - 8 * VG - 16
+; CHECK-ARMPL-NEXT:    ptrue p4.s
+; CHECK-ARMPL-NEXT:    mov z8.d, z2.d
+; CHECK-ARMPL-NEXT:    mov p0.b, p4.b
+; CHECK-ARMPL-NEXT:    bl armpl_svpow_f32_x
+; CHECK-ARMPL-NEXT:    fmov z1.s, #5.00000000
+; CHECK-ARMPL-NEXT:    fcmge p0.s, p4/z, z1.s, z8.s
+; CHECK-ARMPL-NEXT:    ldr p4, [sp, #7, mul vl] // 2-byte Reload
+; CHECK-ARMPL-NEXT:    mov z0.s, p0/m, z8.s
+; CHECK-ARMPL-NEXT:    ldr z8, [sp, #1, mul vl] // 16-byte Folded Reload
+; CHECK-ARMPL-NEXT:    addvl sp, sp, #2
+; CHECK-ARMPL-NEXT:    ldp x29, x30, [sp], #16 // 16-byte Folded Reload
+; CHECK-ARMPL-NEXT:    ret
+  %pow = call fast <vscale x 4 x float> @llvm.pow.nxv4f32(<vscale x 4 x float> %a, <vscale x 4 x float> %b)
+  %fcmp = fcmp fast ole <vscale x 4 x float> %c, splat (float 5.0e+0)
+  %res = select <vscale x 4 x i1> %fcmp, <vscale x 4 x float> %c, <vscale x 4 x float> %pow
+  ret <vscale x 4 x float> %res
+}
+
+define <vscale x 2 x double> @foo_false_is_pow_nxv2f64(<vscale x 2 x double> %a, <vscale x 2 x double> %b, <vscale x 2 x double> %c, <vscale x 2 x double> %d) {
+; CHECK-ARMPL-LABEL: foo_false_is_pow_nxv2f64:
+; CHECK-ARMPL:       // %bb.0:
+; CHECK-ARMPL-NEXT:    stp x29, x30, [sp, #-16]! // 16-byte Folded Spill
+; CHECK-ARMPL-NEXT:    addvl sp, sp, #-3
+; CHECK-ARMPL-NEXT:    str p4, [sp, #7, mul vl] // 2-byte Spill
+; CHECK-ARMPL-NEXT:    str z9, [sp, #1, mul vl] // 16-byte Folded Spill
+; CHECK-ARMPL-NEXT:    str z8, [sp, #2, mul vl] // 16-byte Folded Spill
+; CHECK-ARMPL-NEXT:    .cfi_escape 0x0f, 0x08, 0x8f, 0x10, 0x92, 0x2e, 0x00, 0x48, 0x1e, 0x22 // sp + 16 + 24 * VG
+; CHECK-ARMPL-NEXT:    .cfi_offset w30, -8
+; CHECK-ARMPL-NEXT:    .cfi_offset w29, -16
+; CHECK-ARMPL-NEXT:    .cfi_escape 0x10, 0x48, 0x09, 0x92, 0x2e, 0x00, 0x11, 0x78, 0x1e, 0x22, 0x40, 0x1c // $d8 @ cfa - 8 * VG - 16
+; CHECK-ARMPL-NEXT:    .cfi_escape 0x10, 0x49, 0x09, 0x92, 0x2e, 0x00, 0x11, 0x70, 0x1e, 0x22, 0x40, 0x1c // $d9 @ cfa - 16 * VG - 16
+; CHECK-ARMPL-NEXT:    ptrue p4.d
+; CHECK-ARMPL-NEXT:    mov z8.d, z3.d
+; CHECK-ARMPL-NEXT:    mov z9.d, z2.d
+; CHECK-ARMPL-NEXT:    mov p0.b, p4.b
+; CHECK-ARMPL-NEXT:    bl armpl_svpow_f64_x
+; CHECK-ARMPL-NEXT:    fmov z1.d, #5.00000000
+; CHECK-ARMPL-NEXT:    fcmge p0.d, p4/z, z1.d, z9.d
+; CHECK-ARMPL-NEXT:    ldr z9, [sp, #1, mul vl] // 16-byte Folded Reload
+; CHECK-ARMPL-NEXT:    ldr p4, [sp, #7, mul vl] // 2-byte Reload
+; CHECK-ARMPL-NEXT:    mov z0.d, p0/m, z8.d
+; CHECK-ARMPL-NEXT:    ldr z8, [sp, #2, mul vl] // 16-byte Folded Reload
+; CHECK-ARMPL-NEXT:    addvl sp, sp, #3
+; CHECK-ARMPL-NEXT:    ldp x29, x30, [sp], #16 // 16-byte Folded Reload
+; CHECK-ARMPL-NEXT:    ret
+  %pow = call fast <vscale x 2 x double> @llvm.pow.nxv2f64(<vscale x 2 x double> %a, <vscale x 2 x double> %b)
+  %fcmp = fcmp fast ole <vscale x 2 x double> %c, splat (double 5.0e+0)
+  %res = select <vscale x 2 x i1> %fcmp, <vscale x 2 x double> %d, <vscale x 2 x double> %pow
+  ret <vscale x 2 x double> %res
+}
+
+; Only added one variant for the true value case, since the other VFs are tested above.
+define <4 x float> @foo_true_is_pow_v4f32(<4 x float> %a, <4 x float> %b, <4 x float> %c, <4 x float> %d) {
+; CHECK-ARMPL-LABEL: foo_true_is_pow_v4f32:
+; CHECK-ARMPL:       // %bb.0:
+; CHECK-ARMPL-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
+; CHECK-ARMPL-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-ARMPL-NEXT:    .cfi_offset w30, -16
+; CHECK-ARMPL-NEXT:    mov v16.16b, v2.16b
+; CHECK-ARMPL-NEXT:    bl armpl_vpowq_f32
+; CHECK-ARMPL-NEXT:    fmov v1.4s, #5.00000000
+; CHECK-ARMPL-NEXT:    fcmge v1.4s, v1.4s, v16.4s
+; CHECK-ARMPL-NEXT:    bif v0.16b, v16.16b, v1.16b
+; CHECK-ARMPL-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
+; CHECK-ARMPL-NEXT:    ret
+  %pow = call fast <4 x float> @llvm.pow.v4f32(<4 x float> %a, <4 x float> %b)
+  %fcmp = fcmp fast ole <4 x float> %c, splat (float 5.0e+0)
+  %res = select <4 x i1> %fcmp, <4 x float> %pow, <4 x float> %c
+  ret <4 x float> %res
+}
+
+; Negative tests
+
+; We don't have a vector library pow function for half types
+define <8 x half> @foo_true_is_pow_v8f16(<8 x half> %a, <8 x half> %b, <8 x half> %c, <8 x half> %d) {
+; CHECK-ARMPL-LABEL: foo_true_is_pow_v8f16:
+; CHECK-ARMPL:       // %bb.0:
+; CHECK-ARMPL-NEXT:    sub sp, sp, #80
+; CHECK-ARMPL-NEXT:    str x30, [sp, #64] // 8-byte Spill
+; CHECK-ARMPL-NEXT:    .cfi_def_cfa_offset 80
+; CHECK-ARMPL-NEXT:    .cfi_offset w30, -16
+; CHECK-ARMPL-NEXT:    stp q2, q0, [sp] // 32-byte Folded Spill
+; CHECK-ARMPL-NEXT:    mov h3, v0.h[1]
+; CHECK-ARMPL-NEXT:    mov h2, v1.h[1]
+; CHECK-ARMPL-NEXT:    str q1, [sp, #32] // 16-byte Spill
+; CHECK-ARMPL-NEXT:    fcvt s0, h3
+; CHECK-ARMPL-NEXT:    fcvt s1, h2
+; CHECK-ARMPL-NEXT:    bl powf
+; CHECK-ARMPL-NEXT:    fcvt h0, s0
+; CHECK-ARMPL-NEXT:    str q0, [sp, #48] // 16-byte Spill
+; CHECK-ARMPL-NEXT:    ldp q0, q1, [sp, #16] // 32-byte Folded Reload
+; CHECK-ARMPL-NEXT:    fcvt s0, h0
+; CHECK-ARMPL-NEXT:    fcvt s1, h1
+; CHECK-ARMPL-NEXT:    bl powf
+; CHECK-ARMPL-NEXT:    fcvt h0, s0
+; CHECK-ARMPL-NEXT:    ldp q1, q3, [sp, #32] // 32-byte Folded Reload
+; CHECK-ARMPL-NEXT:    mov h1, v1.h[2]
+; CHECK-ARMPL-NEXT:    mov v0.h[1], v3.h[0]
+; CHECK-ARMPL-NEXT:    fcvt s1, h1
+; CHECK-ARMPL-NEXT:    str q0, [sp, #48] // 16-byte Spill
+; CHECK-ARMPL-NEXT:    ldr q0, [sp, #16] // 16-byte Reload
+; CHECK-ARMPL-NEXT:    mov h0, v0.h[2]
+; CHECK-ARMPL-NEXT:    fcvt s0, h0
+; CHECK-ARMPL-NEXT:    bl powf
+; CHECK-ARMPL-NEXT:    fcvt h0, s0
+; CHECK-ARMPL-NEXT:    ldr q1, [sp, #48] // 16-byte Reload
+; CHECK-ARMPL-NEXT:    mov v1.h[2], v0.h[0]
+; CHECK-ARMPL-NEXT:    str q1, [sp, #48] // 16-byte Spill
+; CHECK-ARMPL-NEXT:    ldp q0, q1, [sp, #16] // 32-byte Folded Reload
+; CHECK-ARMPL-NEXT:    mov h0, v0.h[3]
+; CHECK-ARMPL-NEXT:    mov h1, v1.h[3]
+; CHECK-ARMPL-NEXT:    fcvt s0, h0
+; CHECK-ARMPL-NEXT:    fcvt s1, h1
+; CHECK-ARMPL-NEXT:    bl powf
+; CHECK-ARMPL-NEXT:    fcvt h0, s0
+; CHECK-ARMPL-NEXT:    ldr q1, [sp, #48] // 16-byte Reload
+; CHECK-ARMPL-NEXT:    mov v1.h[3], v0.h[0]
+; CHECK-ARMPL-NEXT:    str q1, [sp, #48] // 16-byte Spill
+; CHECK-ARMPL-NEXT:    ldp q0, q1, [sp, #16] // 32-byte Folded Reload
+; CHECK-ARMPL-NEXT:    mov h0, v0.h[4]
+; CHECK-ARMPL-NEXT:    mov h1, v1.h[4]
+; CHECK-ARMPL-NEXT:    fcvt s0, h0
+; CHECK-ARMPL-NEXT:    fcvt s1, h1
+; CHECK-ARMPL-NEXT:    bl powf
+; CHECK-ARMPL-NEXT:    fcvt h0, s0
+; CHECK-ARMPL-NEXT:    ldr q1, [sp, #48] // 16-byte Reload
+; CHECK-ARMPL-NEXT:    mov v1.h[4], v0.h[0]
+; CHECK-ARMPL-NEXT:    str q1, [sp, #48] // 16-byte Spill
+; CHECK-ARMPL-NEXT:    ldp q0, q1, [sp, #16] // 32-byte Folded Reload
+; CHECK-ARMPL-NEXT:    mov h0, v0.h[5]
+; CHECK-ARMPL-NEXT:    mov h1, v1.h[5]
+; CHECK-ARMPL-NEXT:    fcvt s0, h0
+; CHECK-ARMPL-NEXT:    fcvt s1, h1
+; CHECK-ARMPL-NEXT:    bl powf
+; CHECK-ARMPL-NEXT:    fcvt h0, s0
+; CHECK-ARMPL-NEXT:    ldr q1, [sp, #48] // 16-byte Reload
+; CHECK-ARMPL-NEXT:    mov v1.h[5], v0.h[0]
+; CHECK-ARMPL-NEXT:    str q1, [sp, #48] // 16-byte Spill
+; CHECK-ARMPL-NEXT:    ldp q0, q1, [sp, #16] // 32-byte Folded Reload
+; CHECK-ARMPL-NEXT:    mov h0, v0.h[6]
+; CHECK-ARMPL-NEXT:    mov h1, v1.h[6]
+; CHECK-ARMPL-NEXT:    fcvt s0, h0
+; CHECK-ARMPL-NEXT:    fcvt s1, h1
+; CHECK-ARMPL-NEXT:    bl powf
+; CHECK-ARMPL-NEXT:    fcvt h0, s0
+; CHECK-ARMPL-NEXT:    ldr q1, [sp, #48] // 16-byte Reload
+; CHECK-ARMPL-NEXT:    mov v1.h[6], v0.h[0]
+; CHECK-ARMPL-NEXT:    str q1, [sp, #48] // 16-byte Spill
+; CHECK-ARMPL-NEXT:    ldp q0, q1, [sp, #16] // 32-byte Folded Reload
+; CHECK-ARMPL-NEXT:    mov h0, v0.h[7]
+; CHECK-ARMPL-NEXT:    mov h1, v1.h[7]
+; CHECK-ARMPL-NEXT:    fcvt s0, h0
+; CHECK-ARMPL-NEXT:    fcvt s1, h1
+; CHECK-ARMPL-NEXT:    bl powf
+; CHECK-ARMPL-NEXT:    ldr q2, [sp] // 16-byte Reload
+; CHECK-ARMPL-NEXT:    fcvt h0, s0
+; CHECK-ARMPL-NEXT:    ldr q3, [sp, #48] // 16-byte Reload
+; CHECK-ARMPL-NEXT:    ldr x30, [sp, #64] // 8-byte Reload
+; CHECK-ARMPL-NEXT:    fcmle v1.8h, v2.8h, #0.0
+; CHECK-ARMPL-NEXT:    mov v3.h[7], v0.h[0]
+; CHECK-ARMPL-NEXT:    mov v0.16b, v1.16b
+; CHECK-ARMPL-NEXT:    bsl v0.16b, v3.16b, v2.16b
+; CHECK-ARMPL-NEXT:    add sp, sp, #80
+; CHECK-ARMPL-NEXT:    ret
+  %pow = call fast <8 x half> @llvm.pow.v8f16(<8 x half> %a, <8 x half> %b)
+  %fcmp = fcmp fast ole <8 x half> %c, zeroinitializer
+  %res = select <8 x i1> %fcmp, <8 x half> %pow, <8 x half> %c
+  ret <8 x half> %res
+}
+
+define <4 x float> @foo_pow_mul_use_v4f32(<4 x float> %a, <4 x float> %b, <4 x float> %c, <4 x float> %d) {
+; CHECK-ARMPL-LABEL: foo_pow_mul_use_v4f32:
+; CHECK-ARMPL:       // %bb.0:
+; CHECK-ARMPL-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
+; CHECK-ARMPL-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-ARMPL-NEXT:    .cfi_offset w30, -16
+; CHECK-ARMPL-NEXT:    mov v16.16b, v2.16b
+; CHECK-ARMPL-NEXT:    bl armpl_vpowq_f32
+; CHECK-ARMPL-NEXT:    fmov v1.4s, #5.00000000
+; CHECK-ARMPL-NEXT:    fcmge v1.4s, v1.4s, v16.4s
+; CHECK-ARMPL-NEXT:    bsl v1.16b, v16.16b, v0.16b
+; CHECK-ARMPL-NEXT:    fadd v0.4s, v1.4s, v0.4s
+; CHECK-ARMPL-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
+; CHECK-ARMPL-NEXT:    ret
+  %pow = call fast <4 x float> @llvm.pow.v4f32(<4 x float> %a, <4 x float> %b)
+  %fcmp = fcmp fast ole <4 x float> %c, splat (float 5.0e+0)
+  %sel = select <4 x i1> %fcmp, <4 x float> %c, <4 x float> %pow
+  %res = fadd <4 x float> %sel, %pow
+  ret <4 x float> %res
+}

>From 2844da9a0e7f95ebd9518d10a6556995469d2eeb Mon Sep 17 00:00:00 2001
From: David Sherwood <david.sherwood at arm.com>
Date: Mon, 20 Apr 2026 13:42:35 +0000
Subject: [PATCH 2/3] [AArch64] Sanitise pow inputs using a target DAG combine

Sometimes we see LLVM IR like this:

  %pow = call fast <4 x float> @llvm.pow.v4f32(...)
  %fcmp = fcmp fast ...
  %res = select <4 x i1> %fcmp, <4 x float> %val, <4 x float> %pow

where the pow intrinsic is called unconditionally, but only certain
lanes of the result are used. In fact, LLVM actively encourages code
like this due to the intrinsic being marked as safe to speculatively
execute. However, we know when using certain vector libraries like
ArmPL that this can be very costly if the unused lanes would take
the pow call down an expensive path. For example, if an input to
pow is a special value (inf, NaN, -0) then it triggers slow special
case handling, and ultimately the result is going to be ignored
anyway. For this reason we prefer to sanitise the pow input to
use 'safe' values when we know the result is going to be discarded.
The above example LLVM IR would then look like

  %fcmp = fcmp fast ...
  %sel = select <4 x i1>, <4 x float> splat(float 1.0), ...
  %pow = call fast <4 x float> @llvm.pow.v4f32(<4 x float> %sel, ...)
  %res = select <4 x i1> %fcmp, <4 x float> %val, <4 x float> %pow

where the value 1.0 is chosen due to the fact pow is known to always
return 1.0 for all powers.
---
 .../Target/AArch64/AArch64ISelLowering.cpp    | 63 +++++++++++++++++++
 llvm/test/CodeGen/AArch64/sanitize_vec_pow.ll | 61 +++++++++---------
 2 files changed, 94 insertions(+), 30 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 0ea5b2986e9c0..c6048e84f426b 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -27771,6 +27771,65 @@ static SDValue trySwapVSelectOperands(SDNode *N, SelectionDAG &DAG) {
                      {InverseSetCC, SelectB, SelectA});
 }
 
+static SDValue performVselectPowCombine(SDNode *N,
+                                        TargetLowering::DAGCombinerInfo &DCI) {
+  SDValue Cond = N->getOperand(0);
+  SDValue TrueVal = N->getOperand(1);
+  SDValue FalseVal = N->getOperand(2);
+  bool TrueValIsPow = TrueVal.getOpcode() == ISD::FPOW;
+  bool FalseValIsPow = FalseVal.getOpcode() == ISD::FPOW;
+
+  // If both inputs are pow we could equally remove the select and simply
+  // select between pow inputs instead.
+  if (TrueValIsPow && FalseValIsPow)
+    return SDValue();
+
+  if ((TrueValIsPow && !TrueVal.hasOneUse()) ||
+      (FalseValIsPow && !FalseVal.hasOneUse()))
+    return SDValue();
+
+  EVT VT = N->getValueType(0);
+  RTLIB::Libcall LC = RTLIB::getPOW(VT);
+  SelectionDAG &DAG = DCI.DAG;
+  auto &TLI = DAG.getTargetLoweringInfo();
+  bool HasLibCall =
+      TLI.getLibcallLoweringInfo().getLibcallImpl(LC) != RTLIB::Unsupported;
+  if (!HasLibCall)
+    return SDValue();
+
+  // TODO: For scalable vectors we should really just be able to pass in the
+  // mask when lowering FPOW to a call instruction.
+
+  // For a given call pow(x, y) when x=1.0 it is guaranteed to return 1.0 for
+  // any value of y.
+  SDLoc DL(N);
+  SDValue One = DAG.getConstantFP(1.0, DL, VT.getVectorElementType());
+  SDValue SplatOne = DAG.getSplatVector(VT, DL, One);
+
+  SDValue OldPow = TrueValIsPow ? TrueVal : FalseVal;
+  SDValue OldPowArg0 = OldPow->getOperand(0);
+
+  // Bail out if argument 0 is already a select.
+  if (OldPowArg0.getOpcode() == ISD::VSELECT)
+    return SDValue();
+
+  SDValue NewPowArg0;
+  if (TrueValIsPow)
+    NewPowArg0 = DAG.getNode(ISD::VSELECT, DL, VT, Cond, OldPowArg0, SplatOne);
+  else
+    NewPowArg0 = DAG.getNode(ISD::VSELECT, DL, VT, Cond, SplatOne, OldPowArg0);
+  SDValue NewPow =
+      DAG.getNode(ISD::FPOW, DL, VT, NewPowArg0, OldPow->getOperand(1));
+  NewPow->setFlags(OldPow->getFlags());
+
+  SDValue Res;
+  if (TrueValIsPow)
+    Res = DAG.getNode(ISD::VSELECT, DL, VT, Cond, NewPow, FalseVal);
+  else
+    Res = DAG.getNode(ISD::VSELECT, DL, VT, Cond, TrueVal, NewPow);
+  return Res;
+}
+
 // vselect (v1i1 setcc) ->
 //     vselect (v1iXX setcc)  (XX is the size of the compared operand type)
 // FIXME: Currently the type legalizer can't handle VSELECT having v1i1 as
@@ -27858,6 +27917,10 @@ static SDValue performVSelectCombine(SDNode *N,
     }
   }
 
+  if ((IfTrue.getOpcode() == ISD::FPOW || IfFalse.getOpcode() == ISD::FPOW))
+    if (SDValue R = performVselectPowCombine(N, DCI))
+      return R;
+
   EVT CmpVT = N0.getOperand(0).getValueType();
   if (N0.getOpcode() != ISD::SETCC ||
       CCVT.getVectorElementCount() != ElementCount::getFixed(1) ||
diff --git a/llvm/test/CodeGen/AArch64/sanitize_vec_pow.ll b/llvm/test/CodeGen/AArch64/sanitize_vec_pow.ll
index c4e6a0c74e92a..179d9a92f9b17 100644
--- a/llvm/test/CodeGen/AArch64/sanitize_vec_pow.ll
+++ b/llvm/test/CodeGen/AArch64/sanitize_vec_pow.ll
@@ -8,10 +8,12 @@ define <4 x float> @foo_false_is_pow_v4f32(<4 x float> %a, <4 x float> %b, <4 x
 ; CHECK-ARMPL-NEXT:    .cfi_def_cfa_offset 16
 ; CHECK-ARMPL-NEXT:    .cfi_offset w30, -16
 ; CHECK-ARMPL-NEXT:    mov v16.16b, v2.16b
+; CHECK-ARMPL-NEXT:    fmov v2.4s, #5.00000000
+; CHECK-ARMPL-NEXT:    fcmge v17.4s, v2.4s, v16.4s
+; CHECK-ARMPL-NEXT:    fmov v2.4s, #1.00000000
+; CHECK-ARMPL-NEXT:    bit v0.16b, v2.16b, v17.16b
 ; CHECK-ARMPL-NEXT:    bl armpl_vpowq_f32
-; CHECK-ARMPL-NEXT:    fmov v1.4s, #5.00000000
-; CHECK-ARMPL-NEXT:    fcmge v1.4s, v1.4s, v16.4s
-; CHECK-ARMPL-NEXT:    bit v0.16b, v16.16b, v1.16b
+; CHECK-ARMPL-NEXT:    bit v0.16b, v16.16b, v17.16b
 ; CHECK-ARMPL-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
 ; CHECK-ARMPL-NEXT:    ret
   %pow = call fast <4 x float> @llvm.pow.v4f32(<4 x float> %a, <4 x float> %b)
@@ -27,11 +29,12 @@ define <2 x double> @foo_false_is_pow_v2f64(<2 x double> %a, <2 x double> %b, <2
 ; CHECK-ARMPL-NEXT:    .cfi_def_cfa_offset 16
 ; CHECK-ARMPL-NEXT:    .cfi_offset w30, -16
 ; CHECK-ARMPL-NEXT:    mov v16.16b, v3.16b
-; CHECK-ARMPL-NEXT:    mov v17.16b, v2.16b
+; CHECK-ARMPL-NEXT:    fmov v3.2d, #5.00000000
+; CHECK-ARMPL-NEXT:    fcmge v17.2d, v3.2d, v2.2d
+; CHECK-ARMPL-NEXT:    fmov v2.2d, #1.00000000
+; CHECK-ARMPL-NEXT:    bit v0.16b, v2.16b, v17.16b
 ; CHECK-ARMPL-NEXT:    bl armpl_vpowq_f64
-; CHECK-ARMPL-NEXT:    fmov v1.2d, #5.00000000
-; CHECK-ARMPL-NEXT:    fcmge v1.2d, v1.2d, v17.2d
-; CHECK-ARMPL-NEXT:    bit v0.16b, v16.16b, v1.16b
+; CHECK-ARMPL-NEXT:    bit v0.16b, v16.16b, v17.16b
 ; CHECK-ARMPL-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
 ; CHECK-ARMPL-NEXT:    ret
   %pow = call fast <2 x double> @llvm.pow.v2f64(<2 x double> %a, <2 x double> %b)
@@ -51,15 +54,15 @@ define <vscale x 4 x float> @foo_false_is_pow_nxv4f32(<vscale x 4 x float> %a, <
 ; CHECK-ARMPL-NEXT:    .cfi_offset w30, -8
 ; CHECK-ARMPL-NEXT:    .cfi_offset w29, -16
 ; CHECK-ARMPL-NEXT:    .cfi_escape 0x10, 0x48, 0x09, 0x92, 0x2e, 0x00, 0x11, 0x78, 0x1e, 0x22, 0x40, 0x1c // $d8 @ cfa - 8 * VG - 16
-; CHECK-ARMPL-NEXT:    ptrue p4.s
+; CHECK-ARMPL-NEXT:    fmov z3.s, #5.00000000
 ; CHECK-ARMPL-NEXT:    mov z8.d, z2.d
-; CHECK-ARMPL-NEXT:    mov p0.b, p4.b
+; CHECK-ARMPL-NEXT:    ptrue p0.s
+; CHECK-ARMPL-NEXT:    fcmge p4.s, p0/z, z3.s, z2.s
+; CHECK-ARMPL-NEXT:    fmov z0.s, p4/m, #1.00000000
 ; CHECK-ARMPL-NEXT:    bl armpl_svpow_f32_x
-; CHECK-ARMPL-NEXT:    fmov z1.s, #5.00000000
-; CHECK-ARMPL-NEXT:    fcmge p0.s, p4/z, z1.s, z8.s
-; CHECK-ARMPL-NEXT:    ldr p4, [sp, #7, mul vl] // 2-byte Reload
-; CHECK-ARMPL-NEXT:    mov z0.s, p0/m, z8.s
+; CHECK-ARMPL-NEXT:    mov z0.s, p4/m, z8.s
 ; CHECK-ARMPL-NEXT:    ldr z8, [sp, #1, mul vl] // 16-byte Folded Reload
+; CHECK-ARMPL-NEXT:    ldr p4, [sp, #7, mul vl] // 2-byte Reload
 ; CHECK-ARMPL-NEXT:    addvl sp, sp, #2
 ; CHECK-ARMPL-NEXT:    ldp x29, x30, [sp], #16 // 16-byte Folded Reload
 ; CHECK-ARMPL-NEXT:    ret
@@ -73,27 +76,23 @@ define <vscale x 2 x double> @foo_false_is_pow_nxv2f64(<vscale x 2 x double> %a,
 ; CHECK-ARMPL-LABEL: foo_false_is_pow_nxv2f64:
 ; CHECK-ARMPL:       // %bb.0:
 ; CHECK-ARMPL-NEXT:    stp x29, x30, [sp, #-16]! // 16-byte Folded Spill
-; CHECK-ARMPL-NEXT:    addvl sp, sp, #-3
+; CHECK-ARMPL-NEXT:    addvl sp, sp, #-2
 ; CHECK-ARMPL-NEXT:    str p4, [sp, #7, mul vl] // 2-byte Spill
-; CHECK-ARMPL-NEXT:    str z9, [sp, #1, mul vl] // 16-byte Folded Spill
-; CHECK-ARMPL-NEXT:    str z8, [sp, #2, mul vl] // 16-byte Folded Spill
-; CHECK-ARMPL-NEXT:    .cfi_escape 0x0f, 0x08, 0x8f, 0x10, 0x92, 0x2e, 0x00, 0x48, 0x1e, 0x22 // sp + 16 + 24 * VG
+; CHECK-ARMPL-NEXT:    str z8, [sp, #1, mul vl] // 16-byte Folded Spill
+; CHECK-ARMPL-NEXT:    .cfi_escape 0x0f, 0x08, 0x8f, 0x10, 0x92, 0x2e, 0x00, 0x40, 0x1e, 0x22 // sp + 16 + 16 * VG
 ; CHECK-ARMPL-NEXT:    .cfi_offset w30, -8
 ; CHECK-ARMPL-NEXT:    .cfi_offset w29, -16
 ; CHECK-ARMPL-NEXT:    .cfi_escape 0x10, 0x48, 0x09, 0x92, 0x2e, 0x00, 0x11, 0x78, 0x1e, 0x22, 0x40, 0x1c // $d8 @ cfa - 8 * VG - 16
-; CHECK-ARMPL-NEXT:    .cfi_escape 0x10, 0x49, 0x09, 0x92, 0x2e, 0x00, 0x11, 0x70, 0x1e, 0x22, 0x40, 0x1c // $d9 @ cfa - 16 * VG - 16
-; CHECK-ARMPL-NEXT:    ptrue p4.d
 ; CHECK-ARMPL-NEXT:    mov z8.d, z3.d
-; CHECK-ARMPL-NEXT:    mov z9.d, z2.d
-; CHECK-ARMPL-NEXT:    mov p0.b, p4.b
+; CHECK-ARMPL-NEXT:    fmov z3.d, #5.00000000
+; CHECK-ARMPL-NEXT:    ptrue p0.d
+; CHECK-ARMPL-NEXT:    fcmge p4.d, p0/z, z3.d, z2.d
+; CHECK-ARMPL-NEXT:    fmov z0.d, p4/m, #1.00000000
 ; CHECK-ARMPL-NEXT:    bl armpl_svpow_f64_x
-; CHECK-ARMPL-NEXT:    fmov z1.d, #5.00000000
-; CHECK-ARMPL-NEXT:    fcmge p0.d, p4/z, z1.d, z9.d
-; CHECK-ARMPL-NEXT:    ldr z9, [sp, #1, mul vl] // 16-byte Folded Reload
+; CHECK-ARMPL-NEXT:    mov z0.d, p4/m, z8.d
+; CHECK-ARMPL-NEXT:    ldr z8, [sp, #1, mul vl] // 16-byte Folded Reload
 ; CHECK-ARMPL-NEXT:    ldr p4, [sp, #7, mul vl] // 2-byte Reload
-; CHECK-ARMPL-NEXT:    mov z0.d, p0/m, z8.d
-; CHECK-ARMPL-NEXT:    ldr z8, [sp, #2, mul vl] // 16-byte Folded Reload
-; CHECK-ARMPL-NEXT:    addvl sp, sp, #3
+; CHECK-ARMPL-NEXT:    addvl sp, sp, #2
 ; CHECK-ARMPL-NEXT:    ldp x29, x30, [sp], #16 // 16-byte Folded Reload
 ; CHECK-ARMPL-NEXT:    ret
   %pow = call fast <vscale x 2 x double> @llvm.pow.nxv2f64(<vscale x 2 x double> %a, <vscale x 2 x double> %b)
@@ -110,10 +109,12 @@ define <4 x float> @foo_true_is_pow_v4f32(<4 x float> %a, <4 x float> %b, <4 x f
 ; CHECK-ARMPL-NEXT:    .cfi_def_cfa_offset 16
 ; CHECK-ARMPL-NEXT:    .cfi_offset w30, -16
 ; CHECK-ARMPL-NEXT:    mov v16.16b, v2.16b
+; CHECK-ARMPL-NEXT:    fmov v2.4s, #5.00000000
+; CHECK-ARMPL-NEXT:    fcmge v17.4s, v2.4s, v16.4s
+; CHECK-ARMPL-NEXT:    fmov v2.4s, #1.00000000
+; CHECK-ARMPL-NEXT:    bif v0.16b, v2.16b, v17.16b
 ; CHECK-ARMPL-NEXT:    bl armpl_vpowq_f32
-; CHECK-ARMPL-NEXT:    fmov v1.4s, #5.00000000
-; CHECK-ARMPL-NEXT:    fcmge v1.4s, v1.4s, v16.4s
-; CHECK-ARMPL-NEXT:    bif v0.16b, v16.16b, v1.16b
+; CHECK-ARMPL-NEXT:    bif v0.16b, v16.16b, v17.16b
 ; CHECK-ARMPL-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
 ; CHECK-ARMPL-NEXT:    ret
   %pow = call fast <4 x float> @llvm.pow.v4f32(<4 x float> %a, <4 x float> %b)

>From 797647f5b47c872ccdeb9fb9e6a871ad2f6bd5cc Mon Sep 17 00:00:00 2001
From: David Sherwood <david.sherwood at arm.com>
Date: Thu, 23 Apr 2026 11:46:07 +0000
Subject: [PATCH 3/3] Address review comments

---
 .../Target/AArch64/AArch64ISelLowering.cpp    | 36 +++++++---------
 llvm/test/CodeGen/AArch64/sanitize_vec_pow.ll | 42 ++++++-------------
 2 files changed, 27 insertions(+), 51 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index c6048e84f426b..d89ea80c1baca 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -27773,6 +27773,7 @@ static SDValue trySwapVSelectOperands(SDNode *N, SelectionDAG &DAG) {
 
 static SDValue performVselectPowCombine(SDNode *N,
                                         TargetLowering::DAGCombinerInfo &DCI) {
+  assert(N->getOpcode() == ISD::VSELECT && "Expected VSELECT opcode");
   SDValue Cond = N->getOperand(0);
   SDValue TrueVal = N->getOperand(1);
   SDValue FalseVal = N->getOperand(2);
@@ -27781,7 +27782,7 @@ static SDValue performVselectPowCombine(SDNode *N,
 
   // If both inputs are pow we could equally remove the select and simply
   // select between pow inputs instead.
-  if (TrueValIsPow && FalseValIsPow)
+  if (TrueValIsPow == FalseValIsPow)
     return SDValue();
 
   if ((TrueValIsPow && !TrueVal.hasOneUse()) ||
@@ -27797,37 +27798,29 @@ static SDValue performVselectPowCombine(SDNode *N,
   if (!HasLibCall)
     return SDValue();
 
-  // TODO: For scalable vectors we should really just be able to pass in the
-  // mask when lowering FPOW to a call instruction.
-
-  // For a given call pow(x, y) when x=1.0 it is guaranteed to return 1.0 for
-  // any value of y.
-  SDLoc DL(N);
-  SDValue One = DAG.getConstantFP(1.0, DL, VT.getVectorElementType());
-  SDValue SplatOne = DAG.getSplatVector(VT, DL, One);
-
   SDValue OldPow = TrueValIsPow ? TrueVal : FalseVal;
   SDValue OldPowArg0 = OldPow->getOperand(0);
 
-  // Bail out if argument 0 is already a select.
+  // Bail out if argument 0 is already a select, in order to avoid an infinite
+  // combine loop.
   if (OldPowArg0.getOpcode() == ISD::VSELECT)
     return SDValue();
 
+  // For a given call pow(x, y) when x=1.0 it is guaranteed to return 1.0 for
+  // any value of y.
+  SDLoc DL(N);
+  SDValue SplatOne = DAG.getConstantFP(1.0, DL, VT);
   SDValue NewPowArg0;
   if (TrueValIsPow)
     NewPowArg0 = DAG.getNode(ISD::VSELECT, DL, VT, Cond, OldPowArg0, SplatOne);
   else
     NewPowArg0 = DAG.getNode(ISD::VSELECT, DL, VT, Cond, SplatOne, OldPowArg0);
-  SDValue NewPow =
-      DAG.getNode(ISD::FPOW, DL, VT, NewPowArg0, OldPow->getOperand(1));
-  NewPow->setFlags(OldPow->getFlags());
+  SDValue NewPow = DAG.getNode(ISD::FPOW, DL, VT, NewPowArg0,
+                               OldPow->getOperand(1), OldPow->getFlags());
 
-  SDValue Res;
   if (TrueValIsPow)
-    Res = DAG.getNode(ISD::VSELECT, DL, VT, Cond, NewPow, FalseVal);
-  else
-    Res = DAG.getNode(ISD::VSELECT, DL, VT, Cond, TrueVal, NewPow);
-  return Res;
+    return DAG.getNode(ISD::VSELECT, DL, VT, Cond, NewPow, FalseVal);
+  return DAG.getNode(ISD::VSELECT, DL, VT, Cond, TrueVal, NewPow);
 }
 
 // vselect (v1i1 setcc) ->
@@ -27917,9 +27910,8 @@ static SDValue performVSelectCombine(SDNode *N,
     }
   }
 
-  if ((IfTrue.getOpcode() == ISD::FPOW || IfFalse.getOpcode() == ISD::FPOW))
-    if (SDValue R = performVselectPowCombine(N, DCI))
-      return R;
+  if (SDValue R = performVselectPowCombine(N, DCI))
+    return R;
 
   EVT CmpVT = N0.getOperand(0).getValueType();
   if (N0.getOpcode() != ISD::SETCC ||
diff --git a/llvm/test/CodeGen/AArch64/sanitize_vec_pow.ll b/llvm/test/CodeGen/AArch64/sanitize_vec_pow.ll
index 179d9a92f9b17..3267e2a3764ea 100644
--- a/llvm/test/CodeGen/AArch64/sanitize_vec_pow.ll
+++ b/llvm/test/CodeGen/AArch64/sanitize_vec_pow.ll
@@ -1,12 +1,10 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
 ; RUN: llc -mattr=+neon,+sve -mtriple=aarch64-linux-gnu --vector-library=ArmPL < %s -o - | FileCheck --check-prefix=CHECK-ARMPL %s
 
-define <4 x float> @foo_false_is_pow_v4f32(<4 x float> %a, <4 x float> %b, <4 x float> %c, <4 x float> %d) {
+define <4 x float> @foo_false_is_pow_v4f32(<4 x float> %a, <4 x float> %b, <4 x float> %c, <4 x float> %d) nounwind {
 ; CHECK-ARMPL-LABEL: foo_false_is_pow_v4f32:
 ; CHECK-ARMPL:       // %bb.0:
 ; CHECK-ARMPL-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
-; CHECK-ARMPL-NEXT:    .cfi_def_cfa_offset 16
-; CHECK-ARMPL-NEXT:    .cfi_offset w30, -16
 ; CHECK-ARMPL-NEXT:    mov v16.16b, v2.16b
 ; CHECK-ARMPL-NEXT:    fmov v2.4s, #5.00000000
 ; CHECK-ARMPL-NEXT:    fcmge v17.4s, v2.4s, v16.4s
@@ -22,12 +20,10 @@ define <4 x float> @foo_false_is_pow_v4f32(<4 x float> %a, <4 x float> %b, <4 x
   ret <4 x float> %res
 }
 
-define <2 x double> @foo_false_is_pow_v2f64(<2 x double> %a, <2 x double> %b, <2 x double> %c, <2 x double> %d) {
+define <2 x double> @foo_false_is_pow_v2f64(<2 x double> %a, <2 x double> %b, <2 x double> %c, <2 x double> %d) nounwind {
 ; CHECK-ARMPL-LABEL: foo_false_is_pow_v2f64:
 ; CHECK-ARMPL:       // %bb.0:
 ; CHECK-ARMPL-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
-; CHECK-ARMPL-NEXT:    .cfi_def_cfa_offset 16
-; CHECK-ARMPL-NEXT:    .cfi_offset w30, -16
 ; CHECK-ARMPL-NEXT:    mov v16.16b, v3.16b
 ; CHECK-ARMPL-NEXT:    fmov v3.2d, #5.00000000
 ; CHECK-ARMPL-NEXT:    fcmge v17.2d, v3.2d, v2.2d
@@ -43,20 +39,18 @@ define <2 x double> @foo_false_is_pow_v2f64(<2 x double> %a, <2 x double> %b, <2
   ret <2 x double> %res
 }
 
-define <vscale x 4 x float> @foo_false_is_pow_nxv4f32(<vscale x 4 x float> %a, <vscale x 4 x float> %b, <vscale x 4 x float> %c, <vscale x 4 x float> %d) {
+; TODO: For scalable vectors we should really just be able to pass in the
+; mask when lowering FPOW to a call instruction.
+define <vscale x 4 x float> @foo_false_is_pow_nxv4f32(<vscale x 4 x float> %a, <vscale x 4 x float> %b, <vscale x 4 x float> %c, <vscale x 4 x float> %d) nounwind {
 ; CHECK-ARMPL-LABEL: foo_false_is_pow_nxv4f32:
 ; CHECK-ARMPL:       // %bb.0:
 ; CHECK-ARMPL-NEXT:    stp x29, x30, [sp, #-16]! // 16-byte Folded Spill
 ; CHECK-ARMPL-NEXT:    addvl sp, sp, #-2
+; CHECK-ARMPL-NEXT:    fmov z3.s, #5.00000000
 ; CHECK-ARMPL-NEXT:    str p4, [sp, #7, mul vl] // 2-byte Spill
+; CHECK-ARMPL-NEXT:    ptrue p0.s
 ; CHECK-ARMPL-NEXT:    str z8, [sp, #1, mul vl] // 16-byte Folded Spill
-; CHECK-ARMPL-NEXT:    .cfi_escape 0x0f, 0x08, 0x8f, 0x10, 0x92, 0x2e, 0x00, 0x40, 0x1e, 0x22 // sp + 16 + 16 * VG
-; CHECK-ARMPL-NEXT:    .cfi_offset w30, -8
-; CHECK-ARMPL-NEXT:    .cfi_offset w29, -16
-; CHECK-ARMPL-NEXT:    .cfi_escape 0x10, 0x48, 0x09, 0x92, 0x2e, 0x00, 0x11, 0x78, 0x1e, 0x22, 0x40, 0x1c // $d8 @ cfa - 8 * VG - 16
-; CHECK-ARMPL-NEXT:    fmov z3.s, #5.00000000
 ; CHECK-ARMPL-NEXT:    mov z8.d, z2.d
-; CHECK-ARMPL-NEXT:    ptrue p0.s
 ; CHECK-ARMPL-NEXT:    fcmge p4.s, p0/z, z3.s, z2.s
 ; CHECK-ARMPL-NEXT:    fmov z0.s, p4/m, #1.00000000
 ; CHECK-ARMPL-NEXT:    bl armpl_svpow_f32_x
@@ -72,20 +66,16 @@ define <vscale x 4 x float> @foo_false_is_pow_nxv4f32(<vscale x 4 x float> %a, <
   ret <vscale x 4 x float> %res
 }
 
-define <vscale x 2 x double> @foo_false_is_pow_nxv2f64(<vscale x 2 x double> %a, <vscale x 2 x double> %b, <vscale x 2 x double> %c, <vscale x 2 x double> %d) {
+define <vscale x 2 x double> @foo_false_is_pow_nxv2f64(<vscale x 2 x double> %a, <vscale x 2 x double> %b, <vscale x 2 x double> %c, <vscale x 2 x double> %d) nounwind {
 ; CHECK-ARMPL-LABEL: foo_false_is_pow_nxv2f64:
 ; CHECK-ARMPL:       // %bb.0:
 ; CHECK-ARMPL-NEXT:    stp x29, x30, [sp, #-16]! // 16-byte Folded Spill
 ; CHECK-ARMPL-NEXT:    addvl sp, sp, #-2
-; CHECK-ARMPL-NEXT:    str p4, [sp, #7, mul vl] // 2-byte Spill
 ; CHECK-ARMPL-NEXT:    str z8, [sp, #1, mul vl] // 16-byte Folded Spill
-; CHECK-ARMPL-NEXT:    .cfi_escape 0x0f, 0x08, 0x8f, 0x10, 0x92, 0x2e, 0x00, 0x40, 0x1e, 0x22 // sp + 16 + 16 * VG
-; CHECK-ARMPL-NEXT:    .cfi_offset w30, -8
-; CHECK-ARMPL-NEXT:    .cfi_offset w29, -16
-; CHECK-ARMPL-NEXT:    .cfi_escape 0x10, 0x48, 0x09, 0x92, 0x2e, 0x00, 0x11, 0x78, 0x1e, 0x22, 0x40, 0x1c // $d8 @ cfa - 8 * VG - 16
 ; CHECK-ARMPL-NEXT:    mov z8.d, z3.d
 ; CHECK-ARMPL-NEXT:    fmov z3.d, #5.00000000
 ; CHECK-ARMPL-NEXT:    ptrue p0.d
+; CHECK-ARMPL-NEXT:    str p4, [sp, #7, mul vl] // 2-byte Spill
 ; CHECK-ARMPL-NEXT:    fcmge p4.d, p0/z, z3.d, z2.d
 ; CHECK-ARMPL-NEXT:    fmov z0.d, p4/m, #1.00000000
 ; CHECK-ARMPL-NEXT:    bl armpl_svpow_f64_x
@@ -102,12 +92,10 @@ define <vscale x 2 x double> @foo_false_is_pow_nxv2f64(<vscale x 2 x double> %a,
 }
 
 ; Only added one variant for the true value case, since the other VFs are tested above.
-define <4 x float> @foo_true_is_pow_v4f32(<4 x float> %a, <4 x float> %b, <4 x float> %c, <4 x float> %d) {
+define <4 x float> @foo_true_is_pow_v4f32(<4 x float> %a, <4 x float> %b, <4 x float> %c, <4 x float> %d) nounwind {
 ; CHECK-ARMPL-LABEL: foo_true_is_pow_v4f32:
 ; CHECK-ARMPL:       // %bb.0:
 ; CHECK-ARMPL-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
-; CHECK-ARMPL-NEXT:    .cfi_def_cfa_offset 16
-; CHECK-ARMPL-NEXT:    .cfi_offset w30, -16
 ; CHECK-ARMPL-NEXT:    mov v16.16b, v2.16b
 ; CHECK-ARMPL-NEXT:    fmov v2.4s, #5.00000000
 ; CHECK-ARMPL-NEXT:    fcmge v17.4s, v2.4s, v16.4s
@@ -126,17 +114,15 @@ define <4 x float> @foo_true_is_pow_v4f32(<4 x float> %a, <4 x float> %b, <4 x f
 ; Negative tests
 
 ; We don't have a vector library pow function for half types
-define <8 x half> @foo_true_is_pow_v8f16(<8 x half> %a, <8 x half> %b, <8 x half> %c, <8 x half> %d) {
+define <8 x half> @foo_true_is_pow_v8f16(<8 x half> %a, <8 x half> %b, <8 x half> %c, <8 x half> %d) nounwind {
 ; CHECK-ARMPL-LABEL: foo_true_is_pow_v8f16:
 ; CHECK-ARMPL:       // %bb.0:
 ; CHECK-ARMPL-NEXT:    sub sp, sp, #80
-; CHECK-ARMPL-NEXT:    str x30, [sp, #64] // 8-byte Spill
-; CHECK-ARMPL-NEXT:    .cfi_def_cfa_offset 80
-; CHECK-ARMPL-NEXT:    .cfi_offset w30, -16
 ; CHECK-ARMPL-NEXT:    stp q2, q0, [sp] // 32-byte Folded Spill
 ; CHECK-ARMPL-NEXT:    mov h3, v0.h[1]
 ; CHECK-ARMPL-NEXT:    mov h2, v1.h[1]
 ; CHECK-ARMPL-NEXT:    str q1, [sp, #32] // 16-byte Spill
+; CHECK-ARMPL-NEXT:    str x30, [sp, #64] // 8-byte Spill
 ; CHECK-ARMPL-NEXT:    fcvt s0, h3
 ; CHECK-ARMPL-NEXT:    fcvt s1, h2
 ; CHECK-ARMPL-NEXT:    bl powf
@@ -222,12 +208,10 @@ define <8 x half> @foo_true_is_pow_v8f16(<8 x half> %a, <8 x half> %b, <8 x half
   ret <8 x half> %res
 }
 
-define <4 x float> @foo_pow_mul_use_v4f32(<4 x float> %a, <4 x float> %b, <4 x float> %c, <4 x float> %d) {
+define <4 x float> @foo_pow_mul_use_v4f32(<4 x float> %a, <4 x float> %b, <4 x float> %c, <4 x float> %d) nounwind {
 ; CHECK-ARMPL-LABEL: foo_pow_mul_use_v4f32:
 ; CHECK-ARMPL:       // %bb.0:
 ; CHECK-ARMPL-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
-; CHECK-ARMPL-NEXT:    .cfi_def_cfa_offset 16
-; CHECK-ARMPL-NEXT:    .cfi_offset w30, -16
 ; CHECK-ARMPL-NEXT:    mov v16.16b, v2.16b
 ; CHECK-ARMPL-NEXT:    bl armpl_vpowq_f32
 ; CHECK-ARMPL-NEXT:    fmov v1.4s, #5.00000000



More information about the llvm-commits mailing list