[llvm] [AMDGPU] Avoid fract fold when result is observable at +/-inf (PR #219515)

Arseniy Obolenskiy via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 28 09:22:08 PDT 2026


https://github.com/aobolensk created https://github.com/llvm/llvm-project/pull/219515

fract returns nan at +/-inf, while minnum/minimumnum clamp to the constant there, so the fold is only safe when that difference can't be observed

>From 175e7a81082bd13c6d0e410eeaa34c2df636741d Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Fri, 28 Aug 2026 18:21:14 +0200
Subject: [PATCH] [AMDGPU] Avoid fract fold when result is observable at +/-inf

fract returns nan at +/-inf, while minnum/minimumnum clamp to the constant there, so the fold is only safe when that difference can't be observed
---
 .../Target/AMDGPU/AMDGPUCodeGenPrepare.cpp    |   85 +-
 llvm/test/CodeGen/AMDGPU/fract-match.ll       | 1536 ++++++++++++-----
 2 files changed, 1191 insertions(+), 430 deletions(-)

diff --git a/llvm/lib/Target/AMDGPU/AMDGPUCodeGenPrepare.cpp b/llvm/lib/Target/AMDGPU/AMDGPUCodeGenPrepare.cpp
index 21a8896ed3a6d..e5e353e8d55ee 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUCodeGenPrepare.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUCodeGenPrepare.cpp
@@ -19,6 +19,7 @@
 #include "llvm/ADT/SetVector.h"
 #include "llvm/Analysis/AssumptionCache.h"
 #include "llvm/Analysis/ConstantFolding.h"
+#include "llvm/Analysis/FloatingPointPredicateUtils.h"
 #include "llvm/Analysis/TargetLibraryInfo.h"
 #include "llvm/Analysis/TargetTransformInfo.h"
 #include "llvm/Analysis/UniformityAnalysis.h"
@@ -213,7 +214,11 @@ class AMDGPUCodeGenPrepareImpl
   bool canWidenScalarExtLoad(LoadInst &I) const;
 
   Value *matchFractPatImpl(Value &V, const APFloat &C) const;
-  Value *matchFractPatNanAvoidant(Value &V);
+  Value *matchFractPatNanAvoidant(Value &V,
+                                  Instruction *RootToReplace = nullptr);
+  bool isFractNanAvoidantFoldLegalAtInf(const IntrinsicInst *MinI,
+                                        const Value *Sub, const Value *X,
+                                        Instruction *Root) const;
   Value *applyFractPat(IRBuilder<> &Builder, Value *FractArg);
 
   bool canOptimizeWithRsq(FastMathFlags DivFMF, FastMathFlags SqrtFMF) const;
@@ -1619,11 +1624,11 @@ bool AMDGPUCodeGenPrepareImpl::visitSelectInst(SelectInst &I) {
     Builder.setFastMathFlags(FPOp->getFastMathFlags());
 
     if (IsNanPred == FCmpInst::FCMP_UNO && TrueVal == CmpVal &&
-        CmpVal == matchFractPatNanAvoidant(*FalseVal)) {
+        CmpVal == matchFractPatNanAvoidant(*FalseVal, &I)) {
       // isnan(x) ? x : fract(x)
       Fract = applyFractPat(Builder, CmpVal);
     } else if (IsNanPred == FCmpInst::FCMP_ORD && FalseVal == CmpVal) {
-      if (CmpVal == matchFractPatNanAvoidant(*TrueVal)) {
+      if (CmpVal == matchFractPatNanAvoidant(*TrueVal, &I)) {
         // !isnan(x) ? fract(x) : x
         Fract = applyFractPat(Builder, CmpVal);
       } else {
@@ -2100,7 +2105,11 @@ Value *AMDGPUCodeGenPrepareImpl::matchFractPatImpl(Value &FractSrc,
 ///
 /// If fract is a useful instruction for the subtarget. Does not account for the
 /// nan handling; the instruction has a nan check on the input value.
-Value *AMDGPUCodeGenPrepareImpl::matchFractPatNanAvoidant(Value &V) {
+///
+/// If \p RootToReplace is set, the fract must also be unobservable at +/-inf.
+Value *
+AMDGPUCodeGenPrepareImpl::matchFractPatNanAvoidant(Value &V,
+                                                   Instruction *RootToReplace) {
   Value *Arg0;
   const APFloat *C;
 
@@ -2112,7 +2121,71 @@ Value *AMDGPUCodeGenPrepareImpl::matchFractPatNanAvoidant(Value &V) {
                          m_FMinimum(m_Value(Arg0), m_APFloatAllowPoison(C)))))
     return nullptr;
 
-  return matchFractPatImpl(*Arg0, *C);
+  Value *FractArg = matchFractPatImpl(*Arg0, *C);
+  if (!FractArg)
+    return nullptr;
+
+  if (RootToReplace &&
+      !isFractNanAvoidantFoldLegalAtInf(cast<IntrinsicInst>(&V), Arg0, FractArg,
+                                        RootToReplace))
+    return nullptr;
+
+  return FractArg;
+}
+
+/// True if \p U is a select that never picks \p V when \p X is infinity, e.g.
+/// fract's library inf-clamp: select (fcmp oeq (fabs X), +inf), 0.0, V
+static bool isInfDiscardingSelect(User *U, const Value *X, const Value *V) {
+  auto *Sel = dyn_cast<SelectInst>(U);
+  if (!Sel)
+    return false;
+
+  bool IsTrueArm = Sel->getTrueValue() == V;
+  if (!IsTrueArm && Sel->getFalseValue() != V)
+    return false;
+  if (IsTrueArm && Sel->getFalseValue() == V)
+    return false;
+
+  auto *Cmp = dyn_cast<FCmpInst>(Sel->getCondition());
+  if (!Cmp)
+    return false;
+
+  auto [ClassVal, ClassesIfTrue, ClassesIfFalse] =
+      fcmpImpliesClass(Cmp->getPredicate(), *Sel->getFunction(),
+                       Cmp->getOperand(0), Cmp->getOperand(1));
+  if (ClassVal != X)
+    return false;
+
+  return !((IsTrueArm ? ClassesIfTrue : ClassesIfFalse) & fcInf);
+}
+
+/// At +/-inf \p Sub is nan, so \p MinI clamps while fract would return nan;
+/// true if replacing \p Root with fract of \p X is unobservable there. Callers
+/// must not pass a \p Root that can select around \p MinI at +/-inf, since the
+/// poison cases assume \p Root is poison whenever \p MinI is.
+bool AMDGPUCodeGenPrepareImpl::isFractNanAvoidantFoldLegalAtInf(
+    const IntrinsicInst *MinI, const Value *Sub, const Value *X,
+    Instruction *Root) const {
+  // minimum propagates the nan from x - floor(x), exactly like fract.
+  if (MinI->getIntrinsicID() == Intrinsic::minimum)
+    return true;
+
+  // nnan poisons the min at +/-inf; ninf proves nothing here.
+  if (MinI->hasNoNaNs())
+    return true;
+
+  // At +/-inf the fsub is nan with infinite operands, so nnan/ninf poison it.
+  const auto *FPSub = cast<FPMathOperator>(Sub);
+  if (FPSub->hasNoNaNs() || FPSub->hasNoInfs())
+    return true;
+
+  // The result is only observed where an explicit infinity check discards it:
+  //   isinf(x) ? 0.0 : (isnan(x) ? x : minnum(x - floor(x), C))
+  if (all_of(Root->users(),
+             [=](User *U) { return isInfDiscardingSelect(U, X, Root); }))
+    return true;
+
+  return isKnownNeverInfinity(X, SQ.getWithInstruction(Root));
 }
 
 Value *AMDGPUCodeGenPrepareImpl::applyFractPat(IRBuilder<> &Builder,
@@ -2144,7 +2217,7 @@ bool AMDGPUCodeGenPrepareImpl::visitFMinLike(IntrinsicInst &I) {
       return false;
   } else {
     //  minnum(x - floor(x), MIN_CONSTANT)
-    FractArg = matchFractPatNanAvoidant(I);
+    FractArg = matchFractPatNanAvoidant(I, &I);
     if (!FractArg)
       return false;
 
diff --git a/llvm/test/CodeGen/AMDGPU/fract-match.ll b/llvm/test/CodeGen/AMDGPU/fract-match.ll
index b40763b4ca5f4..b2dfb24b1eacb 100644
--- a/llvm/test/CodeGen/AMDGPU/fract-match.ll
+++ b/llvm/test/CodeGen/AMDGPU/fract-match.ll
@@ -523,24 +523,16 @@ entry:
 }
 
 define float @safe_math_fract_f32_noinf_check(float %x, ptr addrspace(1) writeonly captures(none) %ip) {
-; GFX6-IR-LABEL: define float @safe_math_fract_f32_noinf_check(
-; GFX6-IR-SAME: float [[X:%.*]], ptr addrspace(1) writeonly captures(none) [[IP:%.*]]) {
-; GFX6-IR-NEXT:  [[ENTRY:.*:]]
-; GFX6-IR-NEXT:    [[FLOOR:%.*]] = tail call float @llvm.floor.f32(float [[X]])
-; GFX6-IR-NEXT:    [[SUB:%.*]] = fsub float [[X]], [[FLOOR]]
-; GFX6-IR-NEXT:    [[MIN:%.*]] = tail call float @llvm.minnum.f32(float [[SUB]], float f0x3F7FFFFF)
-; GFX6-IR-NEXT:    [[UNO:%.*]] = fcmp uno float [[X]], 0.000000e+00
-; GFX6-IR-NEXT:    [[COND:%.*]] = select i1 [[UNO]], float [[X]], float [[MIN]]
-; GFX6-IR-NEXT:    store float [[FLOOR]], ptr addrspace(1) [[IP]], align 4
-; GFX6-IR-NEXT:    ret float [[COND]]
-;
-; IR-FRACT-LABEL: define float @safe_math_fract_f32_noinf_check(
-; IR-FRACT-SAME: float [[X:%.*]], ptr addrspace(1) writeonly captures(none) [[IP:%.*]]) {
-; IR-FRACT-NEXT:  [[ENTRY:.*:]]
-; IR-FRACT-NEXT:    [[FLOOR:%.*]] = tail call float @llvm.floor.f32(float [[X]])
-; IR-FRACT-NEXT:    [[COND:%.*]] = call float @llvm.amdgcn.fract.f32(float [[X]])
-; IR-FRACT-NEXT:    store float [[FLOOR]], ptr addrspace(1) [[IP]], align 4
-; IR-FRACT-NEXT:    ret float [[COND]]
+; IR-LABEL: define float @safe_math_fract_f32_noinf_check(
+; IR-SAME: float [[X:%.*]], ptr addrspace(1) writeonly captures(none) [[IP:%.*]]) {
+; IR-NEXT:  [[ENTRY:.*:]]
+; IR-NEXT:    [[FLOOR:%.*]] = tail call float @llvm.floor.f32(float [[X]])
+; IR-NEXT:    [[SUB:%.*]] = fsub float [[X]], [[FLOOR]]
+; IR-NEXT:    [[MIN:%.*]] = tail call float @llvm.minnum.f32(float [[SUB]], float f0x3F7FFFFF)
+; IR-NEXT:    [[UNO:%.*]] = fcmp uno float [[X]], 0.000000e+00
+; IR-NEXT:    [[COND:%.*]] = select i1 [[UNO]], float [[X]], float [[MIN]]
+; IR-NEXT:    store float [[FLOOR]], ptr addrspace(1) [[IP]], align 4
+; IR-NEXT:    ret float [[COND]]
 ;
 ; GFX6-LABEL: safe_math_fract_f32_noinf_check:
 ; GFX6:       ; %bb.0: ; %entry
@@ -557,33 +549,47 @@ define float @safe_math_fract_f32_noinf_check(float %x, ptr addrspace(1) writeon
 ; GFX6-NEXT:    buffer_store_dword v3, v[1:2], s[4:7], 0 addr64
 ; GFX6-NEXT:    s_waitcnt vmcnt(0) expcnt(0)
 ; GFX6-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX7-LABEL: safe_math_fract_f32_noinf_check:
 ; GFX7:       ; %bb.0: ; %entry
 ; GFX7-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX7-NEXT:    v_floor_f32_e32 v3, v0
+; GFX7-NEXT:    v_sub_f32_e32 v4, v0, v3
 ; GFX7-NEXT:    s_mov_b32 s6, 0
+; GFX7-NEXT:    v_min_f32_e32 v4, 0x3f7fffff, v4
+; GFX7-NEXT:    v_cmp_u_f32_e32 vcc, v0, v0
 ; GFX7-NEXT:    s_mov_b32 s7, 0xf000
 ; GFX7-NEXT:    s_mov_b32 s4, s6
 ; GFX7-NEXT:    s_mov_b32 s5, s6
-; GFX7-NEXT:    v_floor_f32_e32 v3, v0
-; GFX7-NEXT:    v_fract_f32_e32 v0, v0
+; GFX7-NEXT:    v_cndmask_b32_e32 v0, v4, v0, vcc
 ; GFX7-NEXT:    buffer_store_dword v3, v[1:2], s[4:7], 0 addr64
 ; GFX7-NEXT:    s_waitcnt vmcnt(0)
 ; GFX7-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX8-LABEL: safe_math_fract_f32_noinf_check:
 ; GFX8:       ; %bb.0: ; %entry
 ; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
 ; GFX8-NEXT:    v_floor_f32_e32 v3, v0
-; GFX8-NEXT:    v_fract_f32_e32 v0, v0
+; GFX8-NEXT:    v_sub_f32_e32 v4, v0, v3
+; GFX8-NEXT:    v_min_f32_e32 v4, 0x3f7fffff, v4
+; GFX8-NEXT:    v_cmp_u_f32_e32 vcc, v0, v0
+; GFX8-NEXT:    v_cndmask_b32_e32 v0, v4, v0, vcc
 ; GFX8-NEXT:    global_store_dword v[1:2], v3, off
 ; GFX8-NEXT:    s_waitcnt vmcnt(0)
 ; GFX8-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX11-LABEL: safe_math_fract_f32_noinf_check:
 ; GFX11:       ; %bb.0: ; %entry
 ; GFX11-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
 ; GFX11-NEXT:    v_floor_f32_e32 v3, v0
-; GFX11-NEXT:    v_fract_f32_e32 v0, v0
+; GFX11-NEXT:    v_cmp_u_f32_e32 vcc_lo, v0, v0
+; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(SKIP_2) | instid1(VALU_DEP_1)
+; GFX11-NEXT:    v_sub_f32_e32 v4, v0, v3
 ; GFX11-NEXT:    global_store_b32 v[1:2], v3, off
+; GFX11-NEXT:    v_min_f32_e32 v4, 0x3f7fffff, v4
+; GFX11-NEXT:    v_cndmask_b32_e32 v0, v4, v0, vcc_lo
 ; GFX11-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX12-LABEL: safe_math_fract_f32_noinf_check:
 ; GFX12:       ; %bb.0: ; %entry
 ; GFX12-NEXT:    s_wait_loadcnt_dscnt 0x0
@@ -592,8 +598,13 @@ define float @safe_math_fract_f32_noinf_check(float %x, ptr addrspace(1) writeon
 ; GFX12-NEXT:    s_wait_bvhcnt 0x0
 ; GFX12-NEXT:    s_wait_kmcnt 0x0
 ; GFX12-NEXT:    v_floor_f32_e32 v3, v0
-; GFX12-NEXT:    v_fract_f32_e32 v0, v0
+; GFX12-NEXT:    v_cmp_u_f32_e32 vcc_lo, v0, v0
+; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(SKIP_3) | instid1(VALU_DEP_1)
+; GFX12-NEXT:    v_sub_f32_e32 v4, v0, v3
 ; GFX12-NEXT:    global_store_b32 v[1:2], v3, off
+; GFX12-NEXT:    v_min_num_f32_e32 v4, 0x3f7fffff, v4
+; GFX12-NEXT:    s_wait_alu depctr_va_vcc(0)
+; GFX12-NEXT:    v_cndmask_b32_e32 v0, v4, v0, vcc_lo
 ; GFX12-NEXT:    s_setpc_b64 s[30:31]
 entry:
   %floor = tail call float @llvm.floor.f32(float %x)
@@ -702,19 +713,13 @@ entry:
 }
 
 define float @basic_fract_f32_nonans(float nofpclass(nan) %x) {
-; GFX6-IR-LABEL: define float @basic_fract_f32_nonans(
-; GFX6-IR-SAME: float nofpclass(nan) [[X:%.*]]) {
-; GFX6-IR-NEXT:  [[ENTRY:.*:]]
-; GFX6-IR-NEXT:    [[FLOOR:%.*]] = tail call float @llvm.floor.f32(float [[X]])
-; GFX6-IR-NEXT:    [[SUB:%.*]] = fsub float [[X]], [[FLOOR]]
-; GFX6-IR-NEXT:    [[MIN:%.*]] = tail call float @llvm.minnum.f32(float [[SUB]], float f0x3F7FFFFF)
-; GFX6-IR-NEXT:    ret float [[MIN]]
-;
-; IR-FRACT-LABEL: define float @basic_fract_f32_nonans(
-; IR-FRACT-SAME: float nofpclass(nan) [[X:%.*]]) {
-; IR-FRACT-NEXT:  [[ENTRY:.*:]]
-; IR-FRACT-NEXT:    [[MIN:%.*]] = call nnan float @llvm.amdgcn.fract.f32(float [[X]])
-; IR-FRACT-NEXT:    ret float [[MIN]]
+; IR-LABEL: define float @basic_fract_f32_nonans(
+; IR-SAME: float nofpclass(nan) [[X:%.*]]) {
+; IR-NEXT:  [[ENTRY:.*:]]
+; IR-NEXT:    [[FLOOR:%.*]] = tail call float @llvm.floor.f32(float [[X]])
+; IR-NEXT:    [[SUB:%.*]] = fsub float [[X]], [[FLOOR]]
+; IR-NEXT:    [[MIN:%.*]] = tail call float @llvm.minnum.f32(float [[SUB]], float f0x3F7FFFFF)
+; IR-NEXT:    ret float [[MIN]]
 ;
 ; GFX6-LABEL: basic_fract_f32_nonans:
 ; GFX6:       ; %bb.0: ; %entry
@@ -723,21 +728,32 @@ define float @basic_fract_f32_nonans(float nofpclass(nan) %x) {
 ; GFX6-NEXT:    v_sub_f32_e32 v0, v0, v1
 ; GFX6-NEXT:    v_min_f32_e32 v0, 0x3f7fffff, v0
 ; GFX6-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX7-LABEL: basic_fract_f32_nonans:
 ; GFX7:       ; %bb.0: ; %entry
 ; GFX7-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX7-NEXT:    v_fract_f32_e32 v0, v0
+; GFX7-NEXT:    v_floor_f32_e32 v1, v0
+; GFX7-NEXT:    v_sub_f32_e32 v0, v0, v1
+; GFX7-NEXT:    v_min_f32_e32 v0, 0x3f7fffff, v0
 ; GFX7-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX8-LABEL: basic_fract_f32_nonans:
 ; GFX8:       ; %bb.0: ; %entry
 ; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX8-NEXT:    v_fract_f32_e32 v0, v0
+; GFX8-NEXT:    v_floor_f32_e32 v1, v0
+; GFX8-NEXT:    v_sub_f32_e32 v0, v0, v1
+; GFX8-NEXT:    v_min_f32_e32 v0, 0x3f7fffff, v0
 ; GFX8-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX11-LABEL: basic_fract_f32_nonans:
 ; GFX11:       ; %bb.0: ; %entry
 ; GFX11-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX11-NEXT:    v_fract_f32_e32 v0, v0
+; GFX11-NEXT:    v_floor_f32_e32 v1, v0
+; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX11-NEXT:    v_sub_f32_e32 v0, v0, v1
+; GFX11-NEXT:    v_min_f32_e32 v0, 0x3f7fffff, v0
 ; GFX11-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX12-LABEL: basic_fract_f32_nonans:
 ; GFX12:       ; %bb.0: ; %entry
 ; GFX12-NEXT:    s_wait_loadcnt_dscnt 0x0
@@ -745,7 +761,10 @@ define float @basic_fract_f32_nonans(float nofpclass(nan) %x) {
 ; GFX12-NEXT:    s_wait_samplecnt 0x0
 ; GFX12-NEXT:    s_wait_bvhcnt 0x0
 ; GFX12-NEXT:    s_wait_kmcnt 0x0
-; GFX12-NEXT:    v_fract_f32_e32 v0, v0
+; GFX12-NEXT:    v_floor_f32_e32 v1, v0
+; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX12-NEXT:    v_sub_f32_e32 v0, v0, v1
+; GFX12-NEXT:    v_min_num_f32_e32 v0, 0x3f7fffff, v0
 ; GFX12-NEXT:    s_setpc_b64 s[30:31]
 entry:
   %floor = tail call float @llvm.floor.f32(float %x)
@@ -812,19 +831,13 @@ entry:
 }
 
 define float @basic_fract_f32_flags_fsub(float nofpclass(nan) %x) {
-; GFX6-IR-LABEL: define float @basic_fract_f32_flags_fsub(
-; GFX6-IR-SAME: float nofpclass(nan) [[X:%.*]]) {
-; GFX6-IR-NEXT:  [[ENTRY:.*:]]
-; GFX6-IR-NEXT:    [[FLOOR:%.*]] = tail call float @llvm.floor.f32(float [[X]])
-; GFX6-IR-NEXT:    [[SUB:%.*]] = fsub nsz float [[X]], [[FLOOR]]
-; GFX6-IR-NEXT:    [[MIN:%.*]] = tail call float @llvm.minnum.f32(float [[SUB]], float f0x3F7FFFFF)
-; GFX6-IR-NEXT:    ret float [[MIN]]
-;
-; IR-FRACT-LABEL: define float @basic_fract_f32_flags_fsub(
-; IR-FRACT-SAME: float nofpclass(nan) [[X:%.*]]) {
-; IR-FRACT-NEXT:  [[ENTRY:.*:]]
-; IR-FRACT-NEXT:    [[MIN:%.*]] = call nnan float @llvm.amdgcn.fract.f32(float [[X]])
-; IR-FRACT-NEXT:    ret float [[MIN]]
+; IR-LABEL: define float @basic_fract_f32_flags_fsub(
+; IR-SAME: float nofpclass(nan) [[X:%.*]]) {
+; IR-NEXT:  [[ENTRY:.*:]]
+; IR-NEXT:    [[FLOOR:%.*]] = tail call float @llvm.floor.f32(float [[X]])
+; IR-NEXT:    [[SUB:%.*]] = fsub nsz float [[X]], [[FLOOR]]
+; IR-NEXT:    [[MIN:%.*]] = tail call float @llvm.minnum.f32(float [[SUB]], float f0x3F7FFFFF)
+; IR-NEXT:    ret float [[MIN]]
 ;
 ; GFX6-LABEL: basic_fract_f32_flags_fsub:
 ; GFX6:       ; %bb.0: ; %entry
@@ -833,21 +846,32 @@ define float @basic_fract_f32_flags_fsub(float nofpclass(nan) %x) {
 ; GFX6-NEXT:    v_sub_f32_e32 v0, v0, v1
 ; GFX6-NEXT:    v_min_f32_e32 v0, 0x3f7fffff, v0
 ; GFX6-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX7-LABEL: basic_fract_f32_flags_fsub:
 ; GFX7:       ; %bb.0: ; %entry
 ; GFX7-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX7-NEXT:    v_fract_f32_e32 v0, v0
+; GFX7-NEXT:    v_floor_f32_e32 v1, v0
+; GFX7-NEXT:    v_sub_f32_e32 v0, v0, v1
+; GFX7-NEXT:    v_min_f32_e32 v0, 0x3f7fffff, v0
 ; GFX7-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX8-LABEL: basic_fract_f32_flags_fsub:
 ; GFX8:       ; %bb.0: ; %entry
 ; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX8-NEXT:    v_fract_f32_e32 v0, v0
+; GFX8-NEXT:    v_floor_f32_e32 v1, v0
+; GFX8-NEXT:    v_sub_f32_e32 v0, v0, v1
+; GFX8-NEXT:    v_min_f32_e32 v0, 0x3f7fffff, v0
 ; GFX8-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX11-LABEL: basic_fract_f32_flags_fsub:
 ; GFX11:       ; %bb.0: ; %entry
 ; GFX11-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX11-NEXT:    v_fract_f32_e32 v0, v0
+; GFX11-NEXT:    v_floor_f32_e32 v1, v0
+; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX11-NEXT:    v_sub_f32_e32 v0, v0, v1
+; GFX11-NEXT:    v_min_f32_e32 v0, 0x3f7fffff, v0
 ; GFX11-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX12-LABEL: basic_fract_f32_flags_fsub:
 ; GFX12:       ; %bb.0: ; %entry
 ; GFX12-NEXT:    s_wait_loadcnt_dscnt 0x0
@@ -855,7 +879,10 @@ define float @basic_fract_f32_flags_fsub(float nofpclass(nan) %x) {
 ; GFX12-NEXT:    s_wait_samplecnt 0x0
 ; GFX12-NEXT:    s_wait_bvhcnt 0x0
 ; GFX12-NEXT:    s_wait_kmcnt 0x0
-; GFX12-NEXT:    v_fract_f32_e32 v0, v0
+; GFX12-NEXT:    v_floor_f32_e32 v1, v0
+; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX12-NEXT:    v_sub_f32_e32 v0, v0, v1
+; GFX12-NEXT:    v_min_num_f32_e32 v0, 0x3f7fffff, v0
 ; GFX12-NEXT:    s_setpc_b64 s[30:31]
 entry:
   %floor = tail call float @llvm.floor.f32(float %x)
@@ -865,24 +892,13 @@ entry:
 }
 
 define <2 x float> @basic_fract_v2f32_nonans(<2 x float> nofpclass(nan) %x) {
-; GFX6-IR-LABEL: define <2 x float> @basic_fract_v2f32_nonans(
-; GFX6-IR-SAME: <2 x float> nofpclass(nan) [[X:%.*]]) {
-; GFX6-IR-NEXT:  [[ENTRY:.*:]]
-; GFX6-IR-NEXT:    [[FLOOR:%.*]] = tail call <2 x float> @llvm.floor.v2f32(<2 x float> [[X]])
-; GFX6-IR-NEXT:    [[SUB:%.*]] = fsub <2 x float> [[X]], [[FLOOR]]
-; GFX6-IR-NEXT:    [[MIN:%.*]] = tail call <2 x float> @llvm.minnum.v2f32(<2 x float> [[SUB]], <2 x float> splat (float f0x3F7FFFFF))
-; GFX6-IR-NEXT:    ret <2 x float> [[MIN]]
-;
-; IR-FRACT-LABEL: define <2 x float> @basic_fract_v2f32_nonans(
-; IR-FRACT-SAME: <2 x float> nofpclass(nan) [[X:%.*]]) {
-; IR-FRACT-NEXT:  [[ENTRY:.*:]]
-; IR-FRACT-NEXT:    [[TMP0:%.*]] = extractelement <2 x float> [[X]], i64 0
-; IR-FRACT-NEXT:    [[TMP1:%.*]] = extractelement <2 x float> [[X]], i64 1
-; IR-FRACT-NEXT:    [[TMP2:%.*]] = call nnan float @llvm.amdgcn.fract.f32(float [[TMP0]])
-; IR-FRACT-NEXT:    [[TMP3:%.*]] = call nnan float @llvm.amdgcn.fract.f32(float [[TMP1]])
-; IR-FRACT-NEXT:    [[TMP4:%.*]] = insertelement <2 x float> poison, float [[TMP2]], i64 0
-; IR-FRACT-NEXT:    [[MIN:%.*]] = insertelement <2 x float> [[TMP4]], float [[TMP3]], i64 1
-; IR-FRACT-NEXT:    ret <2 x float> [[MIN]]
+; IR-LABEL: define <2 x float> @basic_fract_v2f32_nonans(
+; IR-SAME: <2 x float> nofpclass(nan) [[X:%.*]]) {
+; IR-NEXT:  [[ENTRY:.*:]]
+; IR-NEXT:    [[FLOOR:%.*]] = tail call <2 x float> @llvm.floor.v2f32(<2 x float> [[X]])
+; IR-NEXT:    [[SUB:%.*]] = fsub <2 x float> [[X]], [[FLOOR]]
+; IR-NEXT:    [[MIN:%.*]] = tail call <2 x float> @llvm.minnum.v2f32(<2 x float> [[SUB]], <2 x float> splat (float f0x3F7FFFFF))
+; IR-NEXT:    ret <2 x float> [[MIN]]
 ;
 ; GFX6-LABEL: basic_fract_v2f32_nonans:
 ; GFX6:       ; %bb.0: ; %entry
@@ -894,24 +910,39 @@ define <2 x float> @basic_fract_v2f32_nonans(<2 x float> nofpclass(nan) %x) {
 ; GFX6-NEXT:    v_min_f32_e32 v0, 0x3f7fffff, v0
 ; GFX6-NEXT:    v_min_f32_e32 v1, 0x3f7fffff, v1
 ; GFX6-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX7-LABEL: basic_fract_v2f32_nonans:
 ; GFX7:       ; %bb.0: ; %entry
 ; GFX7-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX7-NEXT:    v_fract_f32_e32 v0, v0
-; GFX7-NEXT:    v_fract_f32_e32 v1, v1
+; GFX7-NEXT:    v_floor_f32_e32 v2, v0
+; GFX7-NEXT:    v_floor_f32_e32 v3, v1
+; GFX7-NEXT:    v_sub_f32_e32 v1, v1, v3
+; GFX7-NEXT:    v_sub_f32_e32 v0, v0, v2
+; GFX7-NEXT:    v_min_f32_e32 v0, 0x3f7fffff, v0
+; GFX7-NEXT:    v_min_f32_e32 v1, 0x3f7fffff, v1
 ; GFX7-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX8-LABEL: basic_fract_v2f32_nonans:
 ; GFX8:       ; %bb.0: ; %entry
 ; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX8-NEXT:    v_fract_f32_e32 v0, v0
-; GFX8-NEXT:    v_fract_f32_e32 v1, v1
+; GFX8-NEXT:    v_floor_f32_e32 v2, v0
+; GFX8-NEXT:    v_floor_f32_e32 v3, v1
+; GFX8-NEXT:    v_sub_f32_e32 v1, v1, v3
+; GFX8-NEXT:    v_sub_f32_e32 v0, v0, v2
+; GFX8-NEXT:    v_min_f32_e32 v0, 0x3f7fffff, v0
+; GFX8-NEXT:    v_min_f32_e32 v1, 0x3f7fffff, v1
 ; GFX8-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX11-LABEL: basic_fract_v2f32_nonans:
 ; GFX11:       ; %bb.0: ; %entry
 ; GFX11-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX11-NEXT:    v_fract_f32_e32 v0, v0
-; GFX11-NEXT:    v_fract_f32_e32 v1, v1
+; GFX11-NEXT:    v_floor_f32_e32 v2, v0
+; GFX11-NEXT:    v_floor_f32_e32 v3, v1
+; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX11-NEXT:    v_dual_sub_f32 v0, v0, v2 :: v_dual_sub_f32 v1, v1, v3
+; GFX11-NEXT:    v_dual_min_f32 v0, 0x3f7fffff, v0 :: v_dual_min_f32 v1, 0x3f7fffff, v1
 ; GFX11-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX12-LABEL: basic_fract_v2f32_nonans:
 ; GFX12:       ; %bb.0: ; %entry
 ; GFX12-NEXT:    s_wait_loadcnt_dscnt 0x0
@@ -919,8 +950,11 @@ define <2 x float> @basic_fract_v2f32_nonans(<2 x float> nofpclass(nan) %x) {
 ; GFX12-NEXT:    s_wait_samplecnt 0x0
 ; GFX12-NEXT:    s_wait_bvhcnt 0x0
 ; GFX12-NEXT:    s_wait_kmcnt 0x0
-; GFX12-NEXT:    v_fract_f32_e32 v0, v0
-; GFX12-NEXT:    v_fract_f32_e32 v1, v1
+; GFX12-NEXT:    v_floor_f32_e32 v2, v0
+; GFX12-NEXT:    v_floor_f32_e32 v3, v1
+; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX12-NEXT:    v_dual_sub_f32 v0, v0, v2 :: v_dual_sub_f32 v1, v1, v3
+; GFX12-NEXT:    v_dual_min_num_f32 v0, 0x3f7fffff, v0 :: v_dual_min_num_f32 v1, 0x3f7fffff, v1
 ; GFX12-NEXT:    s_setpc_b64 s[30:31]
 entry:
   %floor = tail call <2 x float> @llvm.floor.v2f32(<2 x float> %x)
@@ -930,23 +964,14 @@ entry:
 }
 
 define float @basic_fract_f32_multi_use_fsub_nonans(float nofpclass(nan) %x, ptr addrspace(1) %ptr) {
-; GFX6-IR-LABEL: define float @basic_fract_f32_multi_use_fsub_nonans(
-; GFX6-IR-SAME: float nofpclass(nan) [[X:%.*]], ptr addrspace(1) [[PTR:%.*]]) {
-; GFX6-IR-NEXT:  [[ENTRY:.*:]]
-; GFX6-IR-NEXT:    [[FLOOR:%.*]] = tail call float @llvm.floor.f32(float [[X]])
-; GFX6-IR-NEXT:    [[SUB:%.*]] = fsub float [[X]], [[FLOOR]]
-; GFX6-IR-NEXT:    [[MIN:%.*]] = tail call float @llvm.minnum.f32(float [[SUB]], float f0x3F7FFFFF)
-; GFX6-IR-NEXT:    store float [[SUB]], ptr addrspace(1) [[PTR]], align 4
-; GFX6-IR-NEXT:    ret float [[MIN]]
-;
-; IR-FRACT-LABEL: define float @basic_fract_f32_multi_use_fsub_nonans(
-; IR-FRACT-SAME: float nofpclass(nan) [[X:%.*]], ptr addrspace(1) [[PTR:%.*]]) {
-; IR-FRACT-NEXT:  [[ENTRY:.*:]]
-; IR-FRACT-NEXT:    [[FLOOR:%.*]] = tail call float @llvm.floor.f32(float [[X]])
-; IR-FRACT-NEXT:    [[SUB:%.*]] = fsub float [[X]], [[FLOOR]]
-; IR-FRACT-NEXT:    [[MIN:%.*]] = call nnan float @llvm.amdgcn.fract.f32(float [[X]])
-; IR-FRACT-NEXT:    store float [[SUB]], ptr addrspace(1) [[PTR]], align 4
-; IR-FRACT-NEXT:    ret float [[MIN]]
+; IR-LABEL: define float @basic_fract_f32_multi_use_fsub_nonans(
+; IR-SAME: float nofpclass(nan) [[X:%.*]], ptr addrspace(1) [[PTR:%.*]]) {
+; IR-NEXT:  [[ENTRY:.*:]]
+; IR-NEXT:    [[FLOOR:%.*]] = tail call float @llvm.floor.f32(float [[X]])
+; IR-NEXT:    [[SUB:%.*]] = fsub float [[X]], [[FLOOR]]
+; IR-NEXT:    [[MIN:%.*]] = tail call float @llvm.minnum.f32(float [[SUB]], float f0x3F7FFFFF)
+; IR-NEXT:    store float [[SUB]], ptr addrspace(1) [[PTR]], align 4
+; IR-NEXT:    ret float [[MIN]]
 ;
 ; GFX6-LABEL: basic_fract_f32_multi_use_fsub_nonans:
 ; GFX6:       ; %bb.0: ; %entry
@@ -961,37 +986,41 @@ define float @basic_fract_f32_multi_use_fsub_nonans(float nofpclass(nan) %x, ptr
 ; GFX6-NEXT:    buffer_store_dword v3, v[1:2], s[4:7], 0 addr64
 ; GFX6-NEXT:    s_waitcnt vmcnt(0) expcnt(0)
 ; GFX6-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX7-LABEL: basic_fract_f32_multi_use_fsub_nonans:
 ; GFX7:       ; %bb.0: ; %entry
 ; GFX7-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX7-NEXT:    s_mov_b32 s6, 0
 ; GFX7-NEXT:    v_floor_f32_e32 v3, v0
+; GFX7-NEXT:    s_mov_b32 s6, 0
+; GFX7-NEXT:    v_sub_f32_e32 v3, v0, v3
 ; GFX7-NEXT:    s_mov_b32 s7, 0xf000
 ; GFX7-NEXT:    s_mov_b32 s4, s6
 ; GFX7-NEXT:    s_mov_b32 s5, s6
-; GFX7-NEXT:    v_sub_f32_e32 v3, v0, v3
-; GFX7-NEXT:    v_fract_f32_e32 v0, v0
+; GFX7-NEXT:    v_min_f32_e32 v0, 0x3f7fffff, v3
 ; GFX7-NEXT:    buffer_store_dword v3, v[1:2], s[4:7], 0 addr64
 ; GFX7-NEXT:    s_waitcnt vmcnt(0)
 ; GFX7-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX8-LABEL: basic_fract_f32_multi_use_fsub_nonans:
 ; GFX8:       ; %bb.0: ; %entry
 ; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
 ; GFX8-NEXT:    v_floor_f32_e32 v3, v0
 ; GFX8-NEXT:    v_sub_f32_e32 v3, v0, v3
-; GFX8-NEXT:    v_fract_f32_e32 v0, v0
+; GFX8-NEXT:    v_min_f32_e32 v0, 0x3f7fffff, v3
 ; GFX8-NEXT:    global_store_dword v[1:2], v3, off
 ; GFX8-NEXT:    s_waitcnt vmcnt(0)
 ; GFX8-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX11-LABEL: basic_fract_f32_multi_use_fsub_nonans:
 ; GFX11:       ; %bb.0: ; %entry
 ; GFX11-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
 ; GFX11-NEXT:    v_floor_f32_e32 v3, v0
-; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_1)
+; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_1)
 ; GFX11-NEXT:    v_sub_f32_e32 v3, v0, v3
-; GFX11-NEXT:    v_fract_f32_e32 v0, v0
+; GFX11-NEXT:    v_min_f32_e32 v0, 0x3f7fffff, v3
 ; GFX11-NEXT:    global_store_b32 v[1:2], v3, off
 ; GFX11-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX12-LABEL: basic_fract_f32_multi_use_fsub_nonans:
 ; GFX12:       ; %bb.0: ; %entry
 ; GFX12-NEXT:    s_wait_loadcnt_dscnt 0x0
@@ -1000,9 +1029,9 @@ define float @basic_fract_f32_multi_use_fsub_nonans(float nofpclass(nan) %x, ptr
 ; GFX12-NEXT:    s_wait_bvhcnt 0x0
 ; GFX12-NEXT:    s_wait_kmcnt 0x0
 ; GFX12-NEXT:    v_floor_f32_e32 v3, v0
-; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_1)
+; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_1)
 ; GFX12-NEXT:    v_sub_f32_e32 v3, v0, v3
-; GFX12-NEXT:    v_fract_f32_e32 v0, v0
+; GFX12-NEXT:    v_min_num_f32_e32 v0, 0x3f7fffff, v3
 ; GFX12-NEXT:    global_store_b32 v[1:2], v3, off
 ; GFX12-NEXT:    s_setpc_b64 s[30:31]
 entry:
@@ -1183,19 +1212,13 @@ entry:
 }
 
 define float @nnan_src_fract_f32(float nofpclass(nan) %x) {
-; GFX6-IR-LABEL: define float @nnan_src_fract_f32(
-; GFX6-IR-SAME: float nofpclass(nan) [[X:%.*]]) {
-; GFX6-IR-NEXT:  [[ENTRY:.*:]]
-; GFX6-IR-NEXT:    [[FLOOR:%.*]] = tail call float @llvm.floor.f32(float [[X]])
-; GFX6-IR-NEXT:    [[SUB:%.*]] = fsub float [[X]], [[FLOOR]]
-; GFX6-IR-NEXT:    [[MIN:%.*]] = tail call float @llvm.minnum.f32(float [[SUB]], float f0x3F7FFFFF)
-; GFX6-IR-NEXT:    ret float [[MIN]]
-;
-; IR-FRACT-LABEL: define float @nnan_src_fract_f32(
-; IR-FRACT-SAME: float nofpclass(nan) [[X:%.*]]) {
-; IR-FRACT-NEXT:  [[ENTRY:.*:]]
-; IR-FRACT-NEXT:    [[MIN:%.*]] = call nnan float @llvm.amdgcn.fract.f32(float [[X]])
-; IR-FRACT-NEXT:    ret float [[MIN]]
+; IR-LABEL: define float @nnan_src_fract_f32(
+; IR-SAME: float nofpclass(nan) [[X:%.*]]) {
+; IR-NEXT:  [[ENTRY:.*:]]
+; IR-NEXT:    [[FLOOR:%.*]] = tail call float @llvm.floor.f32(float [[X]])
+; IR-NEXT:    [[SUB:%.*]] = fsub float [[X]], [[FLOOR]]
+; IR-NEXT:    [[MIN:%.*]] = tail call float @llvm.minnum.f32(float [[SUB]], float f0x3F7FFFFF)
+; IR-NEXT:    ret float [[MIN]]
 ;
 ; GFX6-LABEL: nnan_src_fract_f32:
 ; GFX6:       ; %bb.0: ; %entry
@@ -1204,21 +1227,32 @@ define float @nnan_src_fract_f32(float nofpclass(nan) %x) {
 ; GFX6-NEXT:    v_sub_f32_e32 v0, v0, v1
 ; GFX6-NEXT:    v_min_f32_e32 v0, 0x3f7fffff, v0
 ; GFX6-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX7-LABEL: nnan_src_fract_f32:
 ; GFX7:       ; %bb.0: ; %entry
 ; GFX7-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX7-NEXT:    v_fract_f32_e32 v0, v0
+; GFX7-NEXT:    v_floor_f32_e32 v1, v0
+; GFX7-NEXT:    v_sub_f32_e32 v0, v0, v1
+; GFX7-NEXT:    v_min_f32_e32 v0, 0x3f7fffff, v0
 ; GFX7-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX8-LABEL: nnan_src_fract_f32:
 ; GFX8:       ; %bb.0: ; %entry
 ; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX8-NEXT:    v_fract_f32_e32 v0, v0
+; GFX8-NEXT:    v_floor_f32_e32 v1, v0
+; GFX8-NEXT:    v_sub_f32_e32 v0, v0, v1
+; GFX8-NEXT:    v_min_f32_e32 v0, 0x3f7fffff, v0
 ; GFX8-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX11-LABEL: nnan_src_fract_f32:
 ; GFX11:       ; %bb.0: ; %entry
 ; GFX11-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX11-NEXT:    v_fract_f32_e32 v0, v0
+; GFX11-NEXT:    v_floor_f32_e32 v1, v0
+; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX11-NEXT:    v_sub_f32_e32 v0, v0, v1
+; GFX11-NEXT:    v_min_f32_e32 v0, 0x3f7fffff, v0
 ; GFX11-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX12-LABEL: nnan_src_fract_f32:
 ; GFX12:       ; %bb.0: ; %entry
 ; GFX12-NEXT:    s_wait_loadcnt_dscnt 0x0
@@ -1226,7 +1260,10 @@ define float @nnan_src_fract_f32(float nofpclass(nan) %x) {
 ; GFX12-NEXT:    s_wait_samplecnt 0x0
 ; GFX12-NEXT:    s_wait_bvhcnt 0x0
 ; GFX12-NEXT:    s_wait_kmcnt 0x0
-; GFX12-NEXT:    v_fract_f32_e32 v0, v0
+; GFX12-NEXT:    v_floor_f32_e32 v1, v0
+; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX12-NEXT:    v_sub_f32_e32 v0, v0, v1
+; GFX12-NEXT:    v_min_num_f32_e32 v0, 0x3f7fffff, v0
 ; GFX12-NEXT:    s_setpc_b64 s[30:31]
 entry:
   %floor = tail call float @llvm.floor.f32(float %x)
@@ -1235,46 +1272,49 @@ entry:
   ret float %min
 }
 
-; Negative test
-define float @not_fract_f32_wrong_const(float nofpclass(nan) %x) {
-; IR-LABEL: define float @not_fract_f32_wrong_const(
+define float @ninf_minnum_fract_f32_nonans(float nofpclass(nan) %x) {
+; IR-LABEL: define float @ninf_minnum_fract_f32_nonans(
 ; IR-SAME: float nofpclass(nan) [[X:%.*]]) {
 ; IR-NEXT:  [[ENTRY:.*:]]
 ; IR-NEXT:    [[FLOOR:%.*]] = tail call float @llvm.floor.f32(float [[X]])
 ; IR-NEXT:    [[SUB:%.*]] = fsub float [[X]], [[FLOOR]]
-; IR-NEXT:    [[MIN:%.*]] = tail call float @llvm.minnum.f32(float [[SUB]], float f0x3F7FFFFE)
+; IR-NEXT:    [[MIN:%.*]] = tail call ninf float @llvm.minnum.f32(float [[SUB]], float f0x3F7FFFFF)
 ; IR-NEXT:    ret float [[MIN]]
 ;
-; GFX6-LABEL: not_fract_f32_wrong_const:
+; GFX6-LABEL: ninf_minnum_fract_f32_nonans:
 ; GFX6:       ; %bb.0: ; %entry
 ; GFX6-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
 ; GFX6-NEXT:    v_floor_f32_e32 v1, v0
 ; GFX6-NEXT:    v_sub_f32_e32 v0, v0, v1
-; GFX6-NEXT:    v_min_f32_e32 v0, 0x3f7ffffe, v0
+; GFX6-NEXT:    v_min_f32_e32 v0, 0x3f7fffff, v0
 ; GFX6-NEXT:    s_setpc_b64 s[30:31]
-; GFX7-LABEL: not_fract_f32_wrong_const:
+;
+; GFX7-LABEL: ninf_minnum_fract_f32_nonans:
 ; GFX7:       ; %bb.0: ; %entry
 ; GFX7-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
 ; GFX7-NEXT:    v_floor_f32_e32 v1, v0
 ; GFX7-NEXT:    v_sub_f32_e32 v0, v0, v1
-; GFX7-NEXT:    v_min_f32_e32 v0, 0x3f7ffffe, v0
+; GFX7-NEXT:    v_min_f32_e32 v0, 0x3f7fffff, v0
 ; GFX7-NEXT:    s_setpc_b64 s[30:31]
-; GFX8-LABEL: not_fract_f32_wrong_const:
+;
+; GFX8-LABEL: ninf_minnum_fract_f32_nonans:
 ; GFX8:       ; %bb.0: ; %entry
 ; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
 ; GFX8-NEXT:    v_floor_f32_e32 v1, v0
 ; GFX8-NEXT:    v_sub_f32_e32 v0, v0, v1
-; GFX8-NEXT:    v_min_f32_e32 v0, 0x3f7ffffe, v0
+; GFX8-NEXT:    v_min_f32_e32 v0, 0x3f7fffff, v0
 ; GFX8-NEXT:    s_setpc_b64 s[30:31]
-; GFX11-LABEL: not_fract_f32_wrong_const:
+;
+; GFX11-LABEL: ninf_minnum_fract_f32_nonans:
 ; GFX11:       ; %bb.0: ; %entry
 ; GFX11-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
 ; GFX11-NEXT:    v_floor_f32_e32 v1, v0
 ; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_1)
 ; GFX11-NEXT:    v_sub_f32_e32 v0, v0, v1
-; GFX11-NEXT:    v_min_f32_e32 v0, 0x3f7ffffe, v0
+; GFX11-NEXT:    v_min_f32_e32 v0, 0x3f7fffff, v0
 ; GFX11-NEXT:    s_setpc_b64 s[30:31]
-; GFX12-LABEL: not_fract_f32_wrong_const:
+;
+; GFX12-LABEL: ninf_minnum_fract_f32_nonans:
 ; GFX12:       ; %bb.0: ; %entry
 ; GFX12-NEXT:    s_wait_loadcnt_dscnt 0x0
 ; GFX12-NEXT:    s_wait_expcnt 0x0
@@ -1284,7 +1324,236 @@ define float @not_fract_f32_wrong_const(float nofpclass(nan) %x) {
 ; GFX12-NEXT:    v_floor_f32_e32 v1, v0
 ; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_1)
 ; GFX12-NEXT:    v_sub_f32_e32 v0, v0, v1
-; GFX12-NEXT:    v_min_num_f32_e32 v0, 0x3f7ffffe, v0
+; GFX12-NEXT:    v_min_num_f32_e32 v0, 0x3f7fffff, v0
+; GFX12-NEXT:    s_setpc_b64 s[30:31]
+entry:
+  %floor = tail call float @llvm.floor.f32(float %x)
+  %sub = fsub float %x, %floor
+  %min = tail call ninf float @llvm.minnum.f32(float %sub, float 0x3FEFFFFFE0000000)
+  ret float %min
+}
+
+define float @ninf_fsub_fract_f32_nonans(float nofpclass(nan) %x) {
+; GFX6-IR-LABEL: define float @ninf_fsub_fract_f32_nonans(
+; GFX6-IR-SAME: float nofpclass(nan) [[X:%.*]]) {
+; GFX6-IR-NEXT:  [[ENTRY:.*:]]
+; GFX6-IR-NEXT:    [[FLOOR:%.*]] = tail call float @llvm.floor.f32(float [[X]])
+; GFX6-IR-NEXT:    [[SUB:%.*]] = fsub ninf float [[X]], [[FLOOR]]
+; GFX6-IR-NEXT:    [[MIN:%.*]] = tail call float @llvm.minnum.f32(float [[SUB]], float f0x3F7FFFFF)
+; GFX6-IR-NEXT:    ret float [[MIN]]
+;
+; IR-FRACT-LABEL: define float @ninf_fsub_fract_f32_nonans(
+; IR-FRACT-SAME: float nofpclass(nan) [[X:%.*]]) {
+; IR-FRACT-NEXT:  [[ENTRY:.*:]]
+; IR-FRACT-NEXT:    [[MIN:%.*]] = call nnan float @llvm.amdgcn.fract.f32(float [[X]])
+; IR-FRACT-NEXT:    ret float [[MIN]]
+;
+; GFX6-LABEL: ninf_fsub_fract_f32_nonans:
+; GFX6:       ; %bb.0: ; %entry
+; GFX6-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX6-NEXT:    v_floor_f32_e32 v1, v0
+; GFX6-NEXT:    v_sub_f32_e32 v0, v0, v1
+; GFX6-NEXT:    v_min_f32_e32 v0, 0x3f7fffff, v0
+; GFX6-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX7-LABEL: ninf_fsub_fract_f32_nonans:
+; GFX7:       ; %bb.0: ; %entry
+; GFX7-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX7-NEXT:    v_fract_f32_e32 v0, v0
+; GFX7-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX8-LABEL: ninf_fsub_fract_f32_nonans:
+; GFX8:       ; %bb.0: ; %entry
+; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX8-NEXT:    v_fract_f32_e32 v0, v0
+; GFX8-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX11-LABEL: ninf_fsub_fract_f32_nonans:
+; GFX11:       ; %bb.0: ; %entry
+; GFX11-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-NEXT:    v_fract_f32_e32 v0, v0
+; GFX11-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX12-LABEL: ninf_fsub_fract_f32_nonans:
+; GFX12:       ; %bb.0: ; %entry
+; GFX12-NEXT:    s_wait_loadcnt_dscnt 0x0
+; GFX12-NEXT:    s_wait_expcnt 0x0
+; GFX12-NEXT:    s_wait_samplecnt 0x0
+; GFX12-NEXT:    s_wait_bvhcnt 0x0
+; GFX12-NEXT:    s_wait_kmcnt 0x0
+; GFX12-NEXT:    v_fract_f32_e32 v0, v0
+; GFX12-NEXT:    s_setpc_b64 s[30:31]
+entry:
+  %floor = tail call float @llvm.floor.f32(float %x)
+  %sub = fsub ninf float %x, %floor
+  %min = tail call float @llvm.minnum.f32(float %sub, float 0x3FEFFFFFE0000000)
+  ret float %min
+}
+
+define float @nnan_fsub_fract_f32_nonans(float nofpclass(nan) %x) {
+; GFX6-IR-LABEL: define float @nnan_fsub_fract_f32_nonans(
+; GFX6-IR-SAME: float nofpclass(nan) [[X:%.*]]) {
+; GFX6-IR-NEXT:  [[ENTRY:.*:]]
+; GFX6-IR-NEXT:    [[FLOOR:%.*]] = tail call float @llvm.floor.f32(float [[X]])
+; GFX6-IR-NEXT:    [[SUB:%.*]] = fsub nnan float [[X]], [[FLOOR]]
+; GFX6-IR-NEXT:    [[MIN:%.*]] = tail call float @llvm.minnum.f32(float [[SUB]], float f0x3F7FFFFF)
+; GFX6-IR-NEXT:    ret float [[MIN]]
+;
+; IR-FRACT-LABEL: define float @nnan_fsub_fract_f32_nonans(
+; IR-FRACT-SAME: float nofpclass(nan) [[X:%.*]]) {
+; IR-FRACT-NEXT:  [[ENTRY:.*:]]
+; IR-FRACT-NEXT:    [[MIN:%.*]] = call nnan float @llvm.amdgcn.fract.f32(float [[X]])
+; IR-FRACT-NEXT:    ret float [[MIN]]
+;
+; GFX6-LABEL: nnan_fsub_fract_f32_nonans:
+; GFX6:       ; %bb.0: ; %entry
+; GFX6-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX6-NEXT:    v_floor_f32_e32 v1, v0
+; GFX6-NEXT:    v_sub_f32_e32 v0, v0, v1
+; GFX6-NEXT:    v_min_f32_e32 v0, 0x3f7fffff, v0
+; GFX6-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX7-LABEL: nnan_fsub_fract_f32_nonans:
+; GFX7:       ; %bb.0: ; %entry
+; GFX7-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX7-NEXT:    v_fract_f32_e32 v0, v0
+; GFX7-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX8-LABEL: nnan_fsub_fract_f32_nonans:
+; GFX8:       ; %bb.0: ; %entry
+; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX8-NEXT:    v_fract_f32_e32 v0, v0
+; GFX8-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX11-LABEL: nnan_fsub_fract_f32_nonans:
+; GFX11:       ; %bb.0: ; %entry
+; GFX11-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-NEXT:    v_fract_f32_e32 v0, v0
+; GFX11-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX12-LABEL: nnan_fsub_fract_f32_nonans:
+; GFX12:       ; %bb.0: ; %entry
+; GFX12-NEXT:    s_wait_loadcnt_dscnt 0x0
+; GFX12-NEXT:    s_wait_expcnt 0x0
+; GFX12-NEXT:    s_wait_samplecnt 0x0
+; GFX12-NEXT:    s_wait_bvhcnt 0x0
+; GFX12-NEXT:    s_wait_kmcnt 0x0
+; GFX12-NEXT:    v_fract_f32_e32 v0, v0
+; GFX12-NEXT:    s_setpc_b64 s[30:31]
+entry:
+  %floor = tail call float @llvm.floor.f32(float %x)
+  %sub = fsub nnan float %x, %floor
+  %min = tail call float @llvm.minnum.f32(float %sub, float 0x3FEFFFFFE0000000)
+  ret float %min
+}
+
+define float @basic_fract_f32_nofpclass_nan_inf(float nofpclass(nan inf) %x) {
+; GFX6-IR-LABEL: define float @basic_fract_f32_nofpclass_nan_inf(
+; GFX6-IR-SAME: float nofpclass(nan inf) [[X:%.*]]) {
+; GFX6-IR-NEXT:  [[ENTRY:.*:]]
+; GFX6-IR-NEXT:    [[FLOOR:%.*]] = tail call float @llvm.floor.f32(float [[X]])
+; GFX6-IR-NEXT:    [[SUB:%.*]] = fsub float [[X]], [[FLOOR]]
+; GFX6-IR-NEXT:    [[MIN:%.*]] = tail call float @llvm.minnum.f32(float [[SUB]], float f0x3F7FFFFF)
+; GFX6-IR-NEXT:    ret float [[MIN]]
+;
+; IR-FRACT-LABEL: define float @basic_fract_f32_nofpclass_nan_inf(
+; IR-FRACT-SAME: float nofpclass(nan inf) [[X:%.*]]) {
+; IR-FRACT-NEXT:  [[ENTRY:.*:]]
+; IR-FRACT-NEXT:    [[MIN:%.*]] = call nnan float @llvm.amdgcn.fract.f32(float [[X]])
+; IR-FRACT-NEXT:    ret float [[MIN]]
+;
+; GFX6-LABEL: basic_fract_f32_nofpclass_nan_inf:
+; GFX6:       ; %bb.0: ; %entry
+; GFX6-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX6-NEXT:    v_floor_f32_e32 v1, v0
+; GFX6-NEXT:    v_sub_f32_e32 v0, v0, v1
+; GFX6-NEXT:    v_min_f32_e32 v0, 0x3f7fffff, v0
+; GFX6-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX7-LABEL: basic_fract_f32_nofpclass_nan_inf:
+; GFX7:       ; %bb.0: ; %entry
+; GFX7-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX7-NEXT:    v_fract_f32_e32 v0, v0
+; GFX7-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX8-LABEL: basic_fract_f32_nofpclass_nan_inf:
+; GFX8:       ; %bb.0: ; %entry
+; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX8-NEXT:    v_fract_f32_e32 v0, v0
+; GFX8-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX11-LABEL: basic_fract_f32_nofpclass_nan_inf:
+; GFX11:       ; %bb.0: ; %entry
+; GFX11-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-NEXT:    v_fract_f32_e32 v0, v0
+; GFX11-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX12-LABEL: basic_fract_f32_nofpclass_nan_inf:
+; GFX12:       ; %bb.0: ; %entry
+; GFX12-NEXT:    s_wait_loadcnt_dscnt 0x0
+; GFX12-NEXT:    s_wait_expcnt 0x0
+; GFX12-NEXT:    s_wait_samplecnt 0x0
+; GFX12-NEXT:    s_wait_bvhcnt 0x0
+; GFX12-NEXT:    s_wait_kmcnt 0x0
+; GFX12-NEXT:    v_fract_f32_e32 v0, v0
+; GFX12-NEXT:    s_setpc_b64 s[30:31]
+entry:
+  %floor = tail call float @llvm.floor.f32(float %x)
+  %sub = fsub float %x, %floor
+  %min = tail call float @llvm.minnum.f32(float %sub, float 0x3FEFFFFFE0000000)
+  ret float %min
+}
+
+; Negative test
+define float @not_fract_f32_wrong_const(float nofpclass(nan) %x) {
+; IR-LABEL: define float @not_fract_f32_wrong_const(
+; IR-SAME: float nofpclass(nan) [[X:%.*]]) {
+; IR-NEXT:  [[ENTRY:.*:]]
+; IR-NEXT:    [[FLOOR:%.*]] = tail call float @llvm.floor.f32(float [[X]])
+; IR-NEXT:    [[SUB:%.*]] = fsub float [[X]], [[FLOOR]]
+; IR-NEXT:    [[MIN:%.*]] = tail call float @llvm.minnum.f32(float [[SUB]], float f0x3F7FFFFE)
+; IR-NEXT:    ret float [[MIN]]
+;
+; GFX6-LABEL: not_fract_f32_wrong_const:
+; GFX6:       ; %bb.0: ; %entry
+; GFX6-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX6-NEXT:    v_floor_f32_e32 v1, v0
+; GFX6-NEXT:    v_sub_f32_e32 v0, v0, v1
+; GFX6-NEXT:    v_min_f32_e32 v0, 0x3f7ffffe, v0
+; GFX6-NEXT:    s_setpc_b64 s[30:31]
+; GFX7-LABEL: not_fract_f32_wrong_const:
+; GFX7:       ; %bb.0: ; %entry
+; GFX7-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX7-NEXT:    v_floor_f32_e32 v1, v0
+; GFX7-NEXT:    v_sub_f32_e32 v0, v0, v1
+; GFX7-NEXT:    v_min_f32_e32 v0, 0x3f7ffffe, v0
+; GFX7-NEXT:    s_setpc_b64 s[30:31]
+; GFX8-LABEL: not_fract_f32_wrong_const:
+; GFX8:       ; %bb.0: ; %entry
+; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX8-NEXT:    v_floor_f32_e32 v1, v0
+; GFX8-NEXT:    v_sub_f32_e32 v0, v0, v1
+; GFX8-NEXT:    v_min_f32_e32 v0, 0x3f7ffffe, v0
+; GFX8-NEXT:    s_setpc_b64 s[30:31]
+; GFX11-LABEL: not_fract_f32_wrong_const:
+; GFX11:       ; %bb.0: ; %entry
+; GFX11-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-NEXT:    v_floor_f32_e32 v1, v0
+; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX11-NEXT:    v_sub_f32_e32 v0, v0, v1
+; GFX11-NEXT:    v_min_f32_e32 v0, 0x3f7ffffe, v0
+; GFX11-NEXT:    s_setpc_b64 s[30:31]
+; GFX12-LABEL: not_fract_f32_wrong_const:
+; GFX12:       ; %bb.0: ; %entry
+; GFX12-NEXT:    s_wait_loadcnt_dscnt 0x0
+; GFX12-NEXT:    s_wait_expcnt 0x0
+; GFX12-NEXT:    s_wait_samplecnt 0x0
+; GFX12-NEXT:    s_wait_bvhcnt 0x0
+; GFX12-NEXT:    s_wait_kmcnt 0x0
+; GFX12-NEXT:    v_floor_f32_e32 v1, v0
+; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX12-NEXT:    v_sub_f32_e32 v0, v0, v1
+; GFX12-NEXT:    v_min_num_f32_e32 v0, 0x3f7ffffe, v0
 ; GFX12-NEXT:    s_setpc_b64 s[30:31]
 entry:
   %floor = tail call float @llvm.floor.f32(float %x)
@@ -1559,21 +1828,15 @@ entry:
 
 ; No inf check
 define float @select_nan_fract_f32(float %x) {
-; GFX6-IR-LABEL: define float @select_nan_fract_f32(
-; GFX6-IR-SAME: float [[X:%.*]]) {
-; GFX6-IR-NEXT:  [[ENTRY:.*:]]
-; GFX6-IR-NEXT:    [[FLOOR:%.*]] = tail call float @llvm.floor.f32(float [[X]])
-; GFX6-IR-NEXT:    [[SUB:%.*]] = fsub float [[X]], [[FLOOR]]
-; GFX6-IR-NEXT:    [[MIN:%.*]] = tail call float @llvm.minnum.f32(float [[SUB]], float f0x3F7FFFFF)
-; GFX6-IR-NEXT:    [[UNO:%.*]] = fcmp uno float [[X]], 0.000000e+00
-; GFX6-IR-NEXT:    [[COND:%.*]] = select i1 [[UNO]], float [[X]], float [[MIN]]
-; GFX6-IR-NEXT:    ret float [[COND]]
-;
-; IR-FRACT-LABEL: define float @select_nan_fract_f32(
-; IR-FRACT-SAME: float [[X:%.*]]) {
-; IR-FRACT-NEXT:  [[ENTRY:.*:]]
-; IR-FRACT-NEXT:    [[COND:%.*]] = call float @llvm.amdgcn.fract.f32(float [[X]])
-; IR-FRACT-NEXT:    ret float [[COND]]
+; IR-LABEL: define float @select_nan_fract_f32(
+; IR-SAME: float [[X:%.*]]) {
+; IR-NEXT:  [[ENTRY:.*:]]
+; IR-NEXT:    [[FLOOR:%.*]] = tail call float @llvm.floor.f32(float [[X]])
+; IR-NEXT:    [[SUB:%.*]] = fsub float [[X]], [[FLOOR]]
+; IR-NEXT:    [[MIN:%.*]] = tail call float @llvm.minnum.f32(float [[SUB]], float f0x3F7FFFFF)
+; IR-NEXT:    [[UNO:%.*]] = fcmp uno float [[X]], 0.000000e+00
+; IR-NEXT:    [[COND:%.*]] = select i1 [[UNO]], float [[X]], float [[MIN]]
+; IR-NEXT:    ret float [[COND]]
 ;
 ; GFX6-LABEL: select_nan_fract_f32:
 ; GFX6:       ; %bb.0: ; %entry
@@ -1584,21 +1847,39 @@ define float @select_nan_fract_f32(float %x) {
 ; GFX6-NEXT:    v_cmp_u_f32_e32 vcc, v0, v0
 ; GFX6-NEXT:    v_cndmask_b32_e32 v0, v1, v0, vcc
 ; GFX6-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX7-LABEL: select_nan_fract_f32:
 ; GFX7:       ; %bb.0: ; %entry
 ; GFX7-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX7-NEXT:    v_fract_f32_e32 v0, v0
+; GFX7-NEXT:    v_floor_f32_e32 v1, v0
+; GFX7-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX7-NEXT:    v_min_f32_e32 v1, 0x3f7fffff, v1
+; GFX7-NEXT:    v_cmp_u_f32_e32 vcc, v0, v0
+; GFX7-NEXT:    v_cndmask_b32_e32 v0, v1, v0, vcc
 ; GFX7-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX8-LABEL: select_nan_fract_f32:
 ; GFX8:       ; %bb.0: ; %entry
 ; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX8-NEXT:    v_fract_f32_e32 v0, v0
+; GFX8-NEXT:    v_floor_f32_e32 v1, v0
+; GFX8-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX8-NEXT:    v_min_f32_e32 v1, 0x3f7fffff, v1
+; GFX8-NEXT:    v_cmp_u_f32_e32 vcc, v0, v0
+; GFX8-NEXT:    v_cndmask_b32_e32 v0, v1, v0, vcc
 ; GFX8-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX11-LABEL: select_nan_fract_f32:
 ; GFX11:       ; %bb.0: ; %entry
 ; GFX11-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX11-NEXT:    v_fract_f32_e32 v0, v0
+; GFX11-NEXT:    v_floor_f32_e32 v1, v0
+; GFX11-NEXT:    v_cmp_u_f32_e32 vcc_lo, v0, v0
+; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX11-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX11-NEXT:    v_min_f32_e32 v1, 0x3f7fffff, v1
+; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_1)
+; GFX11-NEXT:    v_cndmask_b32_e32 v0, v1, v0, vcc_lo
 ; GFX11-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX12-LABEL: select_nan_fract_f32:
 ; GFX12:       ; %bb.0: ; %entry
 ; GFX12-NEXT:    s_wait_loadcnt_dscnt 0x0
@@ -1606,7 +1887,14 @@ define float @select_nan_fract_f32(float %x) {
 ; GFX12-NEXT:    s_wait_samplecnt 0x0
 ; GFX12-NEXT:    s_wait_bvhcnt 0x0
 ; GFX12-NEXT:    s_wait_kmcnt 0x0
-; GFX12-NEXT:    v_fract_f32_e32 v0, v0
+; GFX12-NEXT:    v_floor_f32_e32 v1, v0
+; GFX12-NEXT:    v_cmp_u_f32_e32 vcc_lo, v0, v0
+; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX12-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX12-NEXT:    v_min_num_f32_e32 v1, 0x3f7fffff, v1
+; GFX12-NEXT:    s_wait_alu depctr_va_vcc(0)
+; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_1)
+; GFX12-NEXT:    v_cndmask_b32_e32 v0, v1, v0, vcc_lo
 ; GFX12-NEXT:    s_setpc_b64 s[30:31]
 entry:
   %floor = tail call float @llvm.floor.f32(float %x)
@@ -1618,21 +1906,15 @@ entry:
 }
 
 define float @commuted_select_nan_fract_f32(float %x) {
-; GFX6-IR-LABEL: define float @commuted_select_nan_fract_f32(
-; GFX6-IR-SAME: float [[X:%.*]]) {
-; GFX6-IR-NEXT:  [[ENTRY:.*:]]
-; GFX6-IR-NEXT:    [[FLOOR:%.*]] = tail call float @llvm.floor.f32(float [[X]])
-; GFX6-IR-NEXT:    [[SUB:%.*]] = fsub float [[X]], [[FLOOR]]
-; GFX6-IR-NEXT:    [[MIN:%.*]] = tail call float @llvm.minnum.f32(float [[SUB]], float f0x3F7FFFFF)
-; GFX6-IR-NEXT:    [[UNO:%.*]] = fcmp ord float [[X]], 0.000000e+00
-; GFX6-IR-NEXT:    [[COND:%.*]] = select i1 [[UNO]], float [[MIN]], float [[X]]
-; GFX6-IR-NEXT:    ret float [[COND]]
-;
-; IR-FRACT-LABEL: define float @commuted_select_nan_fract_f32(
-; IR-FRACT-SAME: float [[X:%.*]]) {
-; IR-FRACT-NEXT:  [[ENTRY:.*:]]
-; IR-FRACT-NEXT:    [[COND:%.*]] = call float @llvm.amdgcn.fract.f32(float [[X]])
-; IR-FRACT-NEXT:    ret float [[COND]]
+; IR-LABEL: define float @commuted_select_nan_fract_f32(
+; IR-SAME: float [[X:%.*]]) {
+; IR-NEXT:  [[ENTRY:.*:]]
+; IR-NEXT:    [[FLOOR:%.*]] = tail call float @llvm.floor.f32(float [[X]])
+; IR-NEXT:    [[SUB:%.*]] = fsub float [[X]], [[FLOOR]]
+; IR-NEXT:    [[MIN:%.*]] = tail call float @llvm.minnum.f32(float [[SUB]], float f0x3F7FFFFF)
+; IR-NEXT:    [[UNO:%.*]] = fcmp ord float [[X]], 0.000000e+00
+; IR-NEXT:    [[COND:%.*]] = select i1 [[UNO]], float [[MIN]], float [[X]]
+; IR-NEXT:    ret float [[COND]]
 ;
 ; GFX6-LABEL: commuted_select_nan_fract_f32:
 ; GFX6:       ; %bb.0: ; %entry
@@ -1643,21 +1925,39 @@ define float @commuted_select_nan_fract_f32(float %x) {
 ; GFX6-NEXT:    v_cmp_o_f32_e32 vcc, v0, v0
 ; GFX6-NEXT:    v_cndmask_b32_e32 v0, v0, v1, vcc
 ; GFX6-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX7-LABEL: commuted_select_nan_fract_f32:
 ; GFX7:       ; %bb.0: ; %entry
 ; GFX7-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX7-NEXT:    v_fract_f32_e32 v0, v0
+; GFX7-NEXT:    v_floor_f32_e32 v1, v0
+; GFX7-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX7-NEXT:    v_min_f32_e32 v1, 0x3f7fffff, v1
+; GFX7-NEXT:    v_cmp_o_f32_e32 vcc, v0, v0
+; GFX7-NEXT:    v_cndmask_b32_e32 v0, v0, v1, vcc
 ; GFX7-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX8-LABEL: commuted_select_nan_fract_f32:
 ; GFX8:       ; %bb.0: ; %entry
 ; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX8-NEXT:    v_fract_f32_e32 v0, v0
+; GFX8-NEXT:    v_floor_f32_e32 v1, v0
+; GFX8-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX8-NEXT:    v_min_f32_e32 v1, 0x3f7fffff, v1
+; GFX8-NEXT:    v_cmp_o_f32_e32 vcc, v0, v0
+; GFX8-NEXT:    v_cndmask_b32_e32 v0, v0, v1, vcc
 ; GFX8-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX11-LABEL: commuted_select_nan_fract_f32:
 ; GFX11:       ; %bb.0: ; %entry
 ; GFX11-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX11-NEXT:    v_fract_f32_e32 v0, v0
+; GFX11-NEXT:    v_floor_f32_e32 v1, v0
+; GFX11-NEXT:    v_cmp_o_f32_e32 vcc_lo, v0, v0
+; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX11-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX11-NEXT:    v_min_f32_e32 v1, 0x3f7fffff, v1
+; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_1)
+; GFX11-NEXT:    v_cndmask_b32_e32 v0, v0, v1, vcc_lo
 ; GFX11-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX12-LABEL: commuted_select_nan_fract_f32:
 ; GFX12:       ; %bb.0: ; %entry
 ; GFX12-NEXT:    s_wait_loadcnt_dscnt 0x0
@@ -1665,7 +1965,14 @@ define float @commuted_select_nan_fract_f32(float %x) {
 ; GFX12-NEXT:    s_wait_samplecnt 0x0
 ; GFX12-NEXT:    s_wait_bvhcnt 0x0
 ; GFX12-NEXT:    s_wait_kmcnt 0x0
-; GFX12-NEXT:    v_fract_f32_e32 v0, v0
+; GFX12-NEXT:    v_floor_f32_e32 v1, v0
+; GFX12-NEXT:    v_cmp_o_f32_e32 vcc_lo, v0, v0
+; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX12-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX12-NEXT:    v_min_num_f32_e32 v1, 0x3f7fffff, v1
+; GFX12-NEXT:    s_wait_alu depctr_va_vcc(0)
+; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_1)
+; GFX12-NEXT:    v_cndmask_b32_e32 v0, v0, v1, vcc_lo
 ; GFX12-NEXT:    s_setpc_b64 s[30:31]
 entry:
   %floor = tail call float @llvm.floor.f32(float %x)
@@ -1751,27 +2058,13 @@ entry:
 }
 
 define half @basic_fract_f16_nonan(half nofpclass(nan) %x) {
-; GFX6-IR-LABEL: define half @basic_fract_f16_nonan(
-; GFX6-IR-SAME: half nofpclass(nan) [[X:%.*]]) {
-; GFX6-IR-NEXT:  [[ENTRY:.*:]]
-; GFX6-IR-NEXT:    [[FLOOR:%.*]] = tail call half @llvm.floor.f16(half [[X]])
-; GFX6-IR-NEXT:    [[SUB:%.*]] = fsub half [[X]], [[FLOOR]]
-; GFX6-IR-NEXT:    [[MIN:%.*]] = tail call half @llvm.minnum.f16(half [[SUB]], half 9.995110e-01)
-; GFX6-IR-NEXT:    ret half [[MIN]]
-;
-; GFX7-IR-LABEL: define half @basic_fract_f16_nonan(
-; GFX7-IR-SAME: half nofpclass(nan) [[X:%.*]]) {
-; GFX7-IR-NEXT:  [[ENTRY:.*:]]
-; GFX7-IR-NEXT:    [[FLOOR:%.*]] = tail call half @llvm.floor.f16(half [[X]])
-; GFX7-IR-NEXT:    [[SUB:%.*]] = fsub half [[X]], [[FLOOR]]
-; GFX7-IR-NEXT:    [[MIN:%.*]] = tail call half @llvm.minnum.f16(half [[SUB]], half 9.995110e-01)
-; GFX7-IR-NEXT:    ret half [[MIN]]
-;
-; IR-LEGALF16-LABEL: define half @basic_fract_f16_nonan(
-; IR-LEGALF16-SAME: half nofpclass(nan) [[X:%.*]]) {
-; IR-LEGALF16-NEXT:  [[ENTRY:.*:]]
-; IR-LEGALF16-NEXT:    [[MIN:%.*]] = call nnan half @llvm.amdgcn.fract.f16(half [[X]])
-; IR-LEGALF16-NEXT:    ret half [[MIN]]
+; IR-LABEL: define half @basic_fract_f16_nonan(
+; IR-SAME: half nofpclass(nan) [[X:%.*]]) {
+; IR-NEXT:  [[ENTRY:.*:]]
+; IR-NEXT:    [[FLOOR:%.*]] = tail call half @llvm.floor.f16(half [[X]])
+; IR-NEXT:    [[SUB:%.*]] = fsub half [[X]], [[FLOOR]]
+; IR-NEXT:    [[MIN:%.*]] = tail call half @llvm.minnum.f16(half [[SUB]], half 9.995110e-01)
+; IR-NEXT:    ret half [[MIN]]
 ;
 ; GFX6-LABEL: basic_fract_f16_nonan:
 ; GFX6:       ; %bb.0: ; %entry
@@ -1786,6 +2079,7 @@ define half @basic_fract_f16_nonan(half nofpclass(nan) %x) {
 ; GFX6-NEXT:    v_min_f32_e32 v0, 0x3f7fe000, v0
 ; GFX6-NEXT:    v_cvt_f16_f32_e32 v0, v0
 ; GFX6-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX7-LABEL: basic_fract_f16_nonan:
 ; GFX7:       ; %bb.0: ; %entry
 ; GFX7-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
@@ -1799,21 +2093,33 @@ define half @basic_fract_f16_nonan(half nofpclass(nan) %x) {
 ; GFX7-NEXT:    v_min_f32_e32 v0, 0x3f7fe000, v0
 ; GFX7-NEXT:    v_cvt_f16_f32_e32 v0, v0
 ; GFX7-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX8-LABEL: basic_fract_f16_nonan:
 ; GFX8:       ; %bb.0: ; %entry
 ; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX8-NEXT:    v_fract_f16_e32 v0, v0
+; GFX8-NEXT:    v_floor_f16_e32 v1, v0
+; GFX8-NEXT:    v_sub_f16_e32 v0, v0, v1
+; GFX8-NEXT:    v_min_f16_e32 v0, 0x3bff, v0
 ; GFX8-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX11-TRUE16-LABEL: basic_fract_f16_nonan:
 ; GFX11-TRUE16:       ; %bb.0: ; %entry
 ; GFX11-TRUE16-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX11-TRUE16-NEXT:    v_fract_f16_e32 v0.l, v0.l
+; GFX11-TRUE16-NEXT:    v_floor_f16_e32 v0.h, v0.l
+; GFX11-TRUE16-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX11-TRUE16-NEXT:    v_sub_f16_e32 v0.l, v0.l, v0.h
+; GFX11-TRUE16-NEXT:    v_min_f16_e32 v0.l, 0x3bff, v0.l
 ; GFX11-TRUE16-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX11-FAKE16-LABEL: basic_fract_f16_nonan:
 ; GFX11-FAKE16:       ; %bb.0: ; %entry
 ; GFX11-FAKE16-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX11-FAKE16-NEXT:    v_fract_f16_e32 v0, v0
+; GFX11-FAKE16-NEXT:    v_floor_f16_e32 v1, v0
+; GFX11-FAKE16-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX11-FAKE16-NEXT:    v_sub_f16_e32 v0, v0, v1
+; GFX11-FAKE16-NEXT:    v_min_f16_e32 v0, 0x3bff, v0
 ; GFX11-FAKE16-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX12-TRUE16-LABEL: basic_fract_f16_nonan:
 ; GFX12-TRUE16:       ; %bb.0: ; %entry
 ; GFX12-TRUE16-NEXT:    s_wait_loadcnt_dscnt 0x0
@@ -1821,8 +2127,12 @@ define half @basic_fract_f16_nonan(half nofpclass(nan) %x) {
 ; GFX12-TRUE16-NEXT:    s_wait_samplecnt 0x0
 ; GFX12-TRUE16-NEXT:    s_wait_bvhcnt 0x0
 ; GFX12-TRUE16-NEXT:    s_wait_kmcnt 0x0
-; GFX12-TRUE16-NEXT:    v_fract_f16_e32 v0.l, v0.l
+; GFX12-TRUE16-NEXT:    v_floor_f16_e32 v0.h, v0.l
+; GFX12-TRUE16-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX12-TRUE16-NEXT:    v_sub_f16_e32 v0.l, v0.l, v0.h
+; GFX12-TRUE16-NEXT:    v_min_num_f16_e32 v0.l, 0x3bff, v0.l
 ; GFX12-TRUE16-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX12-FAKE16-LABEL: basic_fract_f16_nonan:
 ; GFX12-FAKE16:       ; %bb.0: ; %entry
 ; GFX12-FAKE16-NEXT:    s_wait_loadcnt_dscnt 0x0
@@ -1830,7 +2140,10 @@ define half @basic_fract_f16_nonan(half nofpclass(nan) %x) {
 ; GFX12-FAKE16-NEXT:    s_wait_samplecnt 0x0
 ; GFX12-FAKE16-NEXT:    s_wait_bvhcnt 0x0
 ; GFX12-FAKE16-NEXT:    s_wait_kmcnt 0x0
-; GFX12-FAKE16-NEXT:    v_fract_f16_e32 v0, v0
+; GFX12-FAKE16-NEXT:    v_floor_f16_e32 v1, v0
+; GFX12-FAKE16-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX12-FAKE16-NEXT:    v_sub_f16_e32 v0, v0, v1
+; GFX12-FAKE16-NEXT:    v_min_num_f16_e32 v0, 0x3bff, v0
 ; GFX12-FAKE16-NEXT:    s_setpc_b64 s[30:31]
 entry:
   %floor = tail call half @llvm.floor.f16(half %x)
@@ -1840,32 +2153,13 @@ entry:
 }
 
 define <2 x half> @basic_fract_v2f16_nonan(<2 x half> nofpclass(nan) %x) {
-; GFX6-IR-LABEL: define <2 x half> @basic_fract_v2f16_nonan(
-; GFX6-IR-SAME: <2 x half> nofpclass(nan) [[X:%.*]]) {
-; GFX6-IR-NEXT:  [[ENTRY:.*:]]
-; GFX6-IR-NEXT:    [[FLOOR:%.*]] = tail call <2 x half> @llvm.floor.v2f16(<2 x half> [[X]])
-; GFX6-IR-NEXT:    [[SUB:%.*]] = fsub <2 x half> [[X]], [[FLOOR]]
-; GFX6-IR-NEXT:    [[MIN:%.*]] = tail call <2 x half> @llvm.minnum.v2f16(<2 x half> [[SUB]], <2 x half> splat (half 9.995110e-01))
-; GFX6-IR-NEXT:    ret <2 x half> [[MIN]]
-;
-; GFX7-IR-LABEL: define <2 x half> @basic_fract_v2f16_nonan(
-; GFX7-IR-SAME: <2 x half> nofpclass(nan) [[X:%.*]]) {
-; GFX7-IR-NEXT:  [[ENTRY:.*:]]
-; GFX7-IR-NEXT:    [[FLOOR:%.*]] = tail call <2 x half> @llvm.floor.v2f16(<2 x half> [[X]])
-; GFX7-IR-NEXT:    [[SUB:%.*]] = fsub <2 x half> [[X]], [[FLOOR]]
-; GFX7-IR-NEXT:    [[MIN:%.*]] = tail call <2 x half> @llvm.minnum.v2f16(<2 x half> [[SUB]], <2 x half> splat (half 9.995110e-01))
-; GFX7-IR-NEXT:    ret <2 x half> [[MIN]]
-;
-; IR-LEGALF16-LABEL: define <2 x half> @basic_fract_v2f16_nonan(
-; IR-LEGALF16-SAME: <2 x half> nofpclass(nan) [[X:%.*]]) {
-; IR-LEGALF16-NEXT:  [[ENTRY:.*:]]
-; IR-LEGALF16-NEXT:    [[TMP0:%.*]] = extractelement <2 x half> [[X]], i64 0
-; IR-LEGALF16-NEXT:    [[TMP1:%.*]] = extractelement <2 x half> [[X]], i64 1
-; IR-LEGALF16-NEXT:    [[TMP2:%.*]] = call nnan half @llvm.amdgcn.fract.f16(half [[TMP0]])
-; IR-LEGALF16-NEXT:    [[TMP3:%.*]] = call nnan half @llvm.amdgcn.fract.f16(half [[TMP1]])
-; IR-LEGALF16-NEXT:    [[TMP4:%.*]] = insertelement <2 x half> poison, half [[TMP2]], i64 0
-; IR-LEGALF16-NEXT:    [[MIN:%.*]] = insertelement <2 x half> [[TMP4]], half [[TMP3]], i64 1
-; IR-LEGALF16-NEXT:    ret <2 x half> [[MIN]]
+; IR-LABEL: define <2 x half> @basic_fract_v2f16_nonan(
+; IR-SAME: <2 x half> nofpclass(nan) [[X:%.*]]) {
+; IR-NEXT:  [[ENTRY:.*:]]
+; IR-NEXT:    [[FLOOR:%.*]] = tail call <2 x half> @llvm.floor.v2f16(<2 x half> [[X]])
+; IR-NEXT:    [[SUB:%.*]] = fsub <2 x half> [[X]], [[FLOOR]]
+; IR-NEXT:    [[MIN:%.*]] = tail call <2 x half> @llvm.minnum.v2f16(<2 x half> [[SUB]], <2 x half> splat (half 9.995110e-01))
+; IR-NEXT:    ret <2 x half> [[MIN]]
 ;
 ; GFX6-LABEL: basic_fract_v2f16_nonan:
 ; GFX6:       ; %bb.0: ; %entry
@@ -1892,6 +2186,7 @@ define <2 x half> @basic_fract_v2f16_nonan(<2 x half> nofpclass(nan) %x) {
 ; GFX6-NEXT:    v_lshlrev_b32_e32 v1, 16, v1
 ; GFX6-NEXT:    v_or_b32_e32 v0, v0, v1
 ; GFX6-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX7-LABEL: basic_fract_v2f16_nonan:
 ; GFX7:       ; %bb.0: ; %entry
 ; GFX7-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
@@ -1917,30 +2212,43 @@ define <2 x half> @basic_fract_v2f16_nonan(<2 x half> nofpclass(nan) %x) {
 ; GFX7-NEXT:    v_lshlrev_b32_e32 v1, 16, v1
 ; GFX7-NEXT:    v_or_b32_e32 v0, v0, v1
 ; GFX7-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX8-LABEL: basic_fract_v2f16_nonan:
 ; GFX8:       ; %bb.0: ; %entry
 ; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX8-NEXT:    v_fract_f16_e32 v1, v0
-; GFX8-NEXT:    v_fract_f16_sdwa v0, v0 dst_sel:DWORD dst_unused:UNUSED_PAD src0_sel:WORD_1
-; GFX8-NEXT:    v_pack_b32_f16 v0, v1, v0
+; GFX8-NEXT:    v_floor_f16_sdwa v1, v0 dst_sel:DWORD dst_unused:UNUSED_PAD src0_sel:WORD_1
+; GFX8-NEXT:    v_floor_f16_e32 v2, v0
+; GFX8-NEXT:    v_pack_b32_f16 v1, v2, v1
+; GFX8-NEXT:    v_pk_add_f16 v0, v0, v1 neg_lo:[0,1] neg_hi:[0,1]
+; GFX8-NEXT:    s_movk_i32 s4, 0x3bff
+; GFX8-NEXT:    v_pk_min_f16 v0, v0, s4 op_sel_hi:[1,0]
 ; GFX8-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX11-TRUE16-LABEL: basic_fract_v2f16_nonan:
 ; GFX11-TRUE16:       ; %bb.0: ; %entry
 ; GFX11-TRUE16-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
 ; GFX11-TRUE16-NEXT:    v_lshrrev_b32_e32 v1, 16, v0
-; GFX11-TRUE16-NEXT:    v_fract_f16_e32 v0.l, v0.l
-; GFX11-TRUE16-NEXT:    s_delay_alu instid0(VALU_DEP_2)
-; GFX11-TRUE16-NEXT:    v_fract_f16_e32 v0.h, v1.l
+; GFX11-TRUE16-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(SKIP_1) | instid1(VALU_DEP_1)
+; GFX11-TRUE16-NEXT:    v_floor_f16_e32 v1.h, v1.l
+; GFX11-TRUE16-NEXT:    v_floor_f16_e32 v1.l, v0.l
+; GFX11-TRUE16-NEXT:    v_pk_add_f16 v0, v0, v1 neg_lo:[0,1] neg_hi:[0,1]
+; GFX11-TRUE16-NEXT:    s_delay_alu instid0(VALU_DEP_1)
+; GFX11-TRUE16-NEXT:    v_pk_min_f16 v0, 0x3bff, v0 op_sel_hi:[0,1]
 ; GFX11-TRUE16-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX11-FAKE16-LABEL: basic_fract_v2f16_nonan:
 ; GFX11-FAKE16:       ; %bb.0: ; %entry
 ; GFX11-FAKE16-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
 ; GFX11-FAKE16-NEXT:    v_lshrrev_b32_e32 v1, 16, v0
-; GFX11-FAKE16-NEXT:    v_fract_f16_e32 v0, v0
+; GFX11-FAKE16-NEXT:    v_floor_f16_e32 v2, v0
 ; GFX11-FAKE16-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_1)
-; GFX11-FAKE16-NEXT:    v_fract_f16_e32 v1, v1
-; GFX11-FAKE16-NEXT:    v_pack_b32_f16 v0, v0, v1
+; GFX11-FAKE16-NEXT:    v_floor_f16_e32 v1, v1
+; GFX11-FAKE16-NEXT:    v_pack_b32_f16 v1, v2, v1
+; GFX11-FAKE16-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX11-FAKE16-NEXT:    v_pk_add_f16 v0, v0, v1 neg_lo:[0,1] neg_hi:[0,1]
+; GFX11-FAKE16-NEXT:    v_pk_min_f16 v0, 0x3bff, v0 op_sel_hi:[0,1]
 ; GFX11-FAKE16-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX12-TRUE16-LABEL: basic_fract_v2f16_nonan:
 ; GFX12-TRUE16:       ; %bb.0: ; %entry
 ; GFX12-TRUE16-NEXT:    s_wait_loadcnt_dscnt 0x0
@@ -1949,10 +2257,14 @@ define <2 x half> @basic_fract_v2f16_nonan(<2 x half> nofpclass(nan) %x) {
 ; GFX12-TRUE16-NEXT:    s_wait_bvhcnt 0x0
 ; GFX12-TRUE16-NEXT:    s_wait_kmcnt 0x0
 ; GFX12-TRUE16-NEXT:    v_lshrrev_b32_e32 v1, 16, v0
-; GFX12-TRUE16-NEXT:    v_fract_f16_e32 v0.l, v0.l
-; GFX12-TRUE16-NEXT:    s_delay_alu instid0(VALU_DEP_2)
-; GFX12-TRUE16-NEXT:    v_fract_f16_e32 v0.h, v1.l
+; GFX12-TRUE16-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(SKIP_1) | instid1(VALU_DEP_1)
+; GFX12-TRUE16-NEXT:    v_floor_f16_e32 v1.h, v1.l
+; GFX12-TRUE16-NEXT:    v_floor_f16_e32 v1.l, v0.l
+; GFX12-TRUE16-NEXT:    v_pk_add_f16 v0, v0, v1 neg_lo:[0,1] neg_hi:[0,1]
+; GFX12-TRUE16-NEXT:    s_delay_alu instid0(VALU_DEP_1)
+; GFX12-TRUE16-NEXT:    v_pk_min_num_f16 v0, 0x3bff, v0 op_sel_hi:[0,1]
 ; GFX12-TRUE16-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX12-FAKE16-LABEL: basic_fract_v2f16_nonan:
 ; GFX12-FAKE16:       ; %bb.0: ; %entry
 ; GFX12-FAKE16-NEXT:    s_wait_loadcnt_dscnt 0x0
@@ -1961,10 +2273,13 @@ define <2 x half> @basic_fract_v2f16_nonan(<2 x half> nofpclass(nan) %x) {
 ; GFX12-FAKE16-NEXT:    s_wait_bvhcnt 0x0
 ; GFX12-FAKE16-NEXT:    s_wait_kmcnt 0x0
 ; GFX12-FAKE16-NEXT:    v_lshrrev_b32_e32 v1, 16, v0
-; GFX12-FAKE16-NEXT:    v_fract_f16_e32 v0, v0
+; GFX12-FAKE16-NEXT:    v_floor_f16_e32 v2, v0
 ; GFX12-FAKE16-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_1)
-; GFX12-FAKE16-NEXT:    v_fract_f16_e32 v1, v1
-; GFX12-FAKE16-NEXT:    v_pack_b32_f16 v0, v0, v1
+; GFX12-FAKE16-NEXT:    v_floor_f16_e32 v1, v1
+; GFX12-FAKE16-NEXT:    v_pack_b32_f16 v1, v2, v1
+; GFX12-FAKE16-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX12-FAKE16-NEXT:    v_pk_add_f16 v0, v0, v1 neg_lo:[0,1] neg_hi:[0,1]
+; GFX12-FAKE16-NEXT:    v_pk_min_num_f16 v0, 0x3bff, v0 op_sel_hi:[0,1]
 ; GFX12-FAKE16-NEXT:    s_setpc_b64 s[30:31]
 entry:
   %floor = tail call <2 x half> @llvm.floor.v2f16(<2 x half> %x)
@@ -1974,19 +2289,13 @@ entry:
 }
 
 define double @basic_fract_f64_nanans(double nofpclass(nan) %x) {
-; GFX6-IR-LABEL: define double @basic_fract_f64_nanans(
-; GFX6-IR-SAME: double nofpclass(nan) [[X:%.*]]) {
-; GFX6-IR-NEXT:  [[ENTRY:.*:]]
-; GFX6-IR-NEXT:    [[FLOOR:%.*]] = tail call double @llvm.floor.f64(double [[X]])
-; GFX6-IR-NEXT:    [[SUB:%.*]] = fsub double [[X]], [[FLOOR]]
-; GFX6-IR-NEXT:    [[MIN:%.*]] = tail call double @llvm.minnum.f64(double [[SUB]], double f0x3FEFFFFFFFFFFFFF)
-; GFX6-IR-NEXT:    ret double [[MIN]]
-;
-; IR-FRACT-LABEL: define double @basic_fract_f64_nanans(
-; IR-FRACT-SAME: double nofpclass(nan) [[X:%.*]]) {
-; IR-FRACT-NEXT:  [[ENTRY:.*:]]
-; IR-FRACT-NEXT:    [[MIN:%.*]] = call nnan double @llvm.amdgcn.fract.f64(double [[X]])
-; IR-FRACT-NEXT:    ret double [[MIN]]
+; IR-LABEL: define double @basic_fract_f64_nanans(
+; IR-SAME: double nofpclass(nan) [[X:%.*]]) {
+; IR-NEXT:  [[ENTRY:.*:]]
+; IR-NEXT:    [[FLOOR:%.*]] = tail call double @llvm.floor.f64(double [[X]])
+; IR-NEXT:    [[SUB:%.*]] = fsub double [[X]], [[FLOOR]]
+; IR-NEXT:    [[MIN:%.*]] = tail call double @llvm.minnum.f64(double [[SUB]], double f0x3FEFFFFFFFFFFFFF)
+; IR-NEXT:    ret double [[MIN]]
 ;
 ; GFX6-LABEL: basic_fract_f64_nanans:
 ; GFX6:       ; %bb.0: ; %entry
@@ -2004,21 +2313,38 @@ define double @basic_fract_f64_nanans(double nofpclass(nan) %x) {
 ; GFX6-NEXT:    v_add_f64 v[0:1], v[0:1], -v[2:3]
 ; GFX6-NEXT:    v_min_f64 v[0:1], v[0:1], s[4:5]
 ; GFX6-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX7-LABEL: basic_fract_f64_nanans:
 ; GFX7:       ; %bb.0: ; %entry
 ; GFX7-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX7-NEXT:    v_fract_f64_e32 v[0:1], v[0:1]
+; GFX7-NEXT:    v_floor_f64_e32 v[2:3], v[0:1]
+; GFX7-NEXT:    s_mov_b32 s4, -1
+; GFX7-NEXT:    s_mov_b32 s5, 0x3fefffff
+; GFX7-NEXT:    v_add_f64 v[0:1], v[0:1], -v[2:3]
+; GFX7-NEXT:    v_min_f64 v[0:1], v[0:1], s[4:5]
 ; GFX7-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX8-LABEL: basic_fract_f64_nanans:
 ; GFX8:       ; %bb.0: ; %entry
 ; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX8-NEXT:    v_fract_f64_e32 v[0:1], v[0:1]
+; GFX8-NEXT:    v_floor_f64_e32 v[2:3], v[0:1]
+; GFX8-NEXT:    s_mov_b32 s4, -1
+; GFX8-NEXT:    s_mov_b32 s5, 0x3fefffff
+; GFX8-NEXT:    v_add_f64 v[0:1], v[0:1], -v[2:3]
+; GFX8-NEXT:    v_min_f64 v[0:1], v[0:1], s[4:5]
 ; GFX8-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX11-LABEL: basic_fract_f64_nanans:
 ; GFX11:       ; %bb.0: ; %entry
 ; GFX11-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX11-NEXT:    v_fract_f64_e32 v[0:1], v[0:1]
+; GFX11-NEXT:    v_floor_f64_e32 v[2:3], v[0:1]
+; GFX11-NEXT:    s_mov_b32 s0, -1
+; GFX11-NEXT:    s_mov_b32 s1, 0x3fefffff
+; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX11-NEXT:    v_add_f64 v[0:1], v[0:1], -v[2:3]
+; GFX11-NEXT:    v_min_f64 v[0:1], v[0:1], s[0:1]
 ; GFX11-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX12-LABEL: basic_fract_f64_nanans:
 ; GFX12:       ; %bb.0: ; %entry
 ; GFX12-NEXT:    s_wait_loadcnt_dscnt 0x0
@@ -2026,7 +2352,13 @@ define double @basic_fract_f64_nanans(double nofpclass(nan) %x) {
 ; GFX12-NEXT:    s_wait_samplecnt 0x0
 ; GFX12-NEXT:    s_wait_bvhcnt 0x0
 ; GFX12-NEXT:    s_wait_kmcnt 0x0
-; GFX12-NEXT:    v_fract_f64_e32 v[0:1], v[0:1]
+; GFX12-NEXT:    v_floor_f64_e32 v[2:3], v[0:1]
+; GFX12-NEXT:    s_mov_b32 s0, -1
+; GFX12-NEXT:    s_mov_b32 s1, 0x3fefffff
+; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(SKIP_1) | instid1(VALU_DEP_1)
+; GFX12-NEXT:    v_add_f64_e64 v[0:1], v[0:1], -v[2:3]
+; GFX12-NEXT:    s_wait_alu depctr_sa_sdst(0)
+; GFX12-NEXT:    v_min_num_f64_e32 v[0:1], s[0:1], v[0:1]
 ; GFX12-NEXT:    s_setpc_b64 s[30:31]
 entry:
   %floor = tail call double @llvm.floor.f64(double %x)
@@ -2036,35 +2368,16 @@ entry:
 }
 
 define half @safe_math_fract_f16_noinf_check(half %x, ptr addrspace(1) writeonly captures(none) %ip) {
-; GFX6-IR-LABEL: define half @safe_math_fract_f16_noinf_check(
-; GFX6-IR-SAME: half [[X:%.*]], ptr addrspace(1) writeonly captures(none) [[IP:%.*]]) {
-; GFX6-IR-NEXT:  [[ENTRY:.*:]]
-; GFX6-IR-NEXT:    [[FLOOR:%.*]] = tail call half @llvm.floor.f16(half [[X]])
-; GFX6-IR-NEXT:    [[SUB:%.*]] = fsub half [[X]], [[FLOOR]]
-; GFX6-IR-NEXT:    [[MIN:%.*]] = tail call half @llvm.minnum.f16(half [[SUB]], half 9.995110e-01)
-; GFX6-IR-NEXT:    [[UNO:%.*]] = fcmp uno half [[X]], 0.000000e+00
-; GFX6-IR-NEXT:    [[COND:%.*]] = select i1 [[UNO]], half [[X]], half [[MIN]]
-; GFX6-IR-NEXT:    store half [[FLOOR]], ptr addrspace(1) [[IP]], align 4
-; GFX6-IR-NEXT:    ret half [[COND]]
-;
-; GFX7-IR-LABEL: define half @safe_math_fract_f16_noinf_check(
-; GFX7-IR-SAME: half [[X:%.*]], ptr addrspace(1) writeonly captures(none) [[IP:%.*]]) {
-; GFX7-IR-NEXT:  [[ENTRY:.*:]]
-; GFX7-IR-NEXT:    [[FLOOR:%.*]] = tail call half @llvm.floor.f16(half [[X]])
-; GFX7-IR-NEXT:    [[SUB:%.*]] = fsub half [[X]], [[FLOOR]]
-; GFX7-IR-NEXT:    [[MIN:%.*]] = tail call half @llvm.minnum.f16(half [[SUB]], half 9.995110e-01)
-; GFX7-IR-NEXT:    [[UNO:%.*]] = fcmp uno half [[X]], 0.000000e+00
-; GFX7-IR-NEXT:    [[COND:%.*]] = select i1 [[UNO]], half [[X]], half [[MIN]]
-; GFX7-IR-NEXT:    store half [[FLOOR]], ptr addrspace(1) [[IP]], align 4
-; GFX7-IR-NEXT:    ret half [[COND]]
-;
-; IR-LEGALF16-LABEL: define half @safe_math_fract_f16_noinf_check(
-; IR-LEGALF16-SAME: half [[X:%.*]], ptr addrspace(1) writeonly captures(none) [[IP:%.*]]) {
-; IR-LEGALF16-NEXT:  [[ENTRY:.*:]]
-; IR-LEGALF16-NEXT:    [[FLOOR:%.*]] = tail call half @llvm.floor.f16(half [[X]])
-; IR-LEGALF16-NEXT:    [[COND:%.*]] = call half @llvm.amdgcn.fract.f16(half [[X]])
-; IR-LEGALF16-NEXT:    store half [[FLOOR]], ptr addrspace(1) [[IP]], align 4
-; IR-LEGALF16-NEXT:    ret half [[COND]]
+; IR-LABEL: define half @safe_math_fract_f16_noinf_check(
+; IR-SAME: half [[X:%.*]], ptr addrspace(1) writeonly captures(none) [[IP:%.*]]) {
+; IR-NEXT:  [[ENTRY:.*:]]
+; IR-NEXT:    [[FLOOR:%.*]] = tail call half @llvm.floor.f16(half [[X]])
+; IR-NEXT:    [[SUB:%.*]] = fsub half [[X]], [[FLOOR]]
+; IR-NEXT:    [[MIN:%.*]] = tail call half @llvm.minnum.f16(half [[SUB]], half 9.995110e-01)
+; IR-NEXT:    [[UNO:%.*]] = fcmp uno half [[X]], 0.000000e+00
+; IR-NEXT:    [[COND:%.*]] = select i1 [[UNO]], half [[X]], half [[MIN]]
+; IR-NEXT:    store half [[FLOOR]], ptr addrspace(1) [[IP]], align 4
+; IR-NEXT:    ret half [[COND]]
 ;
 ; GFX6-LABEL: safe_math_fract_f16_noinf_check:
 ; GFX6:       ; %bb.0: ; %entry
@@ -2087,6 +2400,7 @@ define half @safe_math_fract_f16_noinf_check(half %x, ptr addrspace(1) writeonly
 ; GFX6-NEXT:    v_cndmask_b32_e32 v0, v5, v0, vcc
 ; GFX6-NEXT:    s_waitcnt vmcnt(0) expcnt(0)
 ; GFX6-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX7-LABEL: safe_math_fract_f16_noinf_check:
 ; GFX7:       ; %bb.0: ; %entry
 ; GFX7-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
@@ -2108,28 +2422,43 @@ define half @safe_math_fract_f16_noinf_check(half %x, ptr addrspace(1) writeonly
 ; GFX7-NEXT:    v_cndmask_b32_e32 v0, v5, v0, vcc
 ; GFX7-NEXT:    s_waitcnt vmcnt(0)
 ; GFX7-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX8-LABEL: safe_math_fract_f16_noinf_check:
 ; GFX8:       ; %bb.0: ; %entry
 ; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
 ; GFX8-NEXT:    v_floor_f16_e32 v3, v0
-; GFX8-NEXT:    v_fract_f16_e32 v0, v0
+; GFX8-NEXT:    v_sub_f16_e32 v4, v0, v3
+; GFX8-NEXT:    v_min_f16_e32 v4, 0x3bff, v4
+; GFX8-NEXT:    v_cmp_u_f16_e32 vcc, v0, v0
+; GFX8-NEXT:    v_cndmask_b32_e32 v0, v4, v0, vcc
 ; GFX8-NEXT:    global_store_short v[1:2], v3, off
 ; GFX8-NEXT:    s_waitcnt vmcnt(0)
 ; GFX8-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX11-TRUE16-LABEL: safe_math_fract_f16_noinf_check:
 ; GFX11-TRUE16:       ; %bb.0: ; %entry
 ; GFX11-TRUE16-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
 ; GFX11-TRUE16-NEXT:    v_floor_f16_e32 v0.h, v0.l
-; GFX11-TRUE16-NEXT:    v_fract_f16_e32 v0.l, v0.l
+; GFX11-TRUE16-NEXT:    v_cmp_u_f16_e32 vcc_lo, v0.l, v0.l
+; GFX11-TRUE16-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(SKIP_2) | instid1(VALU_DEP_1)
+; GFX11-TRUE16-NEXT:    v_sub_f16_e32 v3.l, v0.l, v0.h
 ; GFX11-TRUE16-NEXT:    global_store_d16_hi_b16 v[1:2], v0, off
+; GFX11-TRUE16-NEXT:    v_min_f16_e32 v3.l, 0x3bff, v3.l
+; GFX11-TRUE16-NEXT:    v_cndmask_b16 v0.l, v3.l, v0.l, vcc_lo
 ; GFX11-TRUE16-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX11-FAKE16-LABEL: safe_math_fract_f16_noinf_check:
 ; GFX11-FAKE16:       ; %bb.0: ; %entry
 ; GFX11-FAKE16-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
 ; GFX11-FAKE16-NEXT:    v_floor_f16_e32 v3, v0
-; GFX11-FAKE16-NEXT:    v_fract_f16_e32 v0, v0
+; GFX11-FAKE16-NEXT:    v_cmp_u_f16_e32 vcc_lo, v0, v0
+; GFX11-FAKE16-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(SKIP_2) | instid1(VALU_DEP_1)
+; GFX11-FAKE16-NEXT:    v_sub_f16_e32 v4, v0, v3
 ; GFX11-FAKE16-NEXT:    global_store_b16 v[1:2], v3, off
+; GFX11-FAKE16-NEXT:    v_min_f16_e32 v4, 0x3bff, v4
+; GFX11-FAKE16-NEXT:    v_cndmask_b32_e32 v0, v4, v0, vcc_lo
 ; GFX11-FAKE16-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX12-TRUE16-LABEL: safe_math_fract_f16_noinf_check:
 ; GFX12-TRUE16:       ; %bb.0: ; %entry
 ; GFX12-TRUE16-NEXT:    s_wait_loadcnt_dscnt 0x0
@@ -2138,9 +2467,15 @@ define half @safe_math_fract_f16_noinf_check(half %x, ptr addrspace(1) writeonly
 ; GFX12-TRUE16-NEXT:    s_wait_bvhcnt 0x0
 ; GFX12-TRUE16-NEXT:    s_wait_kmcnt 0x0
 ; GFX12-TRUE16-NEXT:    v_floor_f16_e32 v0.h, v0.l
-; GFX12-TRUE16-NEXT:    v_fract_f16_e32 v0.l, v0.l
+; GFX12-TRUE16-NEXT:    v_cmp_u_f16_e32 vcc_lo, v0.l, v0.l
+; GFX12-TRUE16-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(SKIP_3) | instid1(VALU_DEP_1)
+; GFX12-TRUE16-NEXT:    v_sub_f16_e32 v3.l, v0.l, v0.h
 ; GFX12-TRUE16-NEXT:    global_store_d16_hi_b16 v[1:2], v0, off
+; GFX12-TRUE16-NEXT:    v_min_num_f16_e32 v3.l, 0x3bff, v3.l
+; GFX12-TRUE16-NEXT:    s_wait_alu depctr_va_vcc(0)
+; GFX12-TRUE16-NEXT:    v_cndmask_b16 v0.l, v3.l, v0.l, vcc_lo
 ; GFX12-TRUE16-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX12-FAKE16-LABEL: safe_math_fract_f16_noinf_check:
 ; GFX12-FAKE16:       ; %bb.0: ; %entry
 ; GFX12-FAKE16-NEXT:    s_wait_loadcnt_dscnt 0x0
@@ -2149,8 +2484,13 @@ define half @safe_math_fract_f16_noinf_check(half %x, ptr addrspace(1) writeonly
 ; GFX12-FAKE16-NEXT:    s_wait_bvhcnt 0x0
 ; GFX12-FAKE16-NEXT:    s_wait_kmcnt 0x0
 ; GFX12-FAKE16-NEXT:    v_floor_f16_e32 v3, v0
-; GFX12-FAKE16-NEXT:    v_fract_f16_e32 v0, v0
+; GFX12-FAKE16-NEXT:    v_cmp_u_f16_e32 vcc_lo, v0, v0
+; GFX12-FAKE16-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(SKIP_3) | instid1(VALU_DEP_1)
+; GFX12-FAKE16-NEXT:    v_sub_f16_e32 v4, v0, v3
 ; GFX12-FAKE16-NEXT:    global_store_b16 v[1:2], v3, off
+; GFX12-FAKE16-NEXT:    v_min_num_f16_e32 v4, 0x3bff, v4
+; GFX12-FAKE16-NEXT:    s_wait_alu depctr_va_vcc(0)
+; GFX12-FAKE16-NEXT:    v_cndmask_b32_e32 v0, v4, v0, vcc_lo
 ; GFX12-FAKE16-NEXT:    s_setpc_b64 s[30:31]
 entry:
   %floor = tail call half @llvm.floor.f16(half %x)
@@ -2163,24 +2503,16 @@ entry:
 }
 
 define double @safe_math_fract_f64_noinf_check(double %x, ptr addrspace(1) writeonly captures(none) %ip) {
-; GFX6-IR-LABEL: define double @safe_math_fract_f64_noinf_check(
-; GFX6-IR-SAME: double [[X:%.*]], ptr addrspace(1) writeonly captures(none) [[IP:%.*]]) {
-; GFX6-IR-NEXT:  [[ENTRY:.*:]]
-; GFX6-IR-NEXT:    [[FLOOR:%.*]] = tail call double @llvm.floor.f64(double [[X]])
-; GFX6-IR-NEXT:    [[SUB:%.*]] = fsub double [[X]], [[FLOOR]]
-; GFX6-IR-NEXT:    [[MIN:%.*]] = tail call double @llvm.minnum.f64(double [[SUB]], double f0x3FEFFFFFFFFFFFFF)
-; GFX6-IR-NEXT:    [[UNO:%.*]] = fcmp uno double [[X]], 0.000000e+00
-; GFX6-IR-NEXT:    [[COND:%.*]] = select i1 [[UNO]], double [[X]], double [[MIN]]
-; GFX6-IR-NEXT:    store double [[FLOOR]], ptr addrspace(1) [[IP]], align 4
-; GFX6-IR-NEXT:    ret double [[COND]]
-;
-; IR-FRACT-LABEL: define double @safe_math_fract_f64_noinf_check(
-; IR-FRACT-SAME: double [[X:%.*]], ptr addrspace(1) writeonly captures(none) [[IP:%.*]]) {
-; IR-FRACT-NEXT:  [[ENTRY:.*:]]
-; IR-FRACT-NEXT:    [[FLOOR:%.*]] = tail call double @llvm.floor.f64(double [[X]])
-; IR-FRACT-NEXT:    [[COND:%.*]] = call double @llvm.amdgcn.fract.f64(double [[X]])
-; IR-FRACT-NEXT:    store double [[FLOOR]], ptr addrspace(1) [[IP]], align 4
-; IR-FRACT-NEXT:    ret double [[COND]]
+; IR-LABEL: define double @safe_math_fract_f64_noinf_check(
+; IR-SAME: double [[X:%.*]], ptr addrspace(1) writeonly captures(none) [[IP:%.*]]) {
+; IR-NEXT:  [[ENTRY:.*:]]
+; IR-NEXT:    [[FLOOR:%.*]] = tail call double @llvm.floor.f64(double [[X]])
+; IR-NEXT:    [[SUB:%.*]] = fsub double [[X]], [[FLOOR]]
+; IR-NEXT:    [[MIN:%.*]] = tail call double @llvm.minnum.f64(double [[SUB]], double f0x3FEFFFFFFFFFFFFF)
+; IR-NEXT:    [[UNO:%.*]] = fcmp uno double [[X]], 0.000000e+00
+; IR-NEXT:    [[COND:%.*]] = select i1 [[UNO]], double [[X]], double [[MIN]]
+; IR-NEXT:    store double [[FLOOR]], ptr addrspace(1) [[IP]], align 4
+; IR-NEXT:    ret double [[COND]]
 ;
 ; GFX6-LABEL: safe_math_fract_f64_noinf_check:
 ; GFX6:       ; %bb.0: ; %entry
@@ -2207,72 +2539,336 @@ define double @safe_math_fract_f64_noinf_check(double %x, ptr addrspace(1) write
 ; GFX6-NEXT:    buffer_store_dwordx2 v[4:5], v[2:3], s[4:7], 0 addr64
 ; GFX6-NEXT:    s_waitcnt vmcnt(0) expcnt(0)
 ; GFX6-NEXT:    s_setpc_b64 s[30:31]
-; GFX7-LABEL: safe_math_fract_f64_noinf_check:
+;
+; GFX7-LABEL: safe_math_fract_f64_noinf_check:
+; GFX7:       ; %bb.0: ; %entry
+; GFX7-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX7-NEXT:    v_floor_f64_e32 v[4:5], v[0:1]
+; GFX7-NEXT:    s_mov_b32 s4, -1
+; GFX7-NEXT:    s_mov_b32 s5, 0x3fefffff
+; GFX7-NEXT:    v_cmp_u_f64_e32 vcc, v[0:1], v[0:1]
+; GFX7-NEXT:    s_mov_b32 s6, 0
+; GFX7-NEXT:    s_mov_b32 s7, 0xf000
+; GFX7-NEXT:    v_add_f64 v[6:7], v[0:1], -v[4:5]
+; GFX7-NEXT:    v_min_f64 v[6:7], v[6:7], s[4:5]
+; GFX7-NEXT:    s_mov_b32 s4, s6
+; GFX7-NEXT:    s_mov_b32 s5, s6
+; GFX7-NEXT:    buffer_store_dwordx2 v[4:5], v[2:3], s[4:7], 0 addr64
+; GFX7-NEXT:    v_cndmask_b32_e32 v0, v6, v0, vcc
+; GFX7-NEXT:    v_cndmask_b32_e32 v1, v7, v1, vcc
+; GFX7-NEXT:    s_waitcnt vmcnt(0)
+; GFX7-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX8-LABEL: safe_math_fract_f64_noinf_check:
+; GFX8:       ; %bb.0: ; %entry
+; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX8-NEXT:    v_floor_f64_e32 v[4:5], v[0:1]
+; GFX8-NEXT:    s_mov_b32 s4, -1
+; GFX8-NEXT:    s_mov_b32 s5, 0x3fefffff
+; GFX8-NEXT:    v_cmp_u_f64_e32 vcc, v[0:1], v[0:1]
+; GFX8-NEXT:    v_add_f64 v[6:7], v[0:1], -v[4:5]
+; GFX8-NEXT:    global_store_dwordx2 v[2:3], v[4:5], off
+; GFX8-NEXT:    v_min_f64 v[6:7], v[6:7], s[4:5]
+; GFX8-NEXT:    v_cndmask_b32_e32 v0, v6, v0, vcc
+; GFX8-NEXT:    v_cndmask_b32_e32 v1, v7, v1, vcc
+; GFX8-NEXT:    s_waitcnt vmcnt(0)
+; GFX8-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX11-LABEL: safe_math_fract_f64_noinf_check:
+; GFX11:       ; %bb.0: ; %entry
+; GFX11-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-NEXT:    v_floor_f64_e32 v[4:5], v[0:1]
+; GFX11-NEXT:    s_mov_b32 s0, -1
+; GFX11-NEXT:    s_mov_b32 s1, 0x3fefffff
+; GFX11-NEXT:    v_cmp_u_f64_e32 vcc_lo, v[0:1], v[0:1]
+; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(SKIP_2) | instid1(VALU_DEP_1)
+; GFX11-NEXT:    v_add_f64 v[6:7], v[0:1], -v[4:5]
+; GFX11-NEXT:    global_store_b64 v[2:3], v[4:5], off
+; GFX11-NEXT:    v_min_f64 v[6:7], v[6:7], s[0:1]
+; GFX11-NEXT:    v_dual_cndmask_b32 v0, v6, v0 :: v_dual_cndmask_b32 v1, v7, v1
+; GFX11-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX12-LABEL: safe_math_fract_f64_noinf_check:
+; GFX12:       ; %bb.0: ; %entry
+; GFX12-NEXT:    s_wait_loadcnt_dscnt 0x0
+; GFX12-NEXT:    s_wait_expcnt 0x0
+; GFX12-NEXT:    s_wait_samplecnt 0x0
+; GFX12-NEXT:    s_wait_bvhcnt 0x0
+; GFX12-NEXT:    s_wait_kmcnt 0x0
+; GFX12-NEXT:    v_floor_f64_e32 v[4:5], v[0:1]
+; GFX12-NEXT:    s_mov_b32 s0, -1
+; GFX12-NEXT:    s_mov_b32 s1, 0x3fefffff
+; GFX12-NEXT:    v_cmp_u_f64_e32 vcc_lo, v[0:1], v[0:1]
+; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(SKIP_4) | instid1(VALU_DEP_1)
+; GFX12-NEXT:    v_add_f64_e64 v[6:7], v[0:1], -v[4:5]
+; GFX12-NEXT:    global_store_b64 v[2:3], v[4:5], off
+; GFX12-NEXT:    s_wait_alu depctr_sa_sdst(0)
+; GFX12-NEXT:    v_min_num_f64_e32 v[6:7], s[0:1], v[6:7]
+; GFX12-NEXT:    s_wait_alu depctr_va_vcc(0)
+; GFX12-NEXT:    v_dual_cndmask_b32 v0, v6, v0 :: v_dual_cndmask_b32 v1, v7, v1
+; GFX12-NEXT:    s_setpc_b64 s[30:31]
+entry:
+  %floor = tail call double @llvm.floor.f64(double %x)
+  %sub = fsub double %x, %floor
+  %min = tail call double @llvm.minnum.f64(double %sub, double 0x3FEFFFFFFFFFFFFF)
+  %uno = fcmp uno double %x, 0.000000e+00
+  %cond = select i1 %uno, double %x, double %min
+  store double %floor, ptr addrspace(1) %ip, align 4
+  ret double %cond
+}
+
+define float @select_nan_fract_f32_flags_select(float %x) {
+; IR-LABEL: define float @select_nan_fract_f32_flags_select(
+; IR-SAME: float [[X:%.*]]) {
+; IR-NEXT:  [[ENTRY:.*:]]
+; IR-NEXT:    [[FLOOR:%.*]] = tail call float @llvm.floor.f32(float [[X]])
+; IR-NEXT:    [[SUB:%.*]] = fsub float [[X]], [[FLOOR]]
+; IR-NEXT:    [[MIN:%.*]] = tail call float @llvm.minnum.f32(float [[SUB]], float f0x3F7FFFFF)
+; IR-NEXT:    [[UNO:%.*]] = fcmp uno float [[X]], 0.000000e+00
+; IR-NEXT:    [[COND:%.*]] = select nsz i1 [[UNO]], float [[X]], float [[MIN]]
+; IR-NEXT:    ret float [[COND]]
+;
+; GFX6-LABEL: select_nan_fract_f32_flags_select:
+; GFX6:       ; %bb.0: ; %entry
+; GFX6-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX6-NEXT:    v_floor_f32_e32 v1, v0
+; GFX6-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX6-NEXT:    v_min_f32_e32 v1, 0x3f7fffff, v1
+; GFX6-NEXT:    v_cmp_u_f32_e32 vcc, v0, v0
+; GFX6-NEXT:    v_cndmask_b32_e32 v0, v1, v0, vcc
+; GFX6-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX7-LABEL: select_nan_fract_f32_flags_select:
+; GFX7:       ; %bb.0: ; %entry
+; GFX7-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX7-NEXT:    v_floor_f32_e32 v1, v0
+; GFX7-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX7-NEXT:    v_min_f32_e32 v1, 0x3f7fffff, v1
+; GFX7-NEXT:    v_cmp_u_f32_e32 vcc, v0, v0
+; GFX7-NEXT:    v_cndmask_b32_e32 v0, v1, v0, vcc
+; GFX7-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX8-LABEL: select_nan_fract_f32_flags_select:
+; GFX8:       ; %bb.0: ; %entry
+; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX8-NEXT:    v_floor_f32_e32 v1, v0
+; GFX8-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX8-NEXT:    v_min_f32_e32 v1, 0x3f7fffff, v1
+; GFX8-NEXT:    v_cmp_u_f32_e32 vcc, v0, v0
+; GFX8-NEXT:    v_cndmask_b32_e32 v0, v1, v0, vcc
+; GFX8-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX11-LABEL: select_nan_fract_f32_flags_select:
+; GFX11:       ; %bb.0: ; %entry
+; GFX11-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-NEXT:    v_floor_f32_e32 v1, v0
+; GFX11-NEXT:    v_cmp_u_f32_e32 vcc_lo, v0, v0
+; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX11-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX11-NEXT:    v_min_f32_e32 v1, 0x3f7fffff, v1
+; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_1)
+; GFX11-NEXT:    v_cndmask_b32_e32 v0, v1, v0, vcc_lo
+; GFX11-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX12-LABEL: select_nan_fract_f32_flags_select:
+; GFX12:       ; %bb.0: ; %entry
+; GFX12-NEXT:    s_wait_loadcnt_dscnt 0x0
+; GFX12-NEXT:    s_wait_expcnt 0x0
+; GFX12-NEXT:    s_wait_samplecnt 0x0
+; GFX12-NEXT:    s_wait_bvhcnt 0x0
+; GFX12-NEXT:    s_wait_kmcnt 0x0
+; GFX12-NEXT:    v_floor_f32_e32 v1, v0
+; GFX12-NEXT:    v_cmp_u_f32_e32 vcc_lo, v0, v0
+; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX12-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX12-NEXT:    v_min_num_f32_e32 v1, 0x3f7fffff, v1
+; GFX12-NEXT:    s_wait_alu depctr_va_vcc(0)
+; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_1)
+; GFX12-NEXT:    v_cndmask_b32_e32 v0, v1, v0, vcc_lo
+; GFX12-NEXT:    s_setpc_b64 s[30:31]
+entry:
+  %floor = tail call float @llvm.floor.f32(float %x)
+  %sub = fsub float %x, %floor
+  %min = tail call float @llvm.minnum.f32(float %sub, float 0x3FEFFFFFE0000000)
+  %uno = fcmp uno float %x, 0.000000e+00
+  %cond = select nsz i1 %uno, float %x, float %min
+  ret float %cond
+}
+
+define float @select_nan_fract_f32_flags_minnum(float %x) {
+; IR-LABEL: define float @select_nan_fract_f32_flags_minnum(
+; IR-SAME: float [[X:%.*]]) {
+; IR-NEXT:  [[ENTRY:.*:]]
+; IR-NEXT:    [[FLOOR:%.*]] = tail call float @llvm.floor.f32(float [[X]])
+; IR-NEXT:    [[SUB:%.*]] = fsub float [[X]], [[FLOOR]]
+; IR-NEXT:    [[MIN:%.*]] = tail call nsz float @llvm.minnum.f32(float [[SUB]], float f0x3F7FFFFF)
+; IR-NEXT:    [[UNO:%.*]] = fcmp uno float [[X]], 0.000000e+00
+; IR-NEXT:    [[COND:%.*]] = select i1 [[UNO]], float [[X]], float [[MIN]]
+; IR-NEXT:    ret float [[COND]]
+;
+; GFX6-LABEL: select_nan_fract_f32_flags_minnum:
+; GFX6:       ; %bb.0: ; %entry
+; GFX6-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX6-NEXT:    v_floor_f32_e32 v1, v0
+; GFX6-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX6-NEXT:    v_min_f32_e32 v1, 0x3f7fffff, v1
+; GFX6-NEXT:    v_cmp_u_f32_e32 vcc, v0, v0
+; GFX6-NEXT:    v_cndmask_b32_e32 v0, v1, v0, vcc
+; GFX6-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX7-LABEL: select_nan_fract_f32_flags_minnum:
+; GFX7:       ; %bb.0: ; %entry
+; GFX7-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX7-NEXT:    v_floor_f32_e32 v1, v0
+; GFX7-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX7-NEXT:    v_min_f32_e32 v1, 0x3f7fffff, v1
+; GFX7-NEXT:    v_cmp_u_f32_e32 vcc, v0, v0
+; GFX7-NEXT:    v_cndmask_b32_e32 v0, v1, v0, vcc
+; GFX7-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX8-LABEL: select_nan_fract_f32_flags_minnum:
+; GFX8:       ; %bb.0: ; %entry
+; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX8-NEXT:    v_floor_f32_e32 v1, v0
+; GFX8-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX8-NEXT:    v_min_f32_e32 v1, 0x3f7fffff, v1
+; GFX8-NEXT:    v_cmp_u_f32_e32 vcc, v0, v0
+; GFX8-NEXT:    v_cndmask_b32_e32 v0, v1, v0, vcc
+; GFX8-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX11-LABEL: select_nan_fract_f32_flags_minnum:
+; GFX11:       ; %bb.0: ; %entry
+; GFX11-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-NEXT:    v_floor_f32_e32 v1, v0
+; GFX11-NEXT:    v_cmp_u_f32_e32 vcc_lo, v0, v0
+; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX11-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX11-NEXT:    v_min_f32_e32 v1, 0x3f7fffff, v1
+; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_1)
+; GFX11-NEXT:    v_cndmask_b32_e32 v0, v1, v0, vcc_lo
+; GFX11-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX12-LABEL: select_nan_fract_f32_flags_minnum:
+; GFX12:       ; %bb.0: ; %entry
+; GFX12-NEXT:    s_wait_loadcnt_dscnt 0x0
+; GFX12-NEXT:    s_wait_expcnt 0x0
+; GFX12-NEXT:    s_wait_samplecnt 0x0
+; GFX12-NEXT:    s_wait_bvhcnt 0x0
+; GFX12-NEXT:    s_wait_kmcnt 0x0
+; GFX12-NEXT:    v_floor_f32_e32 v1, v0
+; GFX12-NEXT:    v_cmp_u_f32_e32 vcc_lo, v0, v0
+; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX12-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX12-NEXT:    v_min_num_f32_e32 v1, 0x3f7fffff, v1
+; GFX12-NEXT:    s_wait_alu depctr_va_vcc(0)
+; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_1)
+; GFX12-NEXT:    v_cndmask_b32_e32 v0, v1, v0, vcc_lo
+; GFX12-NEXT:    s_setpc_b64 s[30:31]
+entry:
+  %floor = tail call float @llvm.floor.f32(float %x)
+  %sub = fsub float %x, %floor
+  %min = tail call nsz float @llvm.minnum.f32(float %sub, float 0x3FEFFFFFE0000000)
+  %uno = fcmp uno float %x, 0.000000e+00
+  %cond = select i1 %uno, float %x, float %min
+  ret float %cond
+}
+
+define float @select_nan_fract_f32_ninf_minnum(float %x) {
+; IR-LABEL: define float @select_nan_fract_f32_ninf_minnum(
+; IR-SAME: float [[X:%.*]]) {
+; IR-NEXT:  [[ENTRY:.*:]]
+; IR-NEXT:    [[FLOOR:%.*]] = tail call float @llvm.floor.f32(float [[X]])
+; IR-NEXT:    [[SUB:%.*]] = fsub float [[X]], [[FLOOR]]
+; IR-NEXT:    [[MIN:%.*]] = tail call ninf float @llvm.minnum.f32(float [[SUB]], float f0x3F7FFFFF)
+; IR-NEXT:    [[UNO:%.*]] = fcmp uno float [[X]], 0.000000e+00
+; IR-NEXT:    [[COND:%.*]] = select i1 [[UNO]], float [[X]], float [[MIN]]
+; IR-NEXT:    ret float [[COND]]
+;
+; GFX6-LABEL: select_nan_fract_f32_ninf_minnum:
+; GFX6:       ; %bb.0: ; %entry
+; GFX6-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX6-NEXT:    v_floor_f32_e32 v1, v0
+; GFX6-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX6-NEXT:    v_min_f32_e32 v1, 0x3f7fffff, v1
+; GFX6-NEXT:    v_cmp_u_f32_e32 vcc, v0, v0
+; GFX6-NEXT:    v_cndmask_b32_e32 v0, v1, v0, vcc
+; GFX6-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX7-LABEL: select_nan_fract_f32_ninf_minnum:
 ; GFX7:       ; %bb.0: ; %entry
 ; GFX7-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX7-NEXT:    v_floor_f64_e32 v[4:5], v[0:1]
-; GFX7-NEXT:    v_fract_f64_e32 v[0:1], v[0:1]
-; GFX7-NEXT:    s_mov_b32 s6, 0
-; GFX7-NEXT:    s_mov_b32 s7, 0xf000
-; GFX7-NEXT:    s_mov_b32 s4, s6
-; GFX7-NEXT:    s_mov_b32 s5, s6
-; GFX7-NEXT:    buffer_store_dwordx2 v[4:5], v[2:3], s[4:7], 0 addr64
-; GFX7-NEXT:    s_waitcnt vmcnt(0)
+; GFX7-NEXT:    v_floor_f32_e32 v1, v0
+; GFX7-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX7-NEXT:    v_min_f32_e32 v1, 0x3f7fffff, v1
+; GFX7-NEXT:    v_cmp_u_f32_e32 vcc, v0, v0
+; GFX7-NEXT:    v_cndmask_b32_e32 v0, v1, v0, vcc
 ; GFX7-NEXT:    s_setpc_b64 s[30:31]
-; GFX8-LABEL: safe_math_fract_f64_noinf_check:
+;
+; GFX8-LABEL: select_nan_fract_f32_ninf_minnum:
 ; GFX8:       ; %bb.0: ; %entry
 ; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX8-NEXT:    v_floor_f64_e32 v[4:5], v[0:1]
-; GFX8-NEXT:    v_fract_f64_e32 v[0:1], v[0:1]
-; GFX8-NEXT:    global_store_dwordx2 v[2:3], v[4:5], off
-; GFX8-NEXT:    s_waitcnt vmcnt(0)
+; GFX8-NEXT:    v_floor_f32_e32 v1, v0
+; GFX8-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX8-NEXT:    v_min_f32_e32 v1, 0x3f7fffff, v1
+; GFX8-NEXT:    v_cmp_u_f32_e32 vcc, v0, v0
+; GFX8-NEXT:    v_cndmask_b32_e32 v0, v1, v0, vcc
 ; GFX8-NEXT:    s_setpc_b64 s[30:31]
-; GFX11-LABEL: safe_math_fract_f64_noinf_check:
+;
+; GFX11-LABEL: select_nan_fract_f32_ninf_minnum:
 ; GFX11:       ; %bb.0: ; %entry
 ; GFX11-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX11-NEXT:    v_floor_f64_e32 v[4:5], v[0:1]
-; GFX11-NEXT:    v_fract_f64_e32 v[0:1], v[0:1]
-; GFX11-NEXT:    global_store_b64 v[2:3], v[4:5], off
+; GFX11-NEXT:    v_floor_f32_e32 v1, v0
+; GFX11-NEXT:    v_cmp_u_f32_e32 vcc_lo, v0, v0
+; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX11-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX11-NEXT:    v_min_f32_e32 v1, 0x3f7fffff, v1
+; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_1)
+; GFX11-NEXT:    v_cndmask_b32_e32 v0, v1, v0, vcc_lo
 ; GFX11-NEXT:    s_setpc_b64 s[30:31]
-; GFX12-LABEL: safe_math_fract_f64_noinf_check:
+;
+; GFX12-LABEL: select_nan_fract_f32_ninf_minnum:
 ; GFX12:       ; %bb.0: ; %entry
 ; GFX12-NEXT:    s_wait_loadcnt_dscnt 0x0
 ; GFX12-NEXT:    s_wait_expcnt 0x0
 ; GFX12-NEXT:    s_wait_samplecnt 0x0
 ; GFX12-NEXT:    s_wait_bvhcnt 0x0
 ; GFX12-NEXT:    s_wait_kmcnt 0x0
-; GFX12-NEXT:    v_floor_f64_e32 v[4:5], v[0:1]
-; GFX12-NEXT:    v_fract_f64_e32 v[0:1], v[0:1]
-; GFX12-NEXT:    global_store_b64 v[2:3], v[4:5], off
+; GFX12-NEXT:    v_floor_f32_e32 v1, v0
+; GFX12-NEXT:    v_cmp_u_f32_e32 vcc_lo, v0, v0
+; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX12-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX12-NEXT:    v_min_num_f32_e32 v1, 0x3f7fffff, v1
+; GFX12-NEXT:    s_wait_alu depctr_va_vcc(0)
+; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_1)
+; GFX12-NEXT:    v_cndmask_b32_e32 v0, v1, v0, vcc_lo
 ; GFX12-NEXT:    s_setpc_b64 s[30:31]
 entry:
-  %floor = tail call double @llvm.floor.f64(double %x)
-  %sub = fsub double %x, %floor
-  %min = tail call double @llvm.minnum.f64(double %sub, double 0x3FEFFFFFFFFFFFFF)
-  %uno = fcmp uno double %x, 0.000000e+00
-  %cond = select i1 %uno, double %x, double %min
-  store double %floor, ptr addrspace(1) %ip, align 4
-  ret double %cond
+  %floor = tail call float @llvm.floor.f32(float %x)
+  %sub = fsub float %x, %floor
+  %min = tail call ninf float @llvm.minnum.f32(float %sub, float 0x3FEFFFFFE0000000)
+  %uno = fcmp uno float %x, 0.000000e+00
+  %cond = select i1 %uno, float %x, float %min
+  ret float %cond
 }
 
-define float @select_nan_fract_f32_flags_select(float %x) {
-; GFX6-IR-LABEL: define float @select_nan_fract_f32_flags_select(
+define float @select_nan_fract_f32_ninf_fsub(float %x) {
+; GFX6-IR-LABEL: define float @select_nan_fract_f32_ninf_fsub(
 ; GFX6-IR-SAME: float [[X:%.*]]) {
 ; GFX6-IR-NEXT:  [[ENTRY:.*:]]
 ; GFX6-IR-NEXT:    [[FLOOR:%.*]] = tail call float @llvm.floor.f32(float [[X]])
-; GFX6-IR-NEXT:    [[SUB:%.*]] = fsub float [[X]], [[FLOOR]]
+; GFX6-IR-NEXT:    [[SUB:%.*]] = fsub ninf float [[X]], [[FLOOR]]
 ; GFX6-IR-NEXT:    [[MIN:%.*]] = tail call float @llvm.minnum.f32(float [[SUB]], float f0x3F7FFFFF)
 ; GFX6-IR-NEXT:    [[UNO:%.*]] = fcmp uno float [[X]], 0.000000e+00
-; GFX6-IR-NEXT:    [[COND:%.*]] = select nsz i1 [[UNO]], float [[X]], float [[MIN]]
+; GFX6-IR-NEXT:    [[COND:%.*]] = select i1 [[UNO]], float [[X]], float [[MIN]]
 ; GFX6-IR-NEXT:    ret float [[COND]]
 ;
-; IR-FRACT-LABEL: define float @select_nan_fract_f32_flags_select(
+; IR-FRACT-LABEL: define float @select_nan_fract_f32_ninf_fsub(
 ; IR-FRACT-SAME: float [[X:%.*]]) {
 ; IR-FRACT-NEXT:  [[ENTRY:.*:]]
-; IR-FRACT-NEXT:    [[COND:%.*]] = call nsz float @llvm.amdgcn.fract.f32(float [[X]])
+; IR-FRACT-NEXT:    [[COND:%.*]] = call float @llvm.amdgcn.fract.f32(float [[X]])
 ; IR-FRACT-NEXT:    ret float [[COND]]
 ;
-; GFX6-LABEL: select_nan_fract_f32_flags_select:
+; GFX6-LABEL: select_nan_fract_f32_ninf_fsub:
 ; GFX6:       ; %bb.0: ; %entry
 ; GFX6-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
 ; GFX6-NEXT:    v_floor_f32_e32 v1, v0
@@ -2281,22 +2877,26 @@ define float @select_nan_fract_f32_flags_select(float %x) {
 ; GFX6-NEXT:    v_cmp_u_f32_e32 vcc, v0, v0
 ; GFX6-NEXT:    v_cndmask_b32_e32 v0, v1, v0, vcc
 ; GFX6-NEXT:    s_setpc_b64 s[30:31]
-; GFX7-LABEL: select_nan_fract_f32_flags_select:
+;
+; GFX7-LABEL: select_nan_fract_f32_ninf_fsub:
 ; GFX7:       ; %bb.0: ; %entry
 ; GFX7-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
 ; GFX7-NEXT:    v_fract_f32_e32 v0, v0
 ; GFX7-NEXT:    s_setpc_b64 s[30:31]
-; GFX8-LABEL: select_nan_fract_f32_flags_select:
+;
+; GFX8-LABEL: select_nan_fract_f32_ninf_fsub:
 ; GFX8:       ; %bb.0: ; %entry
 ; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
 ; GFX8-NEXT:    v_fract_f32_e32 v0, v0
 ; GFX8-NEXT:    s_setpc_b64 s[30:31]
-; GFX11-LABEL: select_nan_fract_f32_flags_select:
+;
+; GFX11-LABEL: select_nan_fract_f32_ninf_fsub:
 ; GFX11:       ; %bb.0: ; %entry
 ; GFX11-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
 ; GFX11-NEXT:    v_fract_f32_e32 v0, v0
 ; GFX11-NEXT:    s_setpc_b64 s[30:31]
-; GFX12-LABEL: select_nan_fract_f32_flags_select:
+;
+; GFX12-LABEL: select_nan_fract_f32_ninf_fsub:
 ; GFX12:       ; %bb.0: ; %entry
 ; GFX12-NEXT:    s_wait_loadcnt_dscnt 0x0
 ; GFX12-NEXT:    s_wait_expcnt 0x0
@@ -2307,70 +2907,107 @@ define float @select_nan_fract_f32_flags_select(float %x) {
 ; GFX12-NEXT:    s_setpc_b64 s[30:31]
 entry:
   %floor = tail call float @llvm.floor.f32(float %x)
-  %sub = fsub float %x, %floor
+  %sub = fsub ninf float %x, %floor
   %min = tail call float @llvm.minnum.f32(float %sub, float 0x3FEFFFFFE0000000)
   %uno = fcmp uno float %x, 0.000000e+00
-  %cond = select nsz i1 %uno, float %x, float %min
+  %cond = select i1 %uno, float %x, float %min
   ret float %cond
 }
 
-define float @select_nan_fract_f32_flags_minnum(float %x) {
-; GFX6-IR-LABEL: define float @select_nan_fract_f32_flags_minnum(
-; GFX6-IR-SAME: float [[X:%.*]]) {
-; GFX6-IR-NEXT:  [[ENTRY:.*:]]
-; GFX6-IR-NEXT:    [[FLOOR:%.*]] = tail call float @llvm.floor.f32(float [[X]])
-; GFX6-IR-NEXT:    [[SUB:%.*]] = fsub float [[X]], [[FLOOR]]
-; GFX6-IR-NEXT:    [[MIN:%.*]] = tail call nsz float @llvm.minnum.f32(float [[SUB]], float f0x3F7FFFFF)
-; GFX6-IR-NEXT:    [[UNO:%.*]] = fcmp uno float [[X]], 0.000000e+00
-; GFX6-IR-NEXT:    [[COND:%.*]] = select i1 [[UNO]], float [[X]], float [[MIN]]
-; GFX6-IR-NEXT:    ret float [[COND]]
-;
-; IR-FRACT-LABEL: define float @select_nan_fract_f32_flags_minnum(
-; IR-FRACT-SAME: float [[X:%.*]]) {
-; IR-FRACT-NEXT:  [[ENTRY:.*:]]
-; IR-FRACT-NEXT:    [[COND:%.*]] = call float @llvm.amdgcn.fract.f32(float [[X]])
-; IR-FRACT-NEXT:    ret float [[COND]]
+define float @select_nan_fract_f32_inf_check_posinf_only(float %x) {
+; IR-LABEL: define float @select_nan_fract_f32_inf_check_posinf_only(
+; IR-SAME: float [[X:%.*]]) {
+; IR-NEXT:  [[ENTRY:.*:]]
+; IR-NEXT:    [[FLOOR:%.*]] = tail call float @llvm.floor.f32(float [[X]])
+; IR-NEXT:    [[SUB:%.*]] = fsub float [[X]], [[FLOOR]]
+; IR-NEXT:    [[MIN:%.*]] = tail call float @llvm.minnum.f32(float [[SUB]], float f0x3F7FFFFF)
+; IR-NEXT:    [[UNO:%.*]] = fcmp uno float [[X]], 0.000000e+00
+; IR-NEXT:    [[SEL:%.*]] = select i1 [[UNO]], float [[X]], float [[MIN]]
+; IR-NEXT:    [[ISINF:%.*]] = fcmp oeq float [[X]], +inf
+; IR-NEXT:    [[RET:%.*]] = select i1 [[ISINF]], float 0.000000e+00, float [[SEL]]
+; IR-NEXT:    ret float [[RET]]
 ;
-; GFX6-LABEL: select_nan_fract_f32_flags_minnum:
+; GFX6-LABEL: select_nan_fract_f32_inf_check_posinf_only:
 ; GFX6:       ; %bb.0: ; %entry
 ; GFX6-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
 ; GFX6-NEXT:    v_floor_f32_e32 v1, v0
 ; GFX6-NEXT:    v_sub_f32_e32 v1, v0, v1
 ; GFX6-NEXT:    v_min_f32_e32 v1, 0x3f7fffff, v1
 ; GFX6-NEXT:    v_cmp_u_f32_e32 vcc, v0, v0
-; GFX6-NEXT:    v_cndmask_b32_e32 v0, v1, v0, vcc
+; GFX6-NEXT:    s_mov_b32 s4, 0x7f800000
+; GFX6-NEXT:    v_cndmask_b32_e32 v1, v1, v0, vcc
+; GFX6-NEXT:    v_cmp_neq_f32_e32 vcc, s4, v0
+; GFX6-NEXT:    v_cndmask_b32_e32 v0, 0, v1, vcc
 ; GFX6-NEXT:    s_setpc_b64 s[30:31]
-; GFX7-LABEL: select_nan_fract_f32_flags_minnum:
+;
+; GFX7-LABEL: select_nan_fract_f32_inf_check_posinf_only:
 ; GFX7:       ; %bb.0: ; %entry
 ; GFX7-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX7-NEXT:    v_fract_f32_e32 v0, v0
+; GFX7-NEXT:    v_floor_f32_e32 v1, v0
+; GFX7-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX7-NEXT:    v_min_f32_e32 v1, 0x3f7fffff, v1
+; GFX7-NEXT:    v_cmp_u_f32_e32 vcc, v0, v0
+; GFX7-NEXT:    s_mov_b32 s4, 0x7f800000
+; GFX7-NEXT:    v_cndmask_b32_e32 v1, v1, v0, vcc
+; GFX7-NEXT:    v_cmp_neq_f32_e32 vcc, s4, v0
+; GFX7-NEXT:    v_cndmask_b32_e32 v0, 0, v1, vcc
 ; GFX7-NEXT:    s_setpc_b64 s[30:31]
-; GFX8-LABEL: select_nan_fract_f32_flags_minnum:
+;
+; GFX8-LABEL: select_nan_fract_f32_inf_check_posinf_only:
 ; GFX8:       ; %bb.0: ; %entry
 ; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX8-NEXT:    v_fract_f32_e32 v0, v0
+; GFX8-NEXT:    v_floor_f32_e32 v1, v0
+; GFX8-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX8-NEXT:    v_min_f32_e32 v1, 0x3f7fffff, v1
+; GFX8-NEXT:    v_cmp_u_f32_e32 vcc, v0, v0
+; GFX8-NEXT:    s_mov_b32 s4, 0x7f800000
+; GFX8-NEXT:    v_cndmask_b32_e32 v1, v1, v0, vcc
+; GFX8-NEXT:    v_cmp_neq_f32_e32 vcc, s4, v0
+; GFX8-NEXT:    v_cndmask_b32_e32 v0, 0, v1, vcc
 ; GFX8-NEXT:    s_setpc_b64 s[30:31]
-; GFX11-LABEL: select_nan_fract_f32_flags_minnum:
+;
+; GFX11-LABEL: select_nan_fract_f32_inf_check_posinf_only:
 ; GFX11:       ; %bb.0: ; %entry
 ; GFX11-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX11-NEXT:    v_fract_f32_e32 v0, v0
+; GFX11-NEXT:    v_floor_f32_e32 v1, v0
+; GFX11-NEXT:    v_cmp_u_f32_e32 vcc_lo, v0, v0
+; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX11-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX11-NEXT:    v_min_f32_e32 v1, 0x3f7fffff, v1
+; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(SKIP_1) | instid1(VALU_DEP_2)
+; GFX11-NEXT:    v_cndmask_b32_e32 v1, v1, v0, vcc_lo
+; GFX11-NEXT:    v_cmp_neq_f32_e32 vcc_lo, 0x7f800000, v0
+; GFX11-NEXT:    v_cndmask_b32_e32 v0, 0, v1, vcc_lo
 ; GFX11-NEXT:    s_setpc_b64 s[30:31]
-; GFX12-LABEL: select_nan_fract_f32_flags_minnum:
+;
+; GFX12-LABEL: select_nan_fract_f32_inf_check_posinf_only:
 ; GFX12:       ; %bb.0: ; %entry
 ; GFX12-NEXT:    s_wait_loadcnt_dscnt 0x0
 ; GFX12-NEXT:    s_wait_expcnt 0x0
 ; GFX12-NEXT:    s_wait_samplecnt 0x0
 ; GFX12-NEXT:    s_wait_bvhcnt 0x0
 ; GFX12-NEXT:    s_wait_kmcnt 0x0
-; GFX12-NEXT:    v_fract_f32_e32 v0, v0
+; GFX12-NEXT:    v_floor_f32_e32 v1, v0
+; GFX12-NEXT:    v_cmp_u_f32_e32 vcc_lo, v0, v0
+; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX12-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX12-NEXT:    v_min_num_f32_e32 v1, 0x3f7fffff, v1
+; GFX12-NEXT:    s_wait_alu depctr_va_vcc(0)
+; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(SKIP_2) | instid1(VALU_DEP_2)
+; GFX12-NEXT:    v_cndmask_b32_e32 v1, v1, v0, vcc_lo
+; GFX12-NEXT:    v_cmp_neq_f32_e32 vcc_lo, 0x7f800000, v0
+; GFX12-NEXT:    s_wait_alu depctr_va_vcc(0)
+; GFX12-NEXT:    v_cndmask_b32_e32 v0, 0, v1, vcc_lo
 ; GFX12-NEXT:    s_setpc_b64 s[30:31]
 entry:
   %floor = tail call float @llvm.floor.f32(float %x)
   %sub = fsub float %x, %floor
-  %min = tail call nsz float @llvm.minnum.f32(float %sub, float 0x3FEFFFFFE0000000)
+  %min = tail call float @llvm.minnum.f32(float %sub, float 0x3FEFFFFFE0000000)
   %uno = fcmp uno float %x, 0.000000e+00
-  %cond = select i1 %uno, float %x, float %min
-  ret float %cond
+  %sel = select i1 %uno, float %x, float %min
+  %isinf = fcmp oeq float %x, 0x7FF0000000000000
+  %ret = select i1 %isinf, float 0.000000e+00, float %sel
+  ret float %ret
 }
 
 define <2 x float> @safe_math_fract_v2f32(<2 x float> %x, ptr addrspace(1) writeonly captures(none) %ip) {
@@ -3638,19 +4275,13 @@ entry:
 }
 
 define float @basic_fract_f32_nonans_minimumnum(float nofpclass(nan) %x) {
-; GFX6-IR-LABEL: define float @basic_fract_f32_nonans_minimumnum(
-; GFX6-IR-SAME: float nofpclass(nan) [[X:%.*]]) {
-; GFX6-IR-NEXT:  [[ENTRY:.*:]]
-; GFX6-IR-NEXT:    [[FLOOR:%.*]] = tail call float @llvm.floor.f32(float [[X]])
-; GFX6-IR-NEXT:    [[SUB:%.*]] = fsub float [[X]], [[FLOOR]]
-; GFX6-IR-NEXT:    [[MIN:%.*]] = tail call float @llvm.minimumnum.f32(float [[SUB]], float f0x3F7FFFFF)
-; GFX6-IR-NEXT:    ret float [[MIN]]
-;
-; IR-FRACT-LABEL: define float @basic_fract_f32_nonans_minimumnum(
-; IR-FRACT-SAME: float nofpclass(nan) [[X:%.*]]) {
-; IR-FRACT-NEXT:  [[ENTRY:.*:]]
-; IR-FRACT-NEXT:    [[MIN:%.*]] = call nnan float @llvm.amdgcn.fract.f32(float [[X]])
-; IR-FRACT-NEXT:    ret float [[MIN]]
+; IR-LABEL: define float @basic_fract_f32_nonans_minimumnum(
+; IR-SAME: float nofpclass(nan) [[X:%.*]]) {
+; IR-NEXT:  [[ENTRY:.*:]]
+; IR-NEXT:    [[FLOOR:%.*]] = tail call float @llvm.floor.f32(float [[X]])
+; IR-NEXT:    [[SUB:%.*]] = fsub float [[X]], [[FLOOR]]
+; IR-NEXT:    [[MIN:%.*]] = tail call float @llvm.minimumnum.f32(float [[SUB]], float f0x3F7FFFFF)
+; IR-NEXT:    ret float [[MIN]]
 ;
 ; GFX6-LABEL: basic_fract_f32_nonans_minimumnum:
 ; GFX6:       ; %bb.0: ; %entry
@@ -3659,21 +4290,32 @@ define float @basic_fract_f32_nonans_minimumnum(float nofpclass(nan) %x) {
 ; GFX6-NEXT:    v_sub_f32_e32 v0, v0, v1
 ; GFX6-NEXT:    v_min_f32_e32 v0, 0x3f7fffff, v0
 ; GFX6-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX7-LABEL: basic_fract_f32_nonans_minimumnum:
 ; GFX7:       ; %bb.0: ; %entry
 ; GFX7-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX7-NEXT:    v_fract_f32_e32 v0, v0
+; GFX7-NEXT:    v_floor_f32_e32 v1, v0
+; GFX7-NEXT:    v_sub_f32_e32 v0, v0, v1
+; GFX7-NEXT:    v_min_f32_e32 v0, 0x3f7fffff, v0
 ; GFX7-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX8-LABEL: basic_fract_f32_nonans_minimumnum:
 ; GFX8:       ; %bb.0: ; %entry
 ; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX8-NEXT:    v_fract_f32_e32 v0, v0
+; GFX8-NEXT:    v_floor_f32_e32 v1, v0
+; GFX8-NEXT:    v_sub_f32_e32 v0, v0, v1
+; GFX8-NEXT:    v_min_f32_e32 v0, 0x3f7fffff, v0
 ; GFX8-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX11-LABEL: basic_fract_f32_nonans_minimumnum:
 ; GFX11:       ; %bb.0: ; %entry
 ; GFX11-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX11-NEXT:    v_fract_f32_e32 v0, v0
+; GFX11-NEXT:    v_floor_f32_e32 v1, v0
+; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX11-NEXT:    v_sub_f32_e32 v0, v0, v1
+; GFX11-NEXT:    v_min_f32_e32 v0, 0x3f7fffff, v0
 ; GFX11-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX12-LABEL: basic_fract_f32_nonans_minimumnum:
 ; GFX12:       ; %bb.0: ; %entry
 ; GFX12-NEXT:    s_wait_loadcnt_dscnt 0x0
@@ -3681,7 +4323,10 @@ define float @basic_fract_f32_nonans_minimumnum(float nofpclass(nan) %x) {
 ; GFX12-NEXT:    s_wait_samplecnt 0x0
 ; GFX12-NEXT:    s_wait_bvhcnt 0x0
 ; GFX12-NEXT:    s_wait_kmcnt 0x0
-; GFX12-NEXT:    v_fract_f32_e32 v0, v0
+; GFX12-NEXT:    v_floor_f32_e32 v1, v0
+; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX12-NEXT:    v_sub_f32_e32 v0, v0, v1
+; GFX12-NEXT:    v_min_num_f32_e32 v0, 0x3f7fffff, v0
 ; GFX12-NEXT:    s_setpc_b64 s[30:31]
 entry:
   %floor = tail call float @llvm.floor.f32(float %x)
@@ -4781,21 +5426,39 @@ define float @safe_math_fract_f32_swapped_edge_case_cmp_neg_inf(float %x) #0 {
 ; GFX6-NEXT:    v_cmp_o_f32_e32 vcc, v0, v0
 ; GFX6-NEXT:    v_cndmask_b32_e32 v0, v0, v1, vcc
 ; GFX6-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX7-LABEL: safe_math_fract_f32_swapped_edge_case_cmp_neg_inf:
 ; GFX7:       ; %bb.0: ; %entry
 ; GFX7-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX7-NEXT:    v_fract_f32_e32 v0, v0
+; GFX7-NEXT:    v_floor_f32_e32 v1, v0
+; GFX7-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX7-NEXT:    v_min_f32_e32 v1, 0x3f7fffff, v1
+; GFX7-NEXT:    v_cmp_o_f32_e32 vcc, v0, v0
+; GFX7-NEXT:    v_cndmask_b32_e32 v0, v0, v1, vcc
 ; GFX7-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX8-LABEL: safe_math_fract_f32_swapped_edge_case_cmp_neg_inf:
 ; GFX8:       ; %bb.0: ; %entry
 ; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX8-NEXT:    v_fract_f32_e32 v0, v0
+; GFX8-NEXT:    v_floor_f32_e32 v1, v0
+; GFX8-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX8-NEXT:    v_min_f32_e32 v1, 0x3f7fffff, v1
+; GFX8-NEXT:    v_cmp_o_f32_e32 vcc, v0, v0
+; GFX8-NEXT:    v_cndmask_b32_e32 v0, v0, v1, vcc
 ; GFX8-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX11-LABEL: safe_math_fract_f32_swapped_edge_case_cmp_neg_inf:
 ; GFX11:       ; %bb.0: ; %entry
 ; GFX11-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX11-NEXT:    v_fract_f32_e32 v0, v0
+; GFX11-NEXT:    v_floor_f32_e32 v1, v0
+; GFX11-NEXT:    v_cmp_o_f32_e32 vcc_lo, v0, v0
+; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX11-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX11-NEXT:    v_min_f32_e32 v1, 0x3f7fffff, v1
+; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_1)
+; GFX11-NEXT:    v_cndmask_b32_e32 v0, v0, v1, vcc_lo
 ; GFX11-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX12-LABEL: safe_math_fract_f32_swapped_edge_case_cmp_neg_inf:
 ; GFX12:       ; %bb.0: ; %entry
 ; GFX12-NEXT:    s_wait_loadcnt_dscnt 0x0
@@ -4803,7 +5466,14 @@ define float @safe_math_fract_f32_swapped_edge_case_cmp_neg_inf(float %x) #0 {
 ; GFX12-NEXT:    s_wait_samplecnt 0x0
 ; GFX12-NEXT:    s_wait_bvhcnt 0x0
 ; GFX12-NEXT:    s_wait_kmcnt 0x0
-; GFX12-NEXT:    v_fract_f32_e32 v0, v0
+; GFX12-NEXT:    v_floor_f32_e32 v1, v0
+; GFX12-NEXT:    v_cmp_o_f32_e32 vcc_lo, v0, v0
+; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX12-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX12-NEXT:    v_min_num_f32_e32 v1, 0x3f7fffff, v1
+; GFX12-NEXT:    s_wait_alu depctr_va_vcc(0)
+; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_1)
+; GFX12-NEXT:    v_cndmask_b32_e32 v0, v0, v1, vcc_lo
 ; GFX12-NEXT:    s_setpc_b64 s[30:31]
 entry:
   %floor = call float @llvm.floor.f32(float %x) #3
@@ -5156,22 +5826,22 @@ define float @safe_math_fract_f32_swapped_edge_case_split_block(float %x, i1 %co
 ; GFX11-NEXT:    ; implicit-def: $vgpr0
 ; GFX11-NEXT:    s_and_saveexec_b32 s0, vcc_lo
 ; GFX11-NEXT:    s_xor_b32 s0, exec_lo, s0
-; GFX11-NEXT:    s_cbranch_execnz .LBB58_3
+; GFX11-NEXT:    s_cbranch_execnz .LBB65_3
 ; GFX11-NEXT:  ; %bb.1: ; %Flow
 ; GFX11-NEXT:    s_and_not1_saveexec_b32 s0, s0
-; GFX11-NEXT:    s_cbranch_execnz .LBB58_4
-; GFX11-NEXT:  .LBB58_2: ; %UnifiedReturnBlock
+; GFX11-NEXT:    s_cbranch_execnz .LBB65_4
+; GFX11-NEXT:  .LBB65_2: ; %UnifiedReturnBlock
 ; GFX11-NEXT:    s_or_b32 exec_lo, exec_lo, s0
 ; GFX11-NEXT:    s_setpc_b64 s[30:31]
-; GFX11-NEXT:  .LBB58_3: ; %ret
+; GFX11-NEXT:  .LBB65_3: ; %ret
 ; GFX11-NEXT:    v_floor_f32_e32 v0, v2
 ; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_1)
 ; GFX11-NEXT:    v_sub_f32_e32 v0, v2, v0
 ; GFX11-NEXT:    ; implicit-def: $vgpr2
 ; GFX11-NEXT:    v_min_f32_e32 v0, 0x3f7fffff, v0
 ; GFX11-NEXT:    s_and_not1_saveexec_b32 s0, s0
-; GFX11-NEXT:    s_cbranch_execz .LBB58_2
-; GFX11-NEXT:  .LBB58_4: ; %edge_cases
+; GFX11-NEXT:    s_cbranch_execz .LBB65_2
+; GFX11-NEXT:  .LBB65_4: ; %edge_cases
 ; GFX11-NEXT:    v_fract_f32_e32 v0, v2
 ; GFX11-NEXT:    v_cmp_neq_f32_e64 vcc_lo, 0x7f800000, |v2|
 ; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_2)
@@ -5194,16 +5864,16 @@ define float @safe_math_fract_f32_swapped_edge_case_split_block(float %x, i1 %co
 ; GFX12-NEXT:    s_and_saveexec_b32 s0, vcc_lo
 ; GFX12-NEXT:    s_wait_alu depctr_sa_sdst(0)
 ; GFX12-NEXT:    s_xor_b32 s0, exec_lo, s0
-; GFX12-NEXT:    s_cbranch_execnz .LBB58_3
+; GFX12-NEXT:    s_cbranch_execnz .LBB65_3
 ; GFX12-NEXT:  ; %bb.1: ; %Flow
 ; GFX12-NEXT:    s_wait_alu depctr_sa_sdst(0)
 ; GFX12-NEXT:    s_and_not1_saveexec_b32 s0, s0
-; GFX12-NEXT:    s_cbranch_execnz .LBB58_4
-; GFX12-NEXT:  .LBB58_2: ; %UnifiedReturnBlock
+; GFX12-NEXT:    s_cbranch_execnz .LBB65_4
+; GFX12-NEXT:  .LBB65_2: ; %UnifiedReturnBlock
 ; GFX12-NEXT:    s_wait_alu depctr_sa_sdst(0)
 ; GFX12-NEXT:    s_or_b32 exec_lo, exec_lo, s0
 ; GFX12-NEXT:    s_setpc_b64 s[30:31]
-; GFX12-NEXT:  .LBB58_3: ; %ret
+; GFX12-NEXT:  .LBB65_3: ; %ret
 ; GFX12-NEXT:    v_floor_f32_e32 v0, v2
 ; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_1)
 ; GFX12-NEXT:    v_sub_f32_e32 v0, v2, v0
@@ -5211,8 +5881,8 @@ define float @safe_math_fract_f32_swapped_edge_case_split_block(float %x, i1 %co
 ; GFX12-NEXT:    v_min_num_f32_e32 v0, 0x3f7fffff, v0
 ; GFX12-NEXT:    s_wait_alu depctr_sa_sdst(0)
 ; GFX12-NEXT:    s_and_not1_saveexec_b32 s0, s0
-; GFX12-NEXT:    s_cbranch_execz .LBB58_2
-; GFX12-NEXT:  .LBB58_4: ; %edge_cases
+; GFX12-NEXT:    s_cbranch_execz .LBB65_2
+; GFX12-NEXT:  .LBB65_4: ; %edge_cases
 ; GFX12-NEXT:    v_fract_f32_e32 v0, v2
 ; GFX12-NEXT:    v_cmp_neq_f32_e64 vcc_lo, 0x7f800000, |v2|
 ; GFX12-NEXT:    s_wait_alu depctr_va_vcc(0)
@@ -5236,7 +5906,6 @@ define float @safe_math_fract_f32_swapped_edge_case_split_block(float %x, i1 %co
 ; GFX6-IR-NEXT:    ret float [[COND8]]
 ; GFX6-IR:       [[RET]]:
 ; GFX6-IR-NEXT:    ret float [[MIN]]
-;
 ; IR-FRACT-LABEL: define float @safe_math_fract_f32_swapped_edge_case_split_block(
 ; IR-FRACT-SAME: float [[X:%.*]], i1 [[COND:%.*]]) #[[ATTR0]] {
 ; IR-FRACT-NEXT:    [[FLOOR:%.*]] = call float @llvm.floor.f32(float [[X]])
@@ -5251,7 +5920,6 @@ define float @safe_math_fract_f32_swapped_edge_case_split_block(float %x, i1 %co
 ; IR-FRACT-NEXT:    ret float [[COND8]]
 ; IR-FRACT:       [[RET]]:
 ; IR-FRACT-NEXT:    ret float [[MIN]]
-;
   %floor = call float @llvm.floor.f32(float %x)
   %sub = fsub float %x, %floor
   %min = call float @llvm.minnum.f32(float %sub, float 0x3FEFFFFFE0000000)
@@ -5402,19 +6070,33 @@ define float @safe_math_fract_f32_swapped_edge_case_inf_check_wrong_compare(floa
 ; GFX7-LABEL: safe_math_fract_f32_swapped_edge_case_inf_check_wrong_compare:
 ; GFX7:       ; %bb.0: ; %entry
 ; GFX7-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX7-NEXT:    v_fract_f32_e32 v0, v0
+; GFX7-NEXT:    v_floor_f32_e32 v1, v0
+; GFX7-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX7-NEXT:    v_min_f32_e32 v1, 0x3f7fffff, v1
+; GFX7-NEXT:    v_cmp_o_f32_e32 vcc, v0, v0
+; GFX7-NEXT:    v_cndmask_b32_e32 v0, v0, v1, vcc
 ; GFX7-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX8-LABEL: safe_math_fract_f32_swapped_edge_case_inf_check_wrong_compare:
 ; GFX8:       ; %bb.0: ; %entry
 ; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX8-NEXT:    v_fract_f32_e32 v0, v0
+; GFX8-NEXT:    v_floor_f32_e32 v1, v0
+; GFX8-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX8-NEXT:    v_min_f32_e32 v1, 0x3f7fffff, v1
+; GFX8-NEXT:    v_cmp_o_f32_e32 vcc, v0, v0
+; GFX8-NEXT:    v_cndmask_b32_e32 v0, v0, v1, vcc
 ; GFX8-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX11-LABEL: safe_math_fract_f32_swapped_edge_case_inf_check_wrong_compare:
 ; GFX11:       ; %bb.0: ; %entry
 ; GFX11-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX11-NEXT:    v_fract_f32_e32 v0, v0
+; GFX11-NEXT:    v_floor_f32_e32 v1, v0
+; GFX11-NEXT:    v_cmp_o_f32_e32 vcc_lo, v0, v0
+; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX11-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX11-NEXT:    v_min_f32_e32 v1, 0x3f7fffff, v1
+; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_1)
+; GFX11-NEXT:    v_cndmask_b32_e32 v0, v0, v1, vcc_lo
 ; GFX11-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX12-LABEL: safe_math_fract_f32_swapped_edge_case_inf_check_wrong_compare:
@@ -5424,7 +6106,14 @@ define float @safe_math_fract_f32_swapped_edge_case_inf_check_wrong_compare(floa
 ; GFX12-NEXT:    s_wait_samplecnt 0x0
 ; GFX12-NEXT:    s_wait_bvhcnt 0x0
 ; GFX12-NEXT:    s_wait_kmcnt 0x0
-; GFX12-NEXT:    v_fract_f32_e32 v0, v0
+; GFX12-NEXT:    v_floor_f32_e32 v1, v0
+; GFX12-NEXT:    v_cmp_o_f32_e32 vcc_lo, v0, v0
+; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX12-NEXT:    v_sub_f32_e32 v1, v0, v1
+; GFX12-NEXT:    v_min_num_f32_e32 v1, 0x3f7fffff, v1
+; GFX12-NEXT:    s_wait_alu depctr_va_vcc(0)
+; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_1)
+; GFX12-NEXT:    v_cndmask_b32_e32 v0, v0, v1, vcc_lo
 ; GFX12-NEXT:    s_setpc_b64 s[30:31]
 ; IR-LABEL: define float @safe_math_fract_f32_swapped_edge_case_inf_check_wrong_compare(
 ; IR-SAME: float [[X:%.*]]) #[[ATTR0]] {
@@ -5438,7 +6127,6 @@ define float @safe_math_fract_f32_swapped_edge_case_inf_check_wrong_compare(floa
 ; IR-NEXT:    [[NOT_NAN:%.*]] = fcmp ord float [[X]], 0.000000e+00
 ; IR-NEXT:    [[COND8:%.*]] = select i1 [[NOT_NAN]], float [[COND]], float [[X]]
 ; IR-NEXT:    ret float [[COND8]]
-;
 entry:
   %floor = call float @llvm.floor.f32(float %x)
   %sub = fsub float %x, %floor



More information about the llvm-commits mailing list