[llvm] [AMDGPU] Gate rootn(x, +-2) -> sqrt/rsqrt fold on nsz/ninf (PR #200578)
Arseniy Obolenskiy via llvm-commits
llvm-commits at lists.llvm.org
Wed Jun 10 09:02:59 PDT 2026
https://github.com/aobolensk updated https://github.com/llvm/llvm-project/pull/200578
>From f7663806c9096a153e5a01af025cbdf77f91e472 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Sat, 30 May 2026 15:52:13 +0200
Subject: [PATCH 1/3] [AMDGPU] Gate rootn(x, +-2) -> sqrt/rsqrt fold on
nsz/ninf
OpenCL rootn and IEEE sqrt/rsqrt disagree on signed-zero and -Inf:
- rootn(-0.0, 2) = +0.0 vs sqrt(-0.0) = -0.0
- rootn(-Inf, 2) = NaN vs sqrt(-Inf) = -NaN (sign differs)
- rootn(-0.0, -2) = +Inf vs 1/sqrt(-0.0) = -Inf
- rootn(-Inf, -2) = +0.0 vs 1/sqrt(-Inf) = NaN
Require the call to carry nsz and ninf before performing the rewrite. __rootn_fast keeps the unconditional fold.
---
llvm/lib/Target/AMDGPU/AMDGPULibCalls.cpp | 11 ++-
.../amdgpu-simplify-libcall-rootn-fast.ll | 12 ++-
.../AMDGPU/amdgpu-simplify-libcall-rootn.ll | 79 ++++++++++++-------
3 files changed, 65 insertions(+), 37 deletions(-)
diff --git a/llvm/lib/Target/AMDGPU/AMDGPULibCalls.cpp b/llvm/lib/Target/AMDGPU/AMDGPULibCalls.cpp
index dc4225b94b466..a27c6ddaed50b 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPULibCalls.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPULibCalls.cpp
@@ -1169,7 +1169,14 @@ bool AMDGPULibCalls::fold_rootn(FPMathOperator *FPOp, IRBuilder<> &B,
Module *M = B.GetInsertBlock()->getModule();
CallInst *CI = cast<CallInst>(FPOp);
- if (ci_opr1 == 2 &&
+
+ // rootn and sqrt disagree on signed-zero / -Inf inputs (e.g. rootn(-0.0, 2)
+ // is +0.0, sqrt(-0.0) is -0.0), so require nsz/ninf. __rootn_fast is exempt.
+ bool IsRootnFast = FInfo.getId() == AMDGPULibFunc::EI_ROOTN_FAST;
+ bool FMFOkForSqrt =
+ IsRootnFast || (FPOp->hasNoSignedZeros() && FPOp->hasNoInfs());
+
+ if (ci_opr1 == 2 && FMFOkForSqrt &&
shouldReplaceLibcallWithIntrinsic(CI,
/*AllowMinSizeF32=*/true,
/*AllowF64=*/true)) {
@@ -1207,7 +1214,7 @@ bool AMDGPULibCalls::fold_rootn(FPMathOperator *FPOp, IRBuilder<> &B,
return true;
}
- if (ci_opr1 == -2 &&
+ if (ci_opr1 == -2 && FMFOkForSqrt &&
shouldReplaceLibcallWithIntrinsic(CI,
/*AllowMinSizeF32=*/true,
/*AllowF64=*/true)) {
diff --git a/llvm/test/CodeGen/AMDGPU/amdgpu-simplify-libcall-rootn-fast.ll b/llvm/test/CodeGen/AMDGPU/amdgpu-simplify-libcall-rootn-fast.ll
index 812f56a8d6f64..a3e47386a4e41 100644
--- a/llvm/test/CodeGen/AMDGPU/amdgpu-simplify-libcall-rootn-fast.ll
+++ b/llvm/test/CodeGen/AMDGPU/amdgpu-simplify-libcall-rootn-fast.ll
@@ -89,7 +89,7 @@ define float @test_rootn_afn_f32__2(float %x) #0 {
; CHECK-LABEL: define float @test_rootn_afn_f32__2(
; CHECK-SAME: float [[X:%.*]]) #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: [[CALL:%.*]] = call afn float @llvm.sqrt.f32(float [[X]]), !fpmath [[META0:![0-9]+]]
+; CHECK-NEXT: [[CALL:%.*]] = tail call afn float @_Z12__rootn_fastfi(float [[X]], i32 2)
; CHECK-NEXT: ret float [[CALL]]
;
entry:
@@ -125,8 +125,7 @@ define float @test_rootn_afn_f32__neg2(float %x) #0 {
; CHECK-LABEL: define float @test_rootn_afn_f32__neg2(
; CHECK-SAME: float [[X:%.*]]) #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: [[TMP0:%.*]] = call contract afn float @llvm.sqrt.f32(float [[X]])
-; CHECK-NEXT: [[TMP1:%.*]] = fdiv contract afn float 1.000000e+00, [[TMP0]], !fpmath [[META0]]
+; CHECK-NEXT: [[TMP1:%.*]] = tail call afn float @_Z12__rootn_fastfi(float [[X]], i32 -2)
; CHECK-NEXT: ret float [[TMP1]]
;
entry:
@@ -138,7 +137,7 @@ define <2 x float> @test_rootn_afn_v2f32__2(<2 x float> %x) #0 {
; CHECK-LABEL: define <2 x float> @test_rootn_afn_v2f32__2(
; CHECK-SAME: <2 x float> [[X:%.*]]) #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: [[CALL:%.*]] = call afn <2 x float> @llvm.sqrt.v2f32(<2 x float> [[X]]), !fpmath [[META0]]
+; CHECK-NEXT: [[CALL:%.*]] = tail call afn <2 x float> @_Z12__rootn_fastDv2_fDv2_i(<2 x float> [[X]], <2 x i32> splat (i32 2))
; CHECK-NEXT: ret <2 x float> [[CALL]]
;
entry:
@@ -174,8 +173,7 @@ define <2 x float> @test_rootn_afn_v2f32__neg2(<2 x float> %x) #0 {
; CHECK-LABEL: define <2 x float> @test_rootn_afn_v2f32__neg2(
; CHECK-SAME: <2 x float> [[X:%.*]]) #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: [[TMP0:%.*]] = call contract afn <2 x float> @llvm.sqrt.v2f32(<2 x float> [[X]])
-; CHECK-NEXT: [[TMP1:%.*]] = fdiv contract afn <2 x float> splat (float 1.000000e+00), [[TMP0]], !fpmath [[META0]]
+; CHECK-NEXT: [[TMP1:%.*]] = tail call afn <2 x float> @_Z12__rootn_fastDv2_fDv2_i(<2 x float> [[X]], <2 x i32> splat (i32 -2))
; CHECK-NEXT: ret <2 x float> [[TMP1]]
;
entry:
@@ -293,7 +291,7 @@ define float @test__rootn_fast_f32__2(float %x) #0 {
; CHECK-LABEL: define float @test__rootn_fast_f32__2(
; CHECK-SAME: float [[X:%.*]]) #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.sqrt.f32(float [[X]]), !fpmath [[META0]]
+; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.sqrt.f32(float [[X]]), !fpmath [[META0:![0-9]+]]
; CHECK-NEXT: ret float [[CALL]]
;
entry:
diff --git a/llvm/test/CodeGen/AMDGPU/amdgpu-simplify-libcall-rootn.ll b/llvm/test/CodeGen/AMDGPU/amdgpu-simplify-libcall-rootn.ll
index 7aa35144627c2..98de3b98ff748 100644
--- a/llvm/test/CodeGen/AMDGPU/amdgpu-simplify-libcall-rootn.ll
+++ b/llvm/test/CodeGen/AMDGPU/amdgpu-simplify-libcall-rootn.ll
@@ -271,7 +271,7 @@ define half @test_rootn_f16_1(half %x) {
define half @test_rootn_f16_2(half %x) {
; CHECK-LABEL: define half @test_rootn_f16_2(
; CHECK-SAME: half [[X:%.*]]) {
-; CHECK-NEXT: [[CALL:%.*]] = call half @llvm.sqrt.f16(half [[X]]), !fpmath [[META1:![0-9]+]]
+; CHECK-NEXT: [[CALL:%.*]] = tail call half @_Z5rootnDhi(half [[X]], i32 2)
; CHECK-NEXT: ret half [[CALL]]
;
%call = tail call half @_Z5rootnDhi(half %x, i32 2)
@@ -306,8 +306,7 @@ define half @test_rootn_f16_neg1(half %x) {
define half @test_rootn_f16_neg2(half %x) {
; CHECK-LABEL: define half @test_rootn_f16_neg2(
; CHECK-SAME: half [[X:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = call contract half @llvm.sqrt.f16(half [[X]])
-; CHECK-NEXT: [[__ROOTN2RSQRT:%.*]] = fdiv contract half 1.000000e+00, [[TMP1]], !fpmath [[META1]]
+; CHECK-NEXT: [[__ROOTN2RSQRT:%.*]] = tail call half @_Z5rootnDhi(half [[X]], i32 -2)
; CHECK-NEXT: ret half [[__ROOTN2RSQRT]]
;
%call = tail call half @_Z5rootnDhi(half %x, i32 -2)
@@ -356,7 +355,7 @@ define <2 x half> @test_rootn_v2f16_1(<2 x half> %x) {
define <2 x half> @test_rootn_v2f16_2(<2 x half> %x) {
; CHECK-LABEL: define <2 x half> @test_rootn_v2f16_2(
; CHECK-SAME: <2 x half> [[X:%.*]]) {
-; CHECK-NEXT: [[CALL:%.*]] = call <2 x half> @llvm.sqrt.v2f16(<2 x half> [[X]]), !fpmath [[META1]]
+; CHECK-NEXT: [[CALL:%.*]] = tail call <2 x half> @_Z5rootnDv2_DhDv2_i(<2 x half> [[X]], <2 x i32> splat (i32 2))
; CHECK-NEXT: ret <2 x half> [[CALL]]
;
%call = tail call <2 x half> @_Z5rootnDv2_DhDv2_i(<2 x half> %x, <2 x i32> <i32 2, i32 2>)
@@ -376,8 +375,7 @@ define <2 x half> @test_rootn_v2f16_neg1(<2 x half> %x) {
define <2 x half> @test_rootn_v2f16_neg2(<2 x half> %x) {
; CHECK-LABEL: define <2 x half> @test_rootn_v2f16_neg2(
; CHECK-SAME: <2 x half> [[X:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = call contract <2 x half> @llvm.sqrt.v2f16(<2 x half> [[X]])
-; CHECK-NEXT: [[__ROOTN2RSQRT:%.*]] = fdiv contract <2 x half> splat (half 1.000000e+00), [[TMP1]], !fpmath [[META1]]
+; CHECK-NEXT: [[__ROOTN2RSQRT:%.*]] = tail call <2 x half> @_Z5rootnDv2_DhDv2_i(<2 x half> [[X]], <2 x i32> splat (i32 -2))
; CHECK-NEXT: ret <2 x half> [[__ROOTN2RSQRT]]
;
%call = tail call <2 x half> @_Z5rootnDv2_DhDv2_i(<2 x half> %x, <2 x i32> <i32 -2, i32 -2>)
@@ -618,7 +616,7 @@ define float @test_rootn_f32__y_2(float %x) {
; CHECK-LABEL: define float @test_rootn_f32__y_2(
; CHECK-SAME: float [[X:%.*]]) {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.sqrt.f32(float [[X]]), !fpmath [[META1]]
+; CHECK-NEXT: [[CALL:%.*]] = tail call float @_Z5rootnfi(float [[X]], i32 2)
; CHECK-NEXT: ret float [[CALL]]
;
entry:
@@ -630,7 +628,7 @@ define float @test_rootn_f32__y_2_flags(float %x) {
; CHECK-LABEL: define float @test_rootn_f32__y_2_flags(
; CHECK-SAME: float [[X:%.*]]) {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[CALL:%.*]] = call nnan nsz float @llvm.sqrt.f32(float [[X]]), !fpmath [[META1]]
+; CHECK-NEXT: [[CALL:%.*]] = tail call nnan nsz float @_Z5rootnfi(float [[X]], i32 2)
; CHECK-NEXT: ret float [[CALL]]
;
entry:
@@ -638,12 +636,26 @@ entry:
ret float %call
}
+; nsz+ninf are required to legalize the rootn(-0.0,2)/rootn(-Inf,2) corners,
+; so the fold should fire here.
+define float @test_rootn_f32__y_2_nsz_ninf(float %x) {
+; CHECK-LABEL: define float @test_rootn_f32__y_2_nsz_ninf(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[CALL:%.*]] = call ninf nsz float @llvm.sqrt.f32(float [[X]]), !fpmath [[META1:![0-9]+]]
+; CHECK-NEXT: ret float [[CALL]]
+;
+entry:
+ %call = tail call nsz ninf float @_Z5rootnfi(float %x, i32 2)
+ ret float %call
+}
+
; Should retain looser existing !fpmath requirements.
define float @test_rootn_f32__y_2_fpmath_3(float %x) {
; CHECK-LABEL: define float @test_rootn_f32__y_2_fpmath_3(
; CHECK-SAME: float [[X:%.*]]) {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[CALL:%.*]] = call nnan nsz float @llvm.sqrt.f32(float [[X]]), !fpmath [[META2:![0-9]+]]
+; CHECK-NEXT: [[CALL:%.*]] = tail call nnan nsz float @_Z5rootnfi(float [[X]], i32 2), !fpmath [[META2:![0-9]+]]
; CHECK-NEXT: ret float [[CALL]]
;
entry:
@@ -655,7 +667,7 @@ define <2 x float> @test_rootn_v2f32__y_2_flags(<2 x float> %x) {
; CHECK-LABEL: define <2 x float> @test_rootn_v2f32__y_2_flags(
; CHECK-SAME: <2 x float> [[X:%.*]]) {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[CALL:%.*]] = call nnan nsz <2 x float> @llvm.sqrt.v2f32(<2 x float> [[X]]), !fpmath [[META1]]
+; CHECK-NEXT: [[CALL:%.*]] = tail call nnan nsz <2 x float> @_Z5rootnDv2_fDv2_i(<2 x float> [[X]], <2 x i32> splat (i32 2))
; CHECK-NEXT: ret <2 x float> [[CALL]]
;
entry:
@@ -667,7 +679,7 @@ define <3 x float> @test_rootn_v3f32__y_2(<3 x float> %x) {
; CHECK-LABEL: define <3 x float> @test_rootn_v3f32__y_2(
; CHECK-SAME: <3 x float> [[X:%.*]]) {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[CALL:%.*]] = call <3 x float> @llvm.sqrt.v3f32(<3 x float> [[X]]), !fpmath [[META1]]
+; CHECK-NEXT: [[CALL:%.*]] = tail call <3 x float> @_Z5rootnDv3_fDv3_i(<3 x float> [[X]], <3 x i32> splat (i32 2))
; CHECK-NEXT: ret <3 x float> [[CALL]]
;
entry:
@@ -679,7 +691,7 @@ define <3 x float> @test_rootn_v3f32__y_2_undef(<3 x float> %x) {
; CHECK-LABEL: define <3 x float> @test_rootn_v3f32__y_2_undef(
; CHECK-SAME: <3 x float> [[X:%.*]]) {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[CALL:%.*]] = call <3 x float> @llvm.sqrt.v3f32(<3 x float> [[X]]), !fpmath [[META1]]
+; CHECK-NEXT: [[CALL:%.*]] = tail call <3 x float> @_Z5rootnDv3_fDv3_i(<3 x float> [[X]], <3 x i32> <i32 2, i32 poison, i32 2>)
; CHECK-NEXT: ret <3 x float> [[CALL]]
;
entry:
@@ -691,7 +703,7 @@ define <4 x float> @test_rootn_v4f32__y_2(<4 x float> %x) {
; CHECK-LABEL: define <4 x float> @test_rootn_v4f32__y_2(
; CHECK-SAME: <4 x float> [[X:%.*]]) {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[CALL:%.*]] = call <4 x float> @llvm.sqrt.v4f32(<4 x float> [[X]]), !fpmath [[META1]]
+; CHECK-NEXT: [[CALL:%.*]] = tail call <4 x float> @_Z5rootnDv4_fDv4_i(<4 x float> [[X]], <4 x i32> splat (i32 2))
; CHECK-NEXT: ret <4 x float> [[CALL]]
;
entry:
@@ -703,7 +715,7 @@ define <8 x float> @test_rootn_v8f32__y_2(<8 x float> %x) {
; CHECK-LABEL: define <8 x float> @test_rootn_v8f32__y_2(
; CHECK-SAME: <8 x float> [[X:%.*]]) {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[CALL:%.*]] = call <8 x float> @llvm.sqrt.v8f32(<8 x float> [[X]]), !fpmath [[META1]]
+; CHECK-NEXT: [[CALL:%.*]] = tail call <8 x float> @_Z5rootnDv8_fDv8_i(<8 x float> [[X]], <8 x i32> splat (i32 2))
; CHECK-NEXT: ret <8 x float> [[CALL]]
;
entry:
@@ -715,7 +727,7 @@ define <16 x float> @test_rootn_v16f32__y_2(<16 x float> %x) {
; CHECK-LABEL: define <16 x float> @test_rootn_v16f32__y_2(
; CHECK-SAME: <16 x float> [[X:%.*]]) {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[CALL:%.*]] = call <16 x float> @llvm.sqrt.v16f32(<16 x float> [[X]]), !fpmath [[META1]]
+; CHECK-NEXT: [[CALL:%.*]] = tail call <16 x float> @_Z5rootnDv16_fDv16_i(<16 x float> [[X]], <16 x i32> splat (i32 2))
; CHECK-NEXT: ret <16 x float> [[CALL]]
;
entry:
@@ -775,7 +787,7 @@ define <2 x float> @test_rootn_v2f32__y_nonsplat_2_poison(<2 x float> %x) {
; CHECK-LABEL: define <2 x float> @test_rootn_v2f32__y_nonsplat_2_poison(
; CHECK-SAME: <2 x float> [[X:%.*]]) {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[CALL:%.*]] = call <2 x float> @llvm.sqrt.v2f32(<2 x float> [[X]]), !fpmath [[META1]]
+; CHECK-NEXT: [[CALL:%.*]] = tail call <2 x float> @_Z5rootnDv2_fDv2_i(<2 x float> [[X]], <2 x i32> <i32 2, i32 poison>)
; CHECK-NEXT: ret <2 x float> [[CALL]]
;
entry:
@@ -883,8 +895,7 @@ define float @test_rootn_f32__y_neg2(float %x) {
; CHECK-LABEL: define float @test_rootn_f32__y_neg2(
; CHECK-SAME: float [[X:%.*]]) {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[TMP0:%.*]] = call contract float @llvm.sqrt.f32(float [[X]])
-; CHECK-NEXT: [[__ROOTN2RSQRT:%.*]] = fdiv contract float 1.000000e+00, [[TMP0]], !fpmath [[META1]]
+; CHECK-NEXT: [[__ROOTN2RSQRT:%.*]] = tail call float @_Z5rootnfi(float [[X]], i32 -2)
; CHECK-NEXT: ret float [[__ROOTN2RSQRT]]
;
entry:
@@ -896,8 +907,7 @@ define float @test_rootn_f32__y_neg2__flags(float %x) {
; CHECK-LABEL: define float @test_rootn_f32__y_neg2__flags(
; CHECK-SAME: float [[X:%.*]]) {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[TMP0:%.*]] = call nnan nsz contract float @llvm.sqrt.f32(float [[X]])
-; CHECK-NEXT: [[__ROOTN2RSQRT:%.*]] = fdiv nnan nsz contract float 1.000000e+00, [[TMP0]], !fpmath [[META1]]
+; CHECK-NEXT: [[__ROOTN2RSQRT:%.*]] = tail call nnan nsz float @_Z5rootnfi(float [[X]], i32 -2)
; CHECK-NEXT: ret float [[__ROOTN2RSQRT]]
;
entry:
@@ -905,6 +915,21 @@ entry:
ret float %call
}
+; nsz+ninf are required to legalize the rootn(-0.0,-2)/rootn(-Inf,-2)
+; corners, so the rsqrt fold should fire here.
+define float @test_rootn_f32__y_neg2__nsz_ninf(float %x) {
+; CHECK-LABEL: define float @test_rootn_f32__y_neg2__nsz_ninf(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = call ninf nsz contract float @llvm.sqrt.f32(float [[X]])
+; CHECK-NEXT: [[TMP1:%.*]] = fdiv ninf nsz contract float 1.000000e+00, [[TMP0]], !fpmath [[META1]]
+; CHECK-NEXT: ret float [[TMP1]]
+;
+entry:
+ %call = tail call nsz ninf float @_Z5rootnfi(float %x, i32 -2)
+ ret float %call
+}
+
define float @test_rootn_f32__y_neg2__strictfp(float %x) #1 {
; CHECK-LABEL: define float @test_rootn_f32__y_neg2__strictfp(
; CHECK-SAME: float [[X:%.*]]) #[[ATTR0]] {
@@ -921,8 +946,8 @@ define float @test_rootn_f32__y_neg2__noinline(float %x) {
; CHECK-LABEL: define float @test_rootn_f32__y_neg2__noinline(
; CHECK-SAME: float [[X:%.*]]) {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[__ROOTN2RSQRT:%.*]] = tail call float @_Z5rootnfi(float [[X]], i32 -2) #[[ATTR3:[0-9]+]]
-; CHECK-NEXT: ret float [[__ROOTN2RSQRT]]
+; CHECK-NEXT: [[CALL:%.*]] = tail call float @_Z5rootnfi(float [[X]], i32 -2) #[[ATTR3:[0-9]+]]
+; CHECK-NEXT: ret float [[CALL]]
;
entry:
%call = tail call float @_Z5rootnfi(float %x, i32 -2) #2
@@ -945,8 +970,7 @@ define <2 x float> @test_rootn_v2f32__y_neg2(<2 x float> %x) {
; CHECK-LABEL: define <2 x float> @test_rootn_v2f32__y_neg2(
; CHECK-SAME: <2 x float> [[X:%.*]]) {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[TMP0:%.*]] = call contract <2 x float> @llvm.sqrt.v2f32(<2 x float> [[X]])
-; CHECK-NEXT: [[__ROOTN2RSQRT:%.*]] = fdiv contract <2 x float> splat (float 1.000000e+00), [[TMP0]], !fpmath [[META1]]
+; CHECK-NEXT: [[__ROOTN2RSQRT:%.*]] = tail call <2 x float> @_Z5rootnDv2_fDv2_i(<2 x float> [[X]], <2 x i32> splat (i32 -2))
; CHECK-NEXT: ret <2 x float> [[__ROOTN2RSQRT]]
;
entry:
@@ -958,8 +982,7 @@ define <2 x float> @test_rootn_v2f32__y_neg2__flags(<2 x float> %x) {
; CHECK-LABEL: define <2 x float> @test_rootn_v2f32__y_neg2__flags(
; CHECK-SAME: <2 x float> [[X:%.*]]) {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[TMP0:%.*]] = call nnan nsz contract <2 x float> @llvm.sqrt.v2f32(<2 x float> [[X]])
-; CHECK-NEXT: [[__ROOTN2RSQRT:%.*]] = fdiv nnan nsz contract <2 x float> splat (float 1.000000e+00), [[TMP0]], !fpmath [[META1]]
+; CHECK-NEXT: [[__ROOTN2RSQRT:%.*]] = tail call nnan nsz <2 x float> @_Z5rootnDv2_fDv2_i(<2 x float> [[X]], <2 x i32> splat (i32 -2))
; CHECK-NEXT: ret <2 x float> [[__ROOTN2RSQRT]]
;
entry:
@@ -1955,8 +1978,8 @@ attributes #2 = { noinline }
;.
; PRELINK: attributes #[[ATTR0]] = { strictfp }
-; PRELINK: attributes #[[ATTR1:[0-9]+]] = { nocallback nocreateundeforpoison nofree nosync nounwind speculatable willreturn memory(none) }
-; PRELINK: attributes #[[ATTR2:[0-9]+]] = { nounwind memory(read) }
+; PRELINK: attributes #[[ATTR1:[0-9]+]] = { nounwind memory(read) }
+; PRELINK: attributes #[[ATTR2:[0-9]+]] = { nocallback nocreateundeforpoison nofree nosync nounwind speculatable willreturn memory(none) }
; PRELINK: attributes #[[ATTR3]] = { noinline }
; PRELINK: attributes #[[ATTR4]] = { nobuiltin }
;.
>From 319ecc85e7d15695626b930b72de13fc40abb553 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Sat, 30 May 2026 23:20:45 +0200
Subject: [PATCH 2/3] Do not exclude fast
---
llvm/lib/Target/AMDGPU/AMDGPULibCalls.cpp | 6 ++----
.../AMDGPU/amdgpu-simplify-libcall-rootn-fast.ll | 13 ++++---------
2 files changed, 6 insertions(+), 13 deletions(-)
diff --git a/llvm/lib/Target/AMDGPU/AMDGPULibCalls.cpp b/llvm/lib/Target/AMDGPU/AMDGPULibCalls.cpp
index a27c6ddaed50b..d497380784d99 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPULibCalls.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPULibCalls.cpp
@@ -1171,10 +1171,8 @@ bool AMDGPULibCalls::fold_rootn(FPMathOperator *FPOp, IRBuilder<> &B,
CallInst *CI = cast<CallInst>(FPOp);
// rootn and sqrt disagree on signed-zero / -Inf inputs (e.g. rootn(-0.0, 2)
- // is +0.0, sqrt(-0.0) is -0.0), so require nsz/ninf. __rootn_fast is exempt.
- bool IsRootnFast = FInfo.getId() == AMDGPULibFunc::EI_ROOTN_FAST;
- bool FMFOkForSqrt =
- IsRootnFast || (FPOp->hasNoSignedZeros() && FPOp->hasNoInfs());
+ // is +0.0, sqrt(-0.0) is -0.0), so require nsz/ninf.
+ bool FMFOkForSqrt = FPOp->hasNoSignedZeros() && FPOp->hasNoInfs();
if (ci_opr1 == 2 && FMFOkForSqrt &&
shouldReplaceLibcallWithIntrinsic(CI,
diff --git a/llvm/test/CodeGen/AMDGPU/amdgpu-simplify-libcall-rootn-fast.ll b/llvm/test/CodeGen/AMDGPU/amdgpu-simplify-libcall-rootn-fast.ll
index a3e47386a4e41..a9721f9358360 100644
--- a/llvm/test/CodeGen/AMDGPU/amdgpu-simplify-libcall-rootn-fast.ll
+++ b/llvm/test/CodeGen/AMDGPU/amdgpu-simplify-libcall-rootn-fast.ll
@@ -291,7 +291,7 @@ define float @test__rootn_fast_f32__2(float %x) #0 {
; CHECK-LABEL: define float @test__rootn_fast_f32__2(
; CHECK-SAME: float [[X:%.*]]) #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.sqrt.f32(float [[X]]), !fpmath [[META0:![0-9]+]]
+; CHECK-NEXT: [[CALL:%.*]] = tail call float @_Z12__rootn_fastfi(float [[X]], i32 2)
; CHECK-NEXT: ret float [[CALL]]
;
entry:
@@ -303,7 +303,7 @@ define float @test__rootn_fast_afn_f32__2(float %x) #0 {
; CHECK-LABEL: define float @test__rootn_fast_afn_f32__2(
; CHECK-SAME: float [[X:%.*]]) #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: [[CALL:%.*]] = call afn float @llvm.sqrt.f32(float [[X]]), !fpmath [[META0]]
+; CHECK-NEXT: [[CALL:%.*]] = tail call afn float @_Z12__rootn_fastfi(float [[X]], i32 2)
; CHECK-NEXT: ret float [[CALL]]
;
entry:
@@ -363,8 +363,7 @@ define float @test__rootn_fast_f32__neg2(float %x) #0 {
; CHECK-LABEL: define float @test__rootn_fast_f32__neg2(
; CHECK-SAME: float [[X:%.*]]) #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: [[TMP0:%.*]] = call contract float @llvm.sqrt.f32(float [[X]])
-; CHECK-NEXT: [[TMP1:%.*]] = fdiv contract float 1.000000e+00, [[TMP0]], !fpmath [[META0]]
+; CHECK-NEXT: [[TMP1:%.*]] = tail call float @_Z12__rootn_fastfi(float [[X]], i32 -2)
; CHECK-NEXT: ret float [[TMP1]]
;
entry:
@@ -376,8 +375,7 @@ define float @test__rootn_fast_afn_f32__neg2(float %x) #0 {
; CHECK-LABEL: define float @test__rootn_fast_afn_f32__neg2(
; CHECK-SAME: float [[X:%.*]]) #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: [[TMP0:%.*]] = call contract afn float @llvm.sqrt.f32(float [[X]])
-; CHECK-NEXT: [[TMP1:%.*]] = fdiv contract afn float 1.000000e+00, [[TMP0]], !fpmath [[META0]]
+; CHECK-NEXT: [[TMP1:%.*]] = tail call afn float @_Z12__rootn_fastfi(float [[X]], i32 -2)
; CHECK-NEXT: ret float [[TMP1]]
;
entry:
@@ -445,6 +443,3 @@ declare <2 x half> @_Z5rootnDv2_DhDv2_i(<2 x half>, <2 x i32>) #0
attributes #0 = { mustprogress nofree norecurse nounwind willreturn memory(none) }
attributes #1 = { mustprogress nofree nounwind willreturn memory(none) }
-;.
-; CHECK: [[META0]] = !{float 2.000000e+00}
-;.
>From 9ea509f2b8971f6ba9776a0b52085d99e1d5b337 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Wed, 10 Jun 2026 18:02:46 +0200
Subject: [PATCH 3/3] Address comment
---
.../AMDGPU/amdgpu-simplify-libcall-rootn-fast.ll | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/llvm/test/CodeGen/AMDGPU/amdgpu-simplify-libcall-rootn-fast.ll b/llvm/test/CodeGen/AMDGPU/amdgpu-simplify-libcall-rootn-fast.ll
index a9721f9358360..e3c852b58f9bb 100644
--- a/llvm/test/CodeGen/AMDGPU/amdgpu-simplify-libcall-rootn-fast.ll
+++ b/llvm/test/CodeGen/AMDGPU/amdgpu-simplify-libcall-rootn-fast.ll
@@ -133,6 +133,18 @@ entry:
ret float %call
}
+define float @test_rootn_afn_f32__2_nsz_ninf(float %x) #0 {
+; CHECK-LABEL: define float @test_rootn_afn_f32__2_nsz_ninf(
+; CHECK-SAME: float [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[CALL:%.*]] = call ninf nsz afn float @llvm.sqrt.f32(float [[X]]), !fpmath [[META0:![0-9]+]]
+; CHECK-NEXT: ret float [[CALL]]
+;
+entry:
+ %call = tail call afn nsz ninf float @_Z5rootnfi(float %x, i32 2)
+ ret float %call
+}
+
define <2 x float> @test_rootn_afn_v2f32__2(<2 x float> %x) #0 {
; CHECK-LABEL: define <2 x float> @test_rootn_afn_v2f32__2(
; CHECK-SAME: <2 x float> [[X:%.*]]) #[[ATTR0]] {
@@ -443,3 +455,6 @@ declare <2 x half> @_Z5rootnDv2_DhDv2_i(<2 x half>, <2 x i32>) #0
attributes #0 = { mustprogress nofree norecurse nounwind willreturn memory(none) }
attributes #1 = { mustprogress nofree nounwind willreturn memory(none) }
+;.
+; CHECK: [[META0]] = !{float 2.000000e+00}
+;.
More information about the llvm-commits
mailing list