[llvm] [AMDGPU] Implement IR variant of isFMAFasterThanFMulAndFAdd (PR #121465)

Chinmay Deshpande via llvm-commits llvm-commits at lists.llvm.org
Mon Jan 6 05:26:06 PST 2025


https://github.com/chinmaydd updated https://github.com/llvm/llvm-project/pull/121465

>From 3d97f5c76e08594e1d2112101f7bdab147af6ddf Mon Sep 17 00:00:00 2001
From: Chinmay Deshpande <ChinmayDiwakar.Deshpande at amd.com>
Date: Thu, 12 Dec 2024 23:51:39 -0500
Subject: [PATCH 1/2] [AMDGPU] Implement IR variant of
 isFMAFasterThanFMulAndFAdd

Change-Id: I2484db303227da9aa53cc8842283c4ba6a332b3a
---
 llvm/lib/Target/AMDGPU/SIISelLowering.cpp     |  58 ++++++
 llvm/lib/Target/AMDGPU/SIISelLowering.h       |   3 +
 .../AMDGPU/is-profitable-to-hoist-ir.ll       | 185 ++++++++++++++++++
 3 files changed, 246 insertions(+)
 create mode 100644 llvm/test/CodeGen/AMDGPU/is-profitable-to-hoist-ir.ll

diff --git a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
index b3cfa398d9b5f6..5d82b7021b6cef 100644
--- a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
@@ -5731,6 +5731,33 @@ bool SITargetLowering::isFMAFasterThanFMulAndFAdd(const MachineFunction &MF,
   return false;
 }
 
+// Refer to comments added to the MIR variant of isFMAFasterThanFMulAndFAdd for
+// specific details.
+bool SITargetLowering::isFMAFasterThanFMulAndFAdd(const Function &F,
+                                                  Type *Ty) const {
+  SIModeRegisterDefaults Mode = SIModeRegisterDefaults(F, *Subtarget);
+  switch (Ty->getScalarSizeInBits()) {
+  case 32: {
+    if (!Subtarget->hasMadMacF32Insts())
+      return Subtarget->hasFastFMAF32();
+
+    if (Mode.FP32Denormals != DenormalMode::getPreserveSign())
+      return Subtarget->hasFastFMAF32() || Subtarget->hasDLInsts();
+
+    return Subtarget->hasFastFMAF32() && Subtarget->hasDLInsts();
+  }
+  case 64:
+    return true;
+  case 16:
+    return Subtarget->has16BitInsts() &&
+           Mode.FP64FP16Denormals != DenormalMode::getPreserveSign();
+  default:
+    break;
+  }
+
+  return false;
+}
+
 bool SITargetLowering::isFMADLegal(const MachineInstr &MI, LLT Ty) const {
   if (!Ty.isScalar())
     return false;
@@ -16945,6 +16972,37 @@ bool SITargetLowering::checkForPhysRegDependency(
   return false;
 }
 
+/// Check if it is profitable to hoist instruction in then/else to if.
+/// Not profitable if I and it's user can form a FMA instruction
+/// because we prefer FMSUB/FMADD.
+bool SITargetLowering::isProfitableToHoist(Instruction *I) const {
+  if (!I->hasOneUse())
+    return true;
+
+  Instruction *User = I->user_back();
+  // TODO: Add more patterns that are not profitable to hoist
+  switch (I->getOpcode()) {
+  case Instruction::FMul: {
+    if (User->getOpcode() != Instruction::FSub &&
+        User->getOpcode() != Instruction::FAdd)
+      return true;
+
+    const TargetOptions &Options = getTargetMachine().Options;
+    const Function *F = I->getFunction();
+    const DataLayout &DL = F->getDataLayout();
+    Type *Ty = User->getOperand(0)->getType();
+
+    return !isOperationLegalOrCustom(ISD::FMA, getValueType(DL, Ty)) ||
+           (Options.AllowFPOpFusion != FPOpFusion::Fast &&
+            !Options.UnsafeFPMath) ||
+           !isFMAFasterThanFMulAndFAdd(*F, Ty);
+  }
+  default:
+    return true;
+  }
+  return true;
+}
+
 void SITargetLowering::emitExpandAtomicAddrSpacePredicate(
     Instruction *AI) const {
   // Given: atomicrmw fadd ptr %addr, float %val ordering
diff --git a/llvm/lib/Target/AMDGPU/SIISelLowering.h b/llvm/lib/Target/AMDGPU/SIISelLowering.h
index f4641e7a659907..d7bb03f25fc02c 100644
--- a/llvm/lib/Target/AMDGPU/SIISelLowering.h
+++ b/llvm/lib/Target/AMDGPU/SIISelLowering.h
@@ -456,6 +456,7 @@ class SITargetLowering final : public AMDGPUTargetLowering {
                                   EVT VT) const override;
   bool isFMAFasterThanFMulAndFAdd(const MachineFunction &MF,
                                   const LLT Ty) const override;
+  bool isFMAFasterThanFMulAndFAdd(const Function &F, Type *Ty) const override;
   bool isFMADLegal(const SelectionDAG &DAG, const SDNode *N) const override;
   bool isFMADLegal(const MachineInstr &MI, const LLT Ty) const override;
 
@@ -535,6 +536,8 @@ class SITargetLowering final : public AMDGPUTargetLowering {
                                  const TargetInstrInfo *TII, unsigned &PhysReg,
                                  int &Cost) const override;
 
+  bool isProfitableToHoist(Instruction *I) const override;
+
   bool isKnownNeverNaNForTargetNode(SDValue Op,
                                     const SelectionDAG &DAG,
                                     bool SNaN = false,
diff --git a/llvm/test/CodeGen/AMDGPU/is-profitable-to-hoist-ir.ll b/llvm/test/CodeGen/AMDGPU/is-profitable-to-hoist-ir.ll
new file mode 100644
index 00000000000000..3c204fda38d458
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/is-profitable-to-hoist-ir.ll
@@ -0,0 +1,185 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -S -passes=simplifycfg -verify-machineinstrs -hoist-common-insts=true -mtriple=amdgcn-- -mcpu=gfx1030 -fp-contract=fast < %s | FileCheck -check-prefix=GFX -check-prefix=GFX-FP-CONTRACT %s
+; RUN: opt -S -passes=simplifycfg -verify-machineinstrs -hoist-common-insts=true -mtriple=amdgcn-- -mcpu=gfx1030 -enable-unsafe-fp-math --denormal-fp-math=ieee < %s | FileCheck -check-prefix=GFX -check-prefix=GFX-UNSAFE-FP-IEEE %s
+; RUN: opt -S -passes=simplifycfg -verify-machineinstrs -hoist-common-insts=true -mtriple=amdgcn-- -mcpu=gfx1030 -enable-unsafe-fp-math --denormal-fp-math=preserve-sign < %s | FileCheck -check-prefix=GFX -check-prefix=GFX-UNSAFE-FP-PRESERVE %s
+
+define double @_branch(ptr dereferenceable(8) %x, ptr dereferenceable(8) %y, ptr dereferenceable(8) %a) #0 {
+; GFX-LABEL: define double @_branch(
+; GFX-SAME: ptr dereferenceable(8) [[X:%.*]], ptr dereferenceable(8) [[Y:%.*]], ptr dereferenceable(8) [[A:%.*]]) #[[ATTR0:[0-9]+]] {
+; GFX-NEXT:  [[ENTRY:.*:]]
+; GFX-NEXT:    [[TMP0:%.*]] = load double, ptr [[Y]], align 8
+; GFX-NEXT:    [[CMP:%.*]] = fcmp oeq double [[TMP0]], 0.000000e+00
+; GFX-NEXT:    [[TMP1:%.*]] = load double, ptr [[X]], align 8
+; GFX-NEXT:    [[TMP2:%.*]] = load double, ptr [[A]], align 8
+; GFX-NEXT:    br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
+; GFX:       [[COMMON_RET:.*]]:
+; GFX-NEXT:    [[COMMON_RET_OP:%.*]] = phi double [ [[MUL:%.*]], %[[IF_THEN]] ], [ [[SUB:%.*]], %[[IF_ELSE]] ]
+; GFX-NEXT:    ret double [[COMMON_RET_OP]]
+; GFX:       [[IF_THEN]]:
+; GFX-NEXT:    [[MUL]] = fmul fast double [[TMP1]], [[TMP2]]
+; GFX-NEXT:    [[ADD:%.*]] = fadd fast double 1.000000e+00, [[MUL]]
+; GFX-NEXT:    br label %[[COMMON_RET]]
+; GFX:       [[IF_ELSE]]:
+; GFX-NEXT:    [[MUL1:%.*]] = fmul fast double [[TMP1]], [[TMP2]]
+; GFX-NEXT:    [[SUB]] = fsub fast double [[MUL1]], [[TMP0]]
+; GFX-NEXT:    br label %[[COMMON_RET]]
+;
+entry:
+  %0 = load double, ptr %y, align 8
+  %cmp = fcmp oeq double %0, 0.000000e+00
+  %1 = load double, ptr %x, align 8
+  br i1 %cmp, label %if.then, label %if.else
+
+if.then:                                          ; preds = %entry
+  %2 = load double, ptr %a, align 8
+  %mul = fmul fast double %1, %2
+  %add = fadd fast double 1.000000e+00, %mul
+  ret double %mul
+
+if.else:                                          ; preds = %entry
+  %3 = load double, ptr %a, align 8
+  %mul1 = fmul fast double %1, %3
+  %sub = fsub fast double %mul1, %0
+  ret double %sub
+}
+
+define float @_branch2(ptr dereferenceable(8) %x, ptr dereferenceable(8) %y, ptr dereferenceable(8) %a) #0 {
+; GFX-LABEL: define float @_branch2(
+; GFX-SAME: ptr dereferenceable(8) [[X:%.*]], ptr dereferenceable(8) [[Y:%.*]], ptr dereferenceable(8) [[A:%.*]]) #[[ATTR0]] {
+; GFX-NEXT:  [[ENTRY:.*:]]
+; GFX-NEXT:    [[TMP0:%.*]] = load float, ptr [[Y]], align 8
+; GFX-NEXT:    [[CMP:%.*]] = fcmp oeq float [[TMP0]], 0.000000e+00
+; GFX-NEXT:    [[TMP1:%.*]] = load float, ptr [[X]], align 8
+; GFX-NEXT:    [[TMP2:%.*]] = load float, ptr [[A]], align 8
+; GFX-NEXT:    br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
+; GFX:       [[COMMON_RET:.*]]:
+; GFX-NEXT:    [[COMMON_RET_OP:%.*]] = phi float [ [[MUL:%.*]], %[[IF_THEN]] ], [ [[SUB:%.*]], %[[IF_ELSE]] ]
+; GFX-NEXT:    ret float [[COMMON_RET_OP]]
+; GFX:       [[IF_THEN]]:
+; GFX-NEXT:    [[MUL]] = fmul fast float [[TMP1]], [[TMP2]]
+; GFX-NEXT:    [[ADD:%.*]] = fadd fast float 1.000000e+00, [[MUL]]
+; GFX-NEXT:    br label %[[COMMON_RET]]
+; GFX:       [[IF_ELSE]]:
+; GFX-NEXT:    [[MUL1:%.*]] = fmul fast float [[TMP1]], [[TMP2]]
+; GFX-NEXT:    [[SUB]] = fsub fast float [[MUL1]], [[TMP0]]
+; GFX-NEXT:    br label %[[COMMON_RET]]
+;
+entry:
+  %0 = load float, ptr %y, align 8
+  %cmp = fcmp oeq float %0, 0.000000e+00
+  %1 = load float, ptr %x, align 8
+  br i1 %cmp, label %if.then, label %if.else
+
+
+if.then:                                          ; preds = %entry
+  %2 = load float, ptr %a, align 8
+  %mul = fmul fast float %1, %2
+  %add = fadd fast float 1.000000e+00, %mul
+  ret float %mul
+
+if.else:                                          ; preds = %entry
+  %3 = load float, ptr %a, align 8
+  %mul1 = fmul fast float %1, %3
+  %sub = fsub fast float %mul1, %0
+  ret float %sub
+}
+
+define half @_branch3(ptr dereferenceable(8) %x, ptr dereferenceable(8) %y, ptr dereferenceable(8) %a) #0 {
+; GFX-CONTRACT-LABEL: define half @_branchr32(
+; GFX-CONTRACT-SAME: ptr dereferenceable(8) [[X:%.*]], ptr dereferenceable(8) [[Y:%.*]], ptr dereferenceable(8) [[A:%.*]]) #[[ATTR0]] {
+; GFX-CONTRACT-NEXT:  [[ENTRY:.*:]]
+; GFX-CONTRACT-NEXT:    [[TMP0:%.*]] = load half, ptr [[Y]], align 8
+; GFX-CONTRACT-NEXT:    [[CMP:%.*]] = fcmp oeq half [[TMP0]], 0xH0000
+; GFX-CONTRACT-NEXT:    [[TMP1:%.*]] = load half, ptr [[X]], align 8
+; GFX-CONTRACT-NEXT:    [[TMP2:%.*]] = load half, ptr [[A]], align 8
+; GFX-CONTRACT-NEXT:    br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
+; GFX-CONTRACT:       [[COMMON_RET:.*]]:
+; GFX-CONTRACT-NEXT:    [[COMMON_RET_OP:%.*]] = phi half [ [[MUL:%.*]], %[[IF_THEN]] ], [ [[SUB:%.*]], %[[IF_ELSE]] ]
+; GFX-CONTRACT-NEXT:    ret half [[COMMON_RET_OP]]
+; GFX-CONTRACT:       [[IF_THEN]]:
+; GFX-CONTRACT-NEXT:    [[MUL]] = fmul fast half [[TMP1]], [[TMP2]]
+; GFX-CONTRACT-NEXT:    [[ADD:%.*]] = fadd fast half 0xH3C00, [[MUL]]
+; GFX-CONTRACT-NEXT:    br label %[[COMMON_RET]]
+; GFX-CONTRACT:       [[IF_ELSE]]:
+; GFX-CONTRACT-NEXT:    [[MUL1:%.*]] = fmul fast half [[TMP1]], [[TMP2]]
+; GFX-CONTRACT-NEXT:    [[SUB]] = fsub fast half [[MUL1]], [[TMP0]]
+; GFX-CONTRACT-NEXT:    br label %[[COMMON_RET]]
+;
+; GFX-FP-CONTRACT-LABEL: define half @_branch3(
+; GFX-FP-CONTRACT-SAME: ptr dereferenceable(8) [[X:%.*]], ptr dereferenceable(8) [[Y:%.*]], ptr dereferenceable(8) [[A:%.*]]) #[[ATTR0]] {
+; GFX-FP-CONTRACT-NEXT:  [[ENTRY:.*:]]
+; GFX-FP-CONTRACT-NEXT:    [[TMP0:%.*]] = load half, ptr [[Y]], align 8
+; GFX-FP-CONTRACT-NEXT:    [[CMP:%.*]] = fcmp oeq half [[TMP0]], 0xH0000
+; GFX-FP-CONTRACT-NEXT:    [[TMP1:%.*]] = load half, ptr [[X]], align 8
+; GFX-FP-CONTRACT-NEXT:    [[TMP2:%.*]] = load half, ptr [[A]], align 8
+; GFX-FP-CONTRACT-NEXT:    br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
+; GFX-FP-CONTRACT:       [[COMMON_RET:.*]]:
+; GFX-FP-CONTRACT-NEXT:    [[COMMON_RET_OP:%.*]] = phi half [ [[MUL:%.*]], %[[IF_THEN]] ], [ [[SUB:%.*]], %[[IF_ELSE]] ]
+; GFX-FP-CONTRACT-NEXT:    ret half [[COMMON_RET_OP]]
+; GFX-FP-CONTRACT:       [[IF_THEN]]:
+; GFX-FP-CONTRACT-NEXT:    [[MUL]] = fmul fast half [[TMP1]], [[TMP2]]
+; GFX-FP-CONTRACT-NEXT:    [[ADD:%.*]] = fadd fast half 0xH3C00, [[MUL]]
+; GFX-FP-CONTRACT-NEXT:    br label %[[COMMON_RET]]
+; GFX-FP-CONTRACT:       [[IF_ELSE]]:
+; GFX-FP-CONTRACT-NEXT:    [[MUL1:%.*]] = fmul fast half [[TMP1]], [[TMP2]]
+; GFX-FP-CONTRACT-NEXT:    [[SUB]] = fsub fast half [[MUL1]], [[TMP0]]
+; GFX-FP-CONTRACT-NEXT:    br label %[[COMMON_RET]]
+;
+; GFX-UNSAFE-FP-IEEE-LABEL: define half @_branch3(
+; GFX-UNSAFE-FP-IEEE-SAME: ptr dereferenceable(8) [[X:%.*]], ptr dereferenceable(8) [[Y:%.*]], ptr dereferenceable(8) [[A:%.*]]) #[[ATTR0]] {
+; GFX-UNSAFE-FP-IEEE-NEXT:  [[ENTRY:.*:]]
+; GFX-UNSAFE-FP-IEEE-NEXT:    [[TMP0:%.*]] = load half, ptr [[Y]], align 8
+; GFX-UNSAFE-FP-IEEE-NEXT:    [[CMP:%.*]] = fcmp oeq half [[TMP0]], 0xH0000
+; GFX-UNSAFE-FP-IEEE-NEXT:    [[TMP1:%.*]] = load half, ptr [[X]], align 8
+; GFX-UNSAFE-FP-IEEE-NEXT:    [[TMP2:%.*]] = load half, ptr [[A]], align 8
+; GFX-UNSAFE-FP-IEEE-NEXT:    br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
+; GFX-UNSAFE-FP-IEEE:       [[COMMON_RET:.*]]:
+; GFX-UNSAFE-FP-IEEE-NEXT:    [[COMMON_RET_OP:%.*]] = phi half [ [[MUL:%.*]], %[[IF_THEN]] ], [ [[SUB:%.*]], %[[IF_ELSE]] ]
+; GFX-UNSAFE-FP-IEEE-NEXT:    ret half [[COMMON_RET_OP]]
+; GFX-UNSAFE-FP-IEEE:       [[IF_THEN]]:
+; GFX-UNSAFE-FP-IEEE-NEXT:    [[MUL]] = fmul fast half [[TMP1]], [[TMP2]]
+; GFX-UNSAFE-FP-IEEE-NEXT:    [[ADD:%.*]] = fadd fast half 0xH3C00, [[MUL]]
+; GFX-UNSAFE-FP-IEEE-NEXT:    br label %[[COMMON_RET]]
+; GFX-UNSAFE-FP-IEEE:       [[IF_ELSE]]:
+; GFX-UNSAFE-FP-IEEE-NEXT:    [[MUL1:%.*]] = fmul fast half [[TMP1]], [[TMP2]]
+; GFX-UNSAFE-FP-IEEE-NEXT:    [[SUB]] = fsub fast half [[MUL1]], [[TMP0]]
+; GFX-UNSAFE-FP-IEEE-NEXT:    br label %[[COMMON_RET]]
+;
+; GFX-UNSAFE-FP-PRESERVE-LABEL: define half @_branch3(
+; GFX-UNSAFE-FP-PRESERVE-SAME: ptr dereferenceable(8) [[X:%.*]], ptr dereferenceable(8) [[Y:%.*]], ptr dereferenceable(8) [[A:%.*]]) #[[ATTR0]] {
+; GFX-UNSAFE-FP-PRESERVE-NEXT:  [[ENTRY:.*:]]
+; GFX-UNSAFE-FP-PRESERVE-NEXT:    [[TMP0:%.*]] = load half, ptr [[Y]], align 8
+; GFX-UNSAFE-FP-PRESERVE-NEXT:    [[CMP:%.*]] = fcmp oeq half [[TMP0]], 0xH0000
+; GFX-UNSAFE-FP-PRESERVE-NEXT:    [[TMP1:%.*]] = load half, ptr [[X]], align 8
+; GFX-UNSAFE-FP-PRESERVE-NEXT:    [[TMP2:%.*]] = load half, ptr [[A]], align 8
+; GFX-UNSAFE-FP-PRESERVE-NEXT:    [[MUL:%.*]] = fmul fast half [[TMP1]], [[TMP2]]
+; GFX-UNSAFE-FP-PRESERVE-NEXT:    br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
+; GFX-UNSAFE-FP-PRESERVE:       [[COMMON_RET:.*]]:
+; GFX-UNSAFE-FP-PRESERVE-NEXT:    [[COMMON_RET_OP:%.*]] = phi half [ [[MUL]], %[[IF_THEN]] ], [ [[SUB:%.*]], %[[IF_ELSE]] ]
+; GFX-UNSAFE-FP-PRESERVE-NEXT:    ret half [[COMMON_RET_OP]]
+; GFX-UNSAFE-FP-PRESERVE:       [[IF_THEN]]:
+; GFX-UNSAFE-FP-PRESERVE-NEXT:    [[ADD:%.*]] = fadd fast half 0xH3C00, [[MUL]]
+; GFX-UNSAFE-FP-PRESERVE-NEXT:    br label %[[COMMON_RET]]
+; GFX-UNSAFE-FP-PRESERVE:       [[IF_ELSE]]:
+; GFX-UNSAFE-FP-PRESERVE-NEXT:    [[SUB]] = fsub fast half [[MUL]], [[TMP0]]
+; GFX-UNSAFE-FP-PRESERVE-NEXT:    br label %[[COMMON_RET]]
+;
+entry:
+  %0 = load half, ptr %y, align 8
+  %cmp = fcmp oeq half %0, 0.000000e+00
+  %1 = load half, ptr %x, align 8
+  br i1 %cmp, label %if.then, label %if.else
+
+if.then:                                          ; preds = %entry
+  %2 = load half, ptr %a, align 8
+  %mul = fmul fast half %1, %2
+  %add = fadd fast half 1.000000e+00, %mul
+  ret half %mul
+
+if.else:                                          ; preds = %entry
+  %3 = load half, ptr %a, align 8
+  %mul1 = fmul fast half %1, %3
+  %sub = fsub fast half %mul1, %0
+  ret half %sub
+}
+

>From f0ede1074c379d3e4f0a61bb41677cd52fc27319 Mon Sep 17 00:00:00 2001
From: Chinmay Deshpande <ChinmayDiwakar.Deshpande at amd.com>
Date: Fri, 3 Jan 2025 06:56:12 -0500
Subject: [PATCH 2/2] [AMDGPU] Incorporate feedback

Change-Id: I12e23d6a9506a6ce6e87fdf2292b4d4f2d88c258
---
 llvm/lib/Target/AMDGPU/SIISelLowering.cpp     |  16 +-
 .../AMDGPU/is-profitable-to-hoist-ir.ll       | 185 --------
 .../CodeGen/AMDGPU/prevent-fmul-hoist-ir.ll   | 447 ++++++++++++++++++
 3 files changed, 454 insertions(+), 194 deletions(-)
 delete mode 100644 llvm/test/CodeGen/AMDGPU/is-profitable-to-hoist-ir.ll
 create mode 100644 llvm/test/CodeGen/AMDGPU/prevent-fmul-hoist-ir.ll

diff --git a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
index 5d82b7021b6cef..c6d83065fd00f7 100644
--- a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
@@ -5735,12 +5735,12 @@ bool SITargetLowering::isFMAFasterThanFMulAndFAdd(const MachineFunction &MF,
 // specific details.
 bool SITargetLowering::isFMAFasterThanFMulAndFAdd(const Function &F,
                                                   Type *Ty) const {
-  SIModeRegisterDefaults Mode = SIModeRegisterDefaults(F, *Subtarget);
   switch (Ty->getScalarSizeInBits()) {
   case 32: {
     if (!Subtarget->hasMadMacF32Insts())
       return Subtarget->hasFastFMAF32();
 
+    SIModeRegisterDefaults Mode = SIModeRegisterDefaults(F, *Subtarget);
     if (Mode.FP32Denormals != DenormalMode::getPreserveSign())
       return Subtarget->hasFastFMAF32() || Subtarget->hasDLInsts();
 
@@ -5748,9 +5748,11 @@ bool SITargetLowering::isFMAFasterThanFMulAndFAdd(const Function &F,
   }
   case 64:
     return true;
-  case 16:
+  case 16: {
+    SIModeRegisterDefaults Mode = SIModeRegisterDefaults(F, *Subtarget);
     return Subtarget->has16BitInsts() &&
            Mode.FP64FP16Denormals != DenormalMode::getPreserveSign();
+  }
   default:
     break;
   }
@@ -16973,8 +16975,6 @@ bool SITargetLowering::checkForPhysRegDependency(
 }
 
 /// Check if it is profitable to hoist instruction in then/else to if.
-/// Not profitable if I and it's user can form a FMA instruction
-/// because we prefer FMSUB/FMADD.
 bool SITargetLowering::isProfitableToHoist(Instruction *I) const {
   if (!I->hasOneUse())
     return true;
@@ -16989,13 +16989,11 @@ bool SITargetLowering::isProfitableToHoist(Instruction *I) const {
 
     const TargetOptions &Options = getTargetMachine().Options;
     const Function *F = I->getFunction();
-    const DataLayout &DL = F->getDataLayout();
-    Type *Ty = User->getOperand(0)->getType();
 
-    return !isOperationLegalOrCustom(ISD::FMA, getValueType(DL, Ty)) ||
-           (Options.AllowFPOpFusion != FPOpFusion::Fast &&
+    return ((!I->hasAllowContract() || !User->hasAllowContract()) &&
+            Options.AllowFPOpFusion != FPOpFusion::Fast &&
             !Options.UnsafeFPMath) ||
-           !isFMAFasterThanFMulAndFAdd(*F, Ty);
+           !isFMAFasterThanFMulAndFAdd(*F, User->getType());
   }
   default:
     return true;
diff --git a/llvm/test/CodeGen/AMDGPU/is-profitable-to-hoist-ir.ll b/llvm/test/CodeGen/AMDGPU/is-profitable-to-hoist-ir.ll
deleted file mode 100644
index 3c204fda38d458..00000000000000
--- a/llvm/test/CodeGen/AMDGPU/is-profitable-to-hoist-ir.ll
+++ /dev/null
@@ -1,185 +0,0 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
-; RUN: opt -S -passes=simplifycfg -verify-machineinstrs -hoist-common-insts=true -mtriple=amdgcn-- -mcpu=gfx1030 -fp-contract=fast < %s | FileCheck -check-prefix=GFX -check-prefix=GFX-FP-CONTRACT %s
-; RUN: opt -S -passes=simplifycfg -verify-machineinstrs -hoist-common-insts=true -mtriple=amdgcn-- -mcpu=gfx1030 -enable-unsafe-fp-math --denormal-fp-math=ieee < %s | FileCheck -check-prefix=GFX -check-prefix=GFX-UNSAFE-FP-IEEE %s
-; RUN: opt -S -passes=simplifycfg -verify-machineinstrs -hoist-common-insts=true -mtriple=amdgcn-- -mcpu=gfx1030 -enable-unsafe-fp-math --denormal-fp-math=preserve-sign < %s | FileCheck -check-prefix=GFX -check-prefix=GFX-UNSAFE-FP-PRESERVE %s
-
-define double @_branch(ptr dereferenceable(8) %x, ptr dereferenceable(8) %y, ptr dereferenceable(8) %a) #0 {
-; GFX-LABEL: define double @_branch(
-; GFX-SAME: ptr dereferenceable(8) [[X:%.*]], ptr dereferenceable(8) [[Y:%.*]], ptr dereferenceable(8) [[A:%.*]]) #[[ATTR0:[0-9]+]] {
-; GFX-NEXT:  [[ENTRY:.*:]]
-; GFX-NEXT:    [[TMP0:%.*]] = load double, ptr [[Y]], align 8
-; GFX-NEXT:    [[CMP:%.*]] = fcmp oeq double [[TMP0]], 0.000000e+00
-; GFX-NEXT:    [[TMP1:%.*]] = load double, ptr [[X]], align 8
-; GFX-NEXT:    [[TMP2:%.*]] = load double, ptr [[A]], align 8
-; GFX-NEXT:    br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
-; GFX:       [[COMMON_RET:.*]]:
-; GFX-NEXT:    [[COMMON_RET_OP:%.*]] = phi double [ [[MUL:%.*]], %[[IF_THEN]] ], [ [[SUB:%.*]], %[[IF_ELSE]] ]
-; GFX-NEXT:    ret double [[COMMON_RET_OP]]
-; GFX:       [[IF_THEN]]:
-; GFX-NEXT:    [[MUL]] = fmul fast double [[TMP1]], [[TMP2]]
-; GFX-NEXT:    [[ADD:%.*]] = fadd fast double 1.000000e+00, [[MUL]]
-; GFX-NEXT:    br label %[[COMMON_RET]]
-; GFX:       [[IF_ELSE]]:
-; GFX-NEXT:    [[MUL1:%.*]] = fmul fast double [[TMP1]], [[TMP2]]
-; GFX-NEXT:    [[SUB]] = fsub fast double [[MUL1]], [[TMP0]]
-; GFX-NEXT:    br label %[[COMMON_RET]]
-;
-entry:
-  %0 = load double, ptr %y, align 8
-  %cmp = fcmp oeq double %0, 0.000000e+00
-  %1 = load double, ptr %x, align 8
-  br i1 %cmp, label %if.then, label %if.else
-
-if.then:                                          ; preds = %entry
-  %2 = load double, ptr %a, align 8
-  %mul = fmul fast double %1, %2
-  %add = fadd fast double 1.000000e+00, %mul
-  ret double %mul
-
-if.else:                                          ; preds = %entry
-  %3 = load double, ptr %a, align 8
-  %mul1 = fmul fast double %1, %3
-  %sub = fsub fast double %mul1, %0
-  ret double %sub
-}
-
-define float @_branch2(ptr dereferenceable(8) %x, ptr dereferenceable(8) %y, ptr dereferenceable(8) %a) #0 {
-; GFX-LABEL: define float @_branch2(
-; GFX-SAME: ptr dereferenceable(8) [[X:%.*]], ptr dereferenceable(8) [[Y:%.*]], ptr dereferenceable(8) [[A:%.*]]) #[[ATTR0]] {
-; GFX-NEXT:  [[ENTRY:.*:]]
-; GFX-NEXT:    [[TMP0:%.*]] = load float, ptr [[Y]], align 8
-; GFX-NEXT:    [[CMP:%.*]] = fcmp oeq float [[TMP0]], 0.000000e+00
-; GFX-NEXT:    [[TMP1:%.*]] = load float, ptr [[X]], align 8
-; GFX-NEXT:    [[TMP2:%.*]] = load float, ptr [[A]], align 8
-; GFX-NEXT:    br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
-; GFX:       [[COMMON_RET:.*]]:
-; GFX-NEXT:    [[COMMON_RET_OP:%.*]] = phi float [ [[MUL:%.*]], %[[IF_THEN]] ], [ [[SUB:%.*]], %[[IF_ELSE]] ]
-; GFX-NEXT:    ret float [[COMMON_RET_OP]]
-; GFX:       [[IF_THEN]]:
-; GFX-NEXT:    [[MUL]] = fmul fast float [[TMP1]], [[TMP2]]
-; GFX-NEXT:    [[ADD:%.*]] = fadd fast float 1.000000e+00, [[MUL]]
-; GFX-NEXT:    br label %[[COMMON_RET]]
-; GFX:       [[IF_ELSE]]:
-; GFX-NEXT:    [[MUL1:%.*]] = fmul fast float [[TMP1]], [[TMP2]]
-; GFX-NEXT:    [[SUB]] = fsub fast float [[MUL1]], [[TMP0]]
-; GFX-NEXT:    br label %[[COMMON_RET]]
-;
-entry:
-  %0 = load float, ptr %y, align 8
-  %cmp = fcmp oeq float %0, 0.000000e+00
-  %1 = load float, ptr %x, align 8
-  br i1 %cmp, label %if.then, label %if.else
-
-
-if.then:                                          ; preds = %entry
-  %2 = load float, ptr %a, align 8
-  %mul = fmul fast float %1, %2
-  %add = fadd fast float 1.000000e+00, %mul
-  ret float %mul
-
-if.else:                                          ; preds = %entry
-  %3 = load float, ptr %a, align 8
-  %mul1 = fmul fast float %1, %3
-  %sub = fsub fast float %mul1, %0
-  ret float %sub
-}
-
-define half @_branch3(ptr dereferenceable(8) %x, ptr dereferenceable(8) %y, ptr dereferenceable(8) %a) #0 {
-; GFX-CONTRACT-LABEL: define half @_branchr32(
-; GFX-CONTRACT-SAME: ptr dereferenceable(8) [[X:%.*]], ptr dereferenceable(8) [[Y:%.*]], ptr dereferenceable(8) [[A:%.*]]) #[[ATTR0]] {
-; GFX-CONTRACT-NEXT:  [[ENTRY:.*:]]
-; GFX-CONTRACT-NEXT:    [[TMP0:%.*]] = load half, ptr [[Y]], align 8
-; GFX-CONTRACT-NEXT:    [[CMP:%.*]] = fcmp oeq half [[TMP0]], 0xH0000
-; GFX-CONTRACT-NEXT:    [[TMP1:%.*]] = load half, ptr [[X]], align 8
-; GFX-CONTRACT-NEXT:    [[TMP2:%.*]] = load half, ptr [[A]], align 8
-; GFX-CONTRACT-NEXT:    br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
-; GFX-CONTRACT:       [[COMMON_RET:.*]]:
-; GFX-CONTRACT-NEXT:    [[COMMON_RET_OP:%.*]] = phi half [ [[MUL:%.*]], %[[IF_THEN]] ], [ [[SUB:%.*]], %[[IF_ELSE]] ]
-; GFX-CONTRACT-NEXT:    ret half [[COMMON_RET_OP]]
-; GFX-CONTRACT:       [[IF_THEN]]:
-; GFX-CONTRACT-NEXT:    [[MUL]] = fmul fast half [[TMP1]], [[TMP2]]
-; GFX-CONTRACT-NEXT:    [[ADD:%.*]] = fadd fast half 0xH3C00, [[MUL]]
-; GFX-CONTRACT-NEXT:    br label %[[COMMON_RET]]
-; GFX-CONTRACT:       [[IF_ELSE]]:
-; GFX-CONTRACT-NEXT:    [[MUL1:%.*]] = fmul fast half [[TMP1]], [[TMP2]]
-; GFX-CONTRACT-NEXT:    [[SUB]] = fsub fast half [[MUL1]], [[TMP0]]
-; GFX-CONTRACT-NEXT:    br label %[[COMMON_RET]]
-;
-; GFX-FP-CONTRACT-LABEL: define half @_branch3(
-; GFX-FP-CONTRACT-SAME: ptr dereferenceable(8) [[X:%.*]], ptr dereferenceable(8) [[Y:%.*]], ptr dereferenceable(8) [[A:%.*]]) #[[ATTR0]] {
-; GFX-FP-CONTRACT-NEXT:  [[ENTRY:.*:]]
-; GFX-FP-CONTRACT-NEXT:    [[TMP0:%.*]] = load half, ptr [[Y]], align 8
-; GFX-FP-CONTRACT-NEXT:    [[CMP:%.*]] = fcmp oeq half [[TMP0]], 0xH0000
-; GFX-FP-CONTRACT-NEXT:    [[TMP1:%.*]] = load half, ptr [[X]], align 8
-; GFX-FP-CONTRACT-NEXT:    [[TMP2:%.*]] = load half, ptr [[A]], align 8
-; GFX-FP-CONTRACT-NEXT:    br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
-; GFX-FP-CONTRACT:       [[COMMON_RET:.*]]:
-; GFX-FP-CONTRACT-NEXT:    [[COMMON_RET_OP:%.*]] = phi half [ [[MUL:%.*]], %[[IF_THEN]] ], [ [[SUB:%.*]], %[[IF_ELSE]] ]
-; GFX-FP-CONTRACT-NEXT:    ret half [[COMMON_RET_OP]]
-; GFX-FP-CONTRACT:       [[IF_THEN]]:
-; GFX-FP-CONTRACT-NEXT:    [[MUL]] = fmul fast half [[TMP1]], [[TMP2]]
-; GFX-FP-CONTRACT-NEXT:    [[ADD:%.*]] = fadd fast half 0xH3C00, [[MUL]]
-; GFX-FP-CONTRACT-NEXT:    br label %[[COMMON_RET]]
-; GFX-FP-CONTRACT:       [[IF_ELSE]]:
-; GFX-FP-CONTRACT-NEXT:    [[MUL1:%.*]] = fmul fast half [[TMP1]], [[TMP2]]
-; GFX-FP-CONTRACT-NEXT:    [[SUB]] = fsub fast half [[MUL1]], [[TMP0]]
-; GFX-FP-CONTRACT-NEXT:    br label %[[COMMON_RET]]
-;
-; GFX-UNSAFE-FP-IEEE-LABEL: define half @_branch3(
-; GFX-UNSAFE-FP-IEEE-SAME: ptr dereferenceable(8) [[X:%.*]], ptr dereferenceable(8) [[Y:%.*]], ptr dereferenceable(8) [[A:%.*]]) #[[ATTR0]] {
-; GFX-UNSAFE-FP-IEEE-NEXT:  [[ENTRY:.*:]]
-; GFX-UNSAFE-FP-IEEE-NEXT:    [[TMP0:%.*]] = load half, ptr [[Y]], align 8
-; GFX-UNSAFE-FP-IEEE-NEXT:    [[CMP:%.*]] = fcmp oeq half [[TMP0]], 0xH0000
-; GFX-UNSAFE-FP-IEEE-NEXT:    [[TMP1:%.*]] = load half, ptr [[X]], align 8
-; GFX-UNSAFE-FP-IEEE-NEXT:    [[TMP2:%.*]] = load half, ptr [[A]], align 8
-; GFX-UNSAFE-FP-IEEE-NEXT:    br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
-; GFX-UNSAFE-FP-IEEE:       [[COMMON_RET:.*]]:
-; GFX-UNSAFE-FP-IEEE-NEXT:    [[COMMON_RET_OP:%.*]] = phi half [ [[MUL:%.*]], %[[IF_THEN]] ], [ [[SUB:%.*]], %[[IF_ELSE]] ]
-; GFX-UNSAFE-FP-IEEE-NEXT:    ret half [[COMMON_RET_OP]]
-; GFX-UNSAFE-FP-IEEE:       [[IF_THEN]]:
-; GFX-UNSAFE-FP-IEEE-NEXT:    [[MUL]] = fmul fast half [[TMP1]], [[TMP2]]
-; GFX-UNSAFE-FP-IEEE-NEXT:    [[ADD:%.*]] = fadd fast half 0xH3C00, [[MUL]]
-; GFX-UNSAFE-FP-IEEE-NEXT:    br label %[[COMMON_RET]]
-; GFX-UNSAFE-FP-IEEE:       [[IF_ELSE]]:
-; GFX-UNSAFE-FP-IEEE-NEXT:    [[MUL1:%.*]] = fmul fast half [[TMP1]], [[TMP2]]
-; GFX-UNSAFE-FP-IEEE-NEXT:    [[SUB]] = fsub fast half [[MUL1]], [[TMP0]]
-; GFX-UNSAFE-FP-IEEE-NEXT:    br label %[[COMMON_RET]]
-;
-; GFX-UNSAFE-FP-PRESERVE-LABEL: define half @_branch3(
-; GFX-UNSAFE-FP-PRESERVE-SAME: ptr dereferenceable(8) [[X:%.*]], ptr dereferenceable(8) [[Y:%.*]], ptr dereferenceable(8) [[A:%.*]]) #[[ATTR0]] {
-; GFX-UNSAFE-FP-PRESERVE-NEXT:  [[ENTRY:.*:]]
-; GFX-UNSAFE-FP-PRESERVE-NEXT:    [[TMP0:%.*]] = load half, ptr [[Y]], align 8
-; GFX-UNSAFE-FP-PRESERVE-NEXT:    [[CMP:%.*]] = fcmp oeq half [[TMP0]], 0xH0000
-; GFX-UNSAFE-FP-PRESERVE-NEXT:    [[TMP1:%.*]] = load half, ptr [[X]], align 8
-; GFX-UNSAFE-FP-PRESERVE-NEXT:    [[TMP2:%.*]] = load half, ptr [[A]], align 8
-; GFX-UNSAFE-FP-PRESERVE-NEXT:    [[MUL:%.*]] = fmul fast half [[TMP1]], [[TMP2]]
-; GFX-UNSAFE-FP-PRESERVE-NEXT:    br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
-; GFX-UNSAFE-FP-PRESERVE:       [[COMMON_RET:.*]]:
-; GFX-UNSAFE-FP-PRESERVE-NEXT:    [[COMMON_RET_OP:%.*]] = phi half [ [[MUL]], %[[IF_THEN]] ], [ [[SUB:%.*]], %[[IF_ELSE]] ]
-; GFX-UNSAFE-FP-PRESERVE-NEXT:    ret half [[COMMON_RET_OP]]
-; GFX-UNSAFE-FP-PRESERVE:       [[IF_THEN]]:
-; GFX-UNSAFE-FP-PRESERVE-NEXT:    [[ADD:%.*]] = fadd fast half 0xH3C00, [[MUL]]
-; GFX-UNSAFE-FP-PRESERVE-NEXT:    br label %[[COMMON_RET]]
-; GFX-UNSAFE-FP-PRESERVE:       [[IF_ELSE]]:
-; GFX-UNSAFE-FP-PRESERVE-NEXT:    [[SUB]] = fsub fast half [[MUL]], [[TMP0]]
-; GFX-UNSAFE-FP-PRESERVE-NEXT:    br label %[[COMMON_RET]]
-;
-entry:
-  %0 = load half, ptr %y, align 8
-  %cmp = fcmp oeq half %0, 0.000000e+00
-  %1 = load half, ptr %x, align 8
-  br i1 %cmp, label %if.then, label %if.else
-
-if.then:                                          ; preds = %entry
-  %2 = load half, ptr %a, align 8
-  %mul = fmul fast half %1, %2
-  %add = fadd fast half 1.000000e+00, %mul
-  ret half %mul
-
-if.else:                                          ; preds = %entry
-  %3 = load half, ptr %a, align 8
-  %mul1 = fmul fast half %1, %3
-  %sub = fsub fast half %mul1, %0
-  ret half %sub
-}
-
diff --git a/llvm/test/CodeGen/AMDGPU/prevent-fmul-hoist-ir.ll b/llvm/test/CodeGen/AMDGPU/prevent-fmul-hoist-ir.ll
new file mode 100644
index 00000000000000..1c5f5882aa1345
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/prevent-fmul-hoist-ir.ll
@@ -0,0 +1,447 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -S -passes=simplifycfg -verify-machineinstrs -hoist-common-insts=true -mtriple=amdgcn-- --fp-contract=fast -mcpu=gfx1030 < %s | FileCheck -check-prefix=GFX -check-prefix=FP-CONTRACT-FAST %s
+; RUN: opt -S -passes=simplifycfg -verify-machineinstrs -hoist-common-insts=true -mtriple=amdgcn-- --fp-contract=off --enable-unsafe-fp-math -mcpu=gfx1030 < %s | FileCheck -check-prefix=GFX -check-prefix=UNSAFE-FP-MATH %s
+; RUN: opt -S -passes=simplifycfg -verify-machineinstrs -hoist-common-insts=true -mtriple=amdgcn-- --fp-contract=off -mcpu=gfx1030 < %s | FileCheck -check-prefix=GFX -check-prefix=NO-UNSAFE-FP-MATH %s
+
+define double @is_profitable_f64_contract(ptr dereferenceable(8) %ptr_x, ptr dereferenceable(8) %ptr_y, ptr dereferenceable(8) %ptr_a) #0 {
+; GFX-LABEL: define double @is_profitable_f64_contract(
+; GFX-SAME: ptr dereferenceable(8) [[PTR_X:%.*]], ptr dereferenceable(8) [[PTR_Y:%.*]], ptr dereferenceable(8) [[PTR_A:%.*]]) #[[ATTR0:[0-9]+]] {
+; GFX-NEXT:  [[ENTRY:.*:]]
+; GFX-NEXT:    [[Y:%.*]] = load double, ptr [[PTR_Y]], align 8
+; GFX-NEXT:    [[CMP:%.*]] = fcmp oeq double [[Y]], 0.000000e+00
+; GFX-NEXT:    [[X:%.*]] = load double, ptr [[PTR_X]], align 8
+; GFX-NEXT:    [[A_1:%.*]] = load double, ptr [[PTR_A]], align 8
+; GFX-NEXT:    br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
+; GFX:       [[COMMON_RET:.*]]:
+; GFX-NEXT:    [[COMMON_RET_OP:%.*]] = phi double [ [[ADD:%.*]], %[[IF_THEN]] ], [ [[SUB:%.*]], %[[IF_ELSE]] ]
+; GFX-NEXT:    ret double [[COMMON_RET_OP]]
+; GFX:       [[IF_THEN]]:
+; GFX-NEXT:    [[MUL:%.*]] = fmul contract double [[X]], [[A_1]]
+; GFX-NEXT:    [[ADD]] = fadd contract double 1.000000e+00, [[MUL]]
+; GFX-NEXT:    br label %[[COMMON_RET]]
+; GFX:       [[IF_ELSE]]:
+; GFX-NEXT:    [[MUL1:%.*]] = fmul contract double [[X]], [[A_1]]
+; GFX-NEXT:    [[SUB]] = fsub contract double [[MUL1]], [[Y]]
+; GFX-NEXT:    br label %[[COMMON_RET]]
+;
+entry:
+  %y = load double, ptr %ptr_y, align 8
+  %cmp = fcmp oeq double %y, 0.000000e+00
+  %x = load double, ptr %ptr_x, align 8
+  br i1 %cmp, label %if.then, label %if.else
+
+if.then:                                          ; preds = %entry
+  %a_1 = load double, ptr %ptr_a, align 8
+  %mul = fmul contract double %x, %a_1
+  %add = fadd contract double 1.000000e+00, %mul
+  ret double %add
+
+if.else:                                          ; preds = %entry
+  %a_2 = load double, ptr %ptr_a, align 8
+  %mul1 = fmul contract double %x, %a_2
+  %sub = fsub contract double %mul1, %y
+  ret double %sub
+}
+
+define double @is_profitable_f64_modifiers(ptr dereferenceable(8) %ptr_x, ptr dereferenceable(8) %ptr_y, ptr dereferenceable(8) %ptr_a) #0 {
+; GFX-LABEL: define double @is_profitable_f64_modifiers(
+; GFX-SAME: ptr dereferenceable(8) [[PTR_X:%.*]], ptr dereferenceable(8) [[PTR_Y:%.*]], ptr dereferenceable(8) [[PTR_A:%.*]]) #[[ATTR0]] {
+; GFX-NEXT:  [[ENTRY:.*:]]
+; GFX-NEXT:    [[Y:%.*]] = load double, ptr [[PTR_Y]], align 8
+; GFX-NEXT:    [[CMP:%.*]] = fcmp oeq double [[Y]], 0.000000e+00
+; GFX-NEXT:    [[X:%.*]] = load double, ptr [[PTR_X]], align 8
+; GFX-NEXT:    [[A_1:%.*]] = load double, ptr [[PTR_A]], align 8
+; GFX-NEXT:    br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
+; GFX:       [[COMMON_RET:.*]]:
+; GFX-NEXT:    [[COMMON_RET_OP:%.*]] = phi double [ [[FNEG:%.*]], %[[IF_THEN]] ], [ [[FABS:%.*]], %[[IF_ELSE]] ]
+; GFX-NEXT:    ret double [[COMMON_RET_OP]]
+; GFX:       [[IF_THEN]]:
+; GFX-NEXT:    [[MUL:%.*]] = fmul contract double [[X]], [[A_1]]
+; GFX-NEXT:    [[ADD:%.*]] = fadd contract double 1.000000e+00, [[MUL]]
+; GFX-NEXT:    [[FNEG]] = fneg double [[ADD]]
+; GFX-NEXT:    br label %[[COMMON_RET]]
+; GFX:       [[IF_ELSE]]:
+; GFX-NEXT:    [[MUL1:%.*]] = fmul contract double [[X]], [[A_1]]
+; GFX-NEXT:    [[SUB:%.*]] = fsub contract double [[MUL1]], [[Y]]
+; GFX-NEXT:    [[FABS]] = call double @llvm.fabs.f64(double [[SUB]])
+; GFX-NEXT:    br label %[[COMMON_RET]]
+;
+entry:
+  %y = load double, ptr %ptr_y, align 8
+  %cmp = fcmp oeq double %y, 0.000000e+00
+  %x = load double, ptr %ptr_x, align 8
+  br i1 %cmp, label %if.then, label %if.else
+
+if.then:                                          ; preds = %entry
+  %a_1 = load double, ptr %ptr_a, align 8
+  %mul = fmul contract double %x, %a_1
+  %add = fadd contract double 1.000000e+00, %mul
+  %fneg = fneg double %add
+  ret double %fneg
+
+if.else:                                          ; preds = %entry
+  %a_2 = load double, ptr %ptr_a, align 8
+  %mul1 = fmul contract double %x, %a_2
+  %sub = fsub contract double %mul1, %y
+  %fabs = call double @llvm.fabs.f64(double %sub)
+  ret double %fabs
+}
+
+define float @is_profitable_f32(ptr dereferenceable(8) %ptr_x, ptr dereferenceable(8) %ptr_y, ptr dereferenceable(8) %ptr_a) #0 {
+; GFX-LABEL: define float @is_profitable_f32(
+; GFX-SAME: ptr dereferenceable(8) [[PTR_X:%.*]], ptr dereferenceable(8) [[PTR_Y:%.*]], ptr dereferenceable(8) [[PTR_A:%.*]]) #[[ATTR0]] {
+; GFX-NEXT:  [[ENTRY:.*:]]
+; GFX-NEXT:    [[Y:%.*]] = load float, ptr [[PTR_Y]], align 8
+; GFX-NEXT:    [[CMP:%.*]] = fcmp oeq float [[Y]], 0.000000e+00
+; GFX-NEXT:    [[X:%.*]] = load float, ptr [[PTR_X]], align 8
+; GFX-NEXT:    [[A_1:%.*]] = load float, ptr [[PTR_A]], align 8
+; GFX-NEXT:    br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
+; GFX:       [[COMMON_RET:.*]]:
+; GFX-NEXT:    [[COMMON_RET_OP:%.*]] = phi float [ [[MUL:%.*]], %[[IF_THEN]] ], [ [[SUB:%.*]], %[[IF_ELSE]] ]
+; GFX-NEXT:    ret float [[COMMON_RET_OP]]
+; GFX:       [[IF_THEN]]:
+; GFX-NEXT:    [[MUL]] = fmul contract float [[X]], [[A_1]]
+; GFX-NEXT:    [[ADD:%.*]] = fadd contract float 1.000000e+00, [[MUL]]
+; GFX-NEXT:    br label %[[COMMON_RET]]
+; GFX:       [[IF_ELSE]]:
+; GFX-NEXT:    [[MUL1:%.*]] = fmul contract float [[X]], [[A_1]]
+; GFX-NEXT:    [[SUB]] = fsub contract float [[MUL1]], [[Y]]
+; GFX-NEXT:    br label %[[COMMON_RET]]
+;
+entry:
+  %y = load float, ptr %ptr_y, align 8
+  %cmp = fcmp oeq float %y, 0.000000e+00
+  %x = load float, ptr %ptr_x, align 8
+  br i1 %cmp, label %if.then, label %if.else
+
+
+if.then:                                          ; preds = %entry
+  %a_1 = load float, ptr %ptr_a, align 8
+  %mul = fmul contract float %x, %a_1
+  %add = fadd contract float 1.000000e+00, %mul
+  ret float %mul
+
+if.else:                                          ; preds = %entry
+  %a_2 = load float, ptr %ptr_a, align 8
+  %mul1 = fmul contract float %x, %a_2
+  %sub = fsub contract float %mul1, %y
+  ret float %sub
+}
+
+define half @is_profitable_f16_preserve(ptr dereferenceable(8) %ptr_x, ptr dereferenceable(8) %ptr_y, ptr dereferenceable(8) %ptr_a) #0 {
+; GFX-LABEL: define half @is_profitable_f16_preserve(
+; GFX-SAME: ptr dereferenceable(8) [[PTR_X:%.*]], ptr dereferenceable(8) [[PTR_Y:%.*]], ptr dereferenceable(8) [[PTR_A:%.*]]) #[[ATTR0]] {
+; GFX-NEXT:  [[ENTRY:.*:]]
+; GFX-NEXT:    [[Y:%.*]] = load half, ptr [[PTR_Y]], align 8
+; GFX-NEXT:    [[CMP:%.*]] = fcmp oeq half [[Y]], 0xH0000
+; GFX-NEXT:    [[X:%.*]] = load half, ptr [[PTR_X]], align 8
+; GFX-NEXT:    [[A_1:%.*]] = load half, ptr [[PTR_A]], align 8
+; GFX-NEXT:    [[MUL:%.*]] = fmul contract half [[X]], [[A_1]]
+; GFX-NEXT:    br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
+; GFX:       [[COMMON_RET:.*]]:
+; GFX-NEXT:    [[COMMON_RET_OP:%.*]] = phi half [ [[MUL]], %[[IF_THEN]] ], [ [[SUB:%.*]], %[[IF_ELSE]] ]
+; GFX-NEXT:    ret half [[COMMON_RET_OP]]
+; GFX:       [[IF_THEN]]:
+; GFX-NEXT:    [[ADD:%.*]] = fadd contract half [[Y]], [[MUL]]
+; GFX-NEXT:    br label %[[COMMON_RET]]
+; GFX:       [[IF_ELSE]]:
+; GFX-NEXT:    [[SUB]] = fsub contract half [[MUL]], [[Y]]
+; GFX-NEXT:    br label %[[COMMON_RET]]
+;
+entry:
+  %y = load half, ptr %ptr_y, align 8
+  %cmp = fcmp oeq half %y, 0.000000e+00
+  %x = load half, ptr %ptr_x, align 8
+  br i1 %cmp, label %if.then, label %if.else
+
+if.then:                                          ; preds = %entry
+  %a_1 = load half, ptr %ptr_a, align 8
+  %mul = fmul contract half %x, %a_1
+  %add = fadd contract half %y, %mul
+  ret half %mul
+
+if.else:                                          ; preds = %entry
+  %a_2 = load half, ptr %ptr_a, align 8
+  %mul1 = fmul contract half %x, %a_2
+  %sub = fsub contract half %mul1, %y
+  ret half %sub
+}
+
+define half @is_profitable_f16_ieee(ptr dereferenceable(8) %ptr_x, ptr dereferenceable(8) %ptr_y, ptr dereferenceable(8) %ptr_a) #1 {
+; GFX-LABEL: define half @is_profitable_f16_ieee(
+; GFX-SAME: ptr dereferenceable(8) [[PTR_X:%.*]], ptr dereferenceable(8) [[PTR_Y:%.*]], ptr dereferenceable(8) [[PTR_A:%.*]]) #[[ATTR1:[0-9]+]] {
+; GFX-NEXT:  [[ENTRY:.*:]]
+; GFX-NEXT:    [[Y:%.*]] = load half, ptr [[PTR_Y]], align 8
+; GFX-NEXT:    [[CMP:%.*]] = fcmp oeq half [[Y]], 0xH0000
+; GFX-NEXT:    [[X:%.*]] = load half, ptr [[PTR_X]], align 8
+; GFX-NEXT:    [[A_1:%.*]] = load half, ptr [[PTR_A]], align 8
+; GFX-NEXT:    br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
+; GFX:       [[COMMON_RET:.*]]:
+; GFX-NEXT:    [[COMMON_RET_OP:%.*]] = phi half [ [[MUL:%.*]], %[[IF_THEN]] ], [ [[SUB:%.*]], %[[IF_ELSE]] ]
+; GFX-NEXT:    ret half [[COMMON_RET_OP]]
+; GFX:       [[IF_THEN]]:
+; GFX-NEXT:    [[MUL]] = fmul contract half [[X]], [[A_1]]
+; GFX-NEXT:    [[ADD:%.*]] = fadd contract half [[Y]], [[MUL]]
+; GFX-NEXT:    br label %[[COMMON_RET]]
+; GFX:       [[IF_ELSE]]:
+; GFX-NEXT:    [[MUL1:%.*]] = fmul contract half [[X]], [[A_1]]
+; GFX-NEXT:    [[SUB]] = fsub contract half [[MUL1]], [[Y]]
+; GFX-NEXT:    br label %[[COMMON_RET]]
+;
+entry:
+  %y = load half, ptr %ptr_y, align 8
+  %cmp = fcmp oeq half %y, 0.000000e+00
+  %x = load half, ptr %ptr_x, align 8
+  br i1 %cmp, label %if.then, label %if.else
+
+if.then:                                          ; preds = %entry
+  %a_1 = load half, ptr %ptr_a, align 8
+  %mul = fmul contract half %x, %a_1
+  %add = fadd contract half %y, %mul
+  ret half %mul
+
+if.else:                                          ; preds = %entry
+  %a_2 = load half, ptr %ptr_a, align 8
+  %mul1 = fmul contract half %x, %a_2
+  %sub = fsub contract half %mul1, %y
+  ret half %sub
+}
+
+define bfloat @is_profitable_bfloat_preserve(ptr dereferenceable(8) %ptr_x, ptr dereferenceable(8) %ptr_y, ptr dereferenceable(8) %ptr_a) #0 {
+; GFX-LABEL: define bfloat @is_profitable_bfloat_preserve(
+; GFX-SAME: ptr dereferenceable(8) [[PTR_X:%.*]], ptr dereferenceable(8) [[PTR_Y:%.*]], ptr dereferenceable(8) [[PTR_A:%.*]]) #[[ATTR0]] {
+; GFX-NEXT:  [[ENTRY:.*:]]
+; GFX-NEXT:    [[Y:%.*]] = load bfloat, ptr [[PTR_Y]], align 8
+; GFX-NEXT:    [[CMP:%.*]] = fcmp oeq bfloat [[Y]], 0xR0000
+; GFX-NEXT:    [[X:%.*]] = load bfloat, ptr [[PTR_X]], align 8
+; GFX-NEXT:    [[A_1:%.*]] = load bfloat, ptr [[PTR_A]], align 8
+; GFX-NEXT:    [[MUL:%.*]] = fmul contract bfloat [[X]], [[A_1]]
+; GFX-NEXT:    br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
+; GFX:       [[COMMON_RET:.*]]:
+; GFX-NEXT:    [[COMMON_RET_OP:%.*]] = phi bfloat [ [[MUL]], %[[IF_THEN]] ], [ [[SUB:%.*]], %[[IF_ELSE]] ]
+; GFX-NEXT:    ret bfloat [[COMMON_RET_OP]]
+; GFX:       [[IF_THEN]]:
+; GFX-NEXT:    [[ADD:%.*]] = fadd contract bfloat 0xR3F80, [[MUL]]
+; GFX-NEXT:    br label %[[COMMON_RET]]
+; GFX:       [[IF_ELSE]]:
+; GFX-NEXT:    [[SUB]] = fsub contract bfloat [[MUL]], [[Y]]
+; GFX-NEXT:    br label %[[COMMON_RET]]
+;
+entry:
+  %y = load bfloat, ptr %ptr_y, align 8
+  %cmp = fcmp oeq bfloat %y, 0.000000e+00
+  %x = load bfloat, ptr %ptr_x, align 8
+  br i1 %cmp, label %if.then, label %if.else
+
+if.then:                                          ; preds = %entry
+  %a_1 = load bfloat, ptr %ptr_a, align 8
+  %mul = fmul contract bfloat %x, %a_1
+  %add = fadd contract bfloat 1.000000e+00, %mul
+  ret bfloat %mul
+
+if.else:                                          ; preds = %entry
+  %a_2 = load bfloat, ptr %ptr_a, align 8
+  %mul1 = fmul contract bfloat %x, %a_2
+  %sub = fsub contract bfloat %mul1, %y
+  ret bfloat %sub
+}
+
+define bfloat @is_profitable_bfloat_ieee(ptr dereferenceable(8) %ptr_x, ptr dereferenceable(8) %ptr_y, ptr dereferenceable(8) %ptr_a) #1 {
+; GFX-LABEL: define bfloat @is_profitable_bfloat_ieee(
+; GFX-SAME: ptr dereferenceable(8) [[PTR_X:%.*]], ptr dereferenceable(8) [[PTR_Y:%.*]], ptr dereferenceable(8) [[PTR_A:%.*]]) #[[ATTR1]] {
+; GFX-NEXT:  [[ENTRY:.*:]]
+; GFX-NEXT:    [[Y:%.*]] = load bfloat, ptr [[PTR_Y]], align 8
+; GFX-NEXT:    [[CMP:%.*]] = fcmp oeq bfloat [[Y]], 0xR0000
+; GFX-NEXT:    [[X:%.*]] = load bfloat, ptr [[PTR_X]], align 8
+; GFX-NEXT:    [[A_1:%.*]] = load bfloat, ptr [[PTR_A]], align 8
+; GFX-NEXT:    br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
+; GFX:       [[COMMON_RET:.*]]:
+; GFX-NEXT:    [[COMMON_RET_OP:%.*]] = phi bfloat [ [[MUL:%.*]], %[[IF_THEN]] ], [ [[SUB:%.*]], %[[IF_ELSE]] ]
+; GFX-NEXT:    ret bfloat [[COMMON_RET_OP]]
+; GFX:       [[IF_THEN]]:
+; GFX-NEXT:    [[MUL]] = fmul contract bfloat [[X]], [[A_1]]
+; GFX-NEXT:    [[ADD:%.*]] = fadd contract bfloat 0xR3F80, [[MUL]]
+; GFX-NEXT:    br label %[[COMMON_RET]]
+; GFX:       [[IF_ELSE]]:
+; GFX-NEXT:    [[MUL1:%.*]] = fmul contract bfloat [[X]], [[A_1]]
+; GFX-NEXT:    [[SUB]] = fsub contract bfloat [[MUL1]], [[Y]]
+; GFX-NEXT:    br label %[[COMMON_RET]]
+;
+entry:
+  %y = load bfloat, ptr %ptr_y, align 8
+  %cmp = fcmp oeq bfloat %y, 0.000000e+00
+  %x = load bfloat, ptr %ptr_x, align 8
+  br i1 %cmp, label %if.then, label %if.else
+
+if.then:                                          ; preds = %entry
+  %a_1 = load bfloat, ptr %ptr_a, align 8
+  %mul = fmul contract bfloat %x, %a_1
+  %add = fadd contract bfloat 1.000000e+00, %mul
+  ret bfloat %mul
+
+if.else:                                          ; preds = %entry
+  %a_2 = load bfloat, ptr %ptr_a, align 8
+  %mul1 = fmul contract bfloat %x, %a_2
+  %sub = fsub contract bfloat %mul1, %y
+  ret bfloat %sub
+}
+
+define double @is_profitable_constant(ptr dereferenceable(8) %ptr_x, ptr dereferenceable(8) %ptr_y, ptr dereferenceable(8) %ptr_a) #1 {
+; GFX-LABEL: define double @is_profitable_constant(
+; GFX-SAME: ptr dereferenceable(8) [[PTR_X:%.*]], ptr dereferenceable(8) [[PTR_Y:%.*]], ptr dereferenceable(8) [[PTR_A:%.*]]) #[[ATTR1]] {
+; GFX-NEXT:  [[ENTRY:.*:]]
+; GFX-NEXT:    [[Y:%.*]] = load double, ptr [[PTR_Y]], align 8
+; GFX-NEXT:    [[CMP:%.*]] = fcmp oeq double [[Y]], 0.000000e+00
+; GFX-NEXT:    [[X:%.*]] = load double, ptr [[PTR_X]], align 8
+; GFX-NEXT:    br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
+; GFX:       [[COMMON_RET:.*]]:
+; GFX-NEXT:    [[COMMON_RET_OP:%.*]] = phi double [ [[ADD:%.*]], %[[IF_THEN]] ], [ [[SUB:%.*]], %[[IF_ELSE]] ]
+; GFX-NEXT:    ret double [[COMMON_RET_OP]]
+; GFX:       [[IF_THEN]]:
+; GFX-NEXT:    [[MUL:%.*]] = fmul contract double 2.000000e+00, [[X]]
+; GFX-NEXT:    [[ADD]] = fadd contract double 1.000000e+00, [[MUL]]
+; GFX-NEXT:    br label %[[COMMON_RET]]
+; GFX:       [[IF_ELSE]]:
+; GFX-NEXT:    [[MUL1:%.*]] = fmul contract double 3.000000e+00, [[X]]
+; GFX-NEXT:    [[SUB]] = fsub contract double [[MUL1]], [[Y]]
+; GFX-NEXT:    br label %[[COMMON_RET]]
+;
+entry:
+  %y = load double, ptr %ptr_y, align 8
+  %cmp = fcmp oeq double %y, 0.000000e+00
+  %x = load double, ptr %ptr_x, align 8
+  br i1 %cmp, label %if.then, label %if.else
+
+if.then:                                          ; preds = %entry
+  %mul = fmul contract double 2.000000e+00, %x
+  %add = fadd contract double 1.000000e+00, %mul
+  ret double %add
+
+if.else:                                          ; preds = %entry
+  %mul1 = fmul contract double 3.000000e+00, %x
+  %sub = fsub contract double %mul1, %y
+  ret double %sub
+}
+
+%vec_type = type <8 x double>
+ at v1_ptr = external addrspace(3) global ptr
+ at v2_ptr = external addrspace(3) global ptr
+
+define %vec_type @is_profitable_vector(ptr dereferenceable(8) %ptr_x, ptr dereferenceable(8) %ptr_y, ptr dereferenceable(8) %ptr_a) {
+; GFX-LABEL: define <8 x double> @is_profitable_vector(
+; GFX-SAME: ptr dereferenceable(8) [[PTR_X:%.*]], ptr dereferenceable(8) [[PTR_Y:%.*]], ptr dereferenceable(8) [[PTR_A:%.*]]) #[[ATTR2:[0-9]+]] {
+; GFX-NEXT:  [[ENTRY:.*:]]
+; GFX-NEXT:    [[Y:%.*]] = load double, ptr [[PTR_Y]], align 8
+; GFX-NEXT:    [[X:%.*]] = load <8 x double>, ptr [[PTR_X]], align 8
+; GFX-NEXT:    [[V1:%.*]] = load <8 x double>, ptr addrspace(3) @v1_ptr, align 64
+; GFX-NEXT:    [[V2:%.*]] = load <8 x double>, ptr addrspace(3) @v2_ptr, align 64
+; GFX-NEXT:    [[CMP:%.*]] = fcmp oeq double [[Y]], 0.000000e+00
+; GFX-NEXT:    br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
+; GFX:       [[COMMON_RET:.*]]:
+; GFX-NEXT:    [[COMMON_RET_OP:%.*]] = phi <8 x double> [ [[ADD:%.*]], %[[IF_THEN]] ], [ [[SUB:%.*]], %[[IF_ELSE]] ]
+; GFX-NEXT:    ret <8 x double> [[COMMON_RET_OP]]
+; GFX:       [[IF_THEN]]:
+; GFX-NEXT:    [[MUL:%.*]] = fmul contract <8 x double> [[V1]], [[X]]
+; GFX-NEXT:    [[ADD]] = fadd contract <8 x double> [[V2]], [[MUL]]
+; GFX-NEXT:    br label %[[COMMON_RET]]
+; GFX:       [[IF_ELSE]]:
+; GFX-NEXT:    [[MUL1:%.*]] = fmul contract <8 x double> [[V1]], [[X]]
+; GFX-NEXT:    [[SUB]] = fsub contract <8 x double> [[MUL1]], [[V2]]
+; GFX-NEXT:    br label %[[COMMON_RET]]
+;
+entry:
+  %y = load double, ptr %ptr_y, align 8
+  %x = load %vec_type, ptr %ptr_x, align 8
+  %v1 = load %vec_type, ptr addrspace(3) @v1_ptr
+  %v2 = load %vec_type, ptr addrspace(3) @v2_ptr
+  %cmp = fcmp oeq double %y, 0.000000e+00
+  br i1 %cmp, label %if.then, label %if.else
+
+if.then:                                          ; preds = %entry
+  %mul = fmul contract %vec_type %v1, %x
+  %add = fadd contract %vec_type %v2, %mul
+  ret %vec_type %add
+
+if.else:                                          ; preds = %entry
+  %mul1 = fmul contract %vec_type %v1, %x
+  %sub = fsub contract %vec_type %mul1, %v2
+  ret %vec_type %sub
+}
+
+define double @is_profitable_f64_nocontract(ptr dereferenceable(8) %ptr_x, ptr dereferenceable(8) %ptr_y, ptr dereferenceable(8) %ptr_a) #0 {
+; FP-CONTRACT-FAST-LABEL: define double @is_profitable_f64_nocontract(
+; FP-CONTRACT-FAST-SAME: ptr dereferenceable(8) [[PTR_X:%.*]], ptr dereferenceable(8) [[PTR_Y:%.*]], ptr dereferenceable(8) [[PTR_A:%.*]]) #[[ATTR0]] {
+; FP-CONTRACT-FAST-NEXT:    [[Y:%.*]] = load double, ptr [[PTR_Y]], align 8
+; FP-CONTRACT-FAST-NEXT:    [[CMP:%.*]] = fcmp oeq double [[Y]], 0.000000e+00
+; FP-CONTRACT-FAST-NEXT:    [[X:%.*]] = load double, ptr [[PTR_X]], align 8
+; FP-CONTRACT-FAST-NEXT:    [[A_1:%.*]] = load double, ptr [[PTR_A]], align 8
+; FP-CONTRACT-FAST-NEXT:    br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
+; FP-CONTRACT-FAST:       [[COMMON_RET:.*]]:
+; FP-CONTRACT-FAST-NEXT:    [[COMMON_RET_OP:%.*]] = phi double [ [[PTR_ADD:%.*]], %[[IF_THEN]] ], [ [[SUB:%.*]], %[[IF_ELSE]] ]
+; FP-CONTRACT-FAST-NEXT:    ret double [[COMMON_RET_OP]]
+; FP-CONTRACT-FAST:       [[IF_THEN]]:
+; FP-CONTRACT-FAST-NEXT:    [[MUL:%.*]] = fmul double [[X]], [[A_1]]
+; FP-CONTRACT-FAST-NEXT:    [[PTR_ADD]] = fadd double 1.000000e+00, [[MUL]]
+; FP-CONTRACT-FAST-NEXT:    br label %[[COMMON_RET]]
+; FP-CONTRACT-FAST:       [[IF_ELSE]]:
+; FP-CONTRACT-FAST-NEXT:    [[MUL1:%.*]] = fmul double [[X]], [[A_1]]
+; FP-CONTRACT-FAST-NEXT:    [[SUB]] = fsub double [[MUL1]], [[Y]]
+; FP-CONTRACT-FAST-NEXT:    br label %[[COMMON_RET]]
+;
+; UNSAFE-FP-MATH-LABEL: define double @is_profitable_f64_nocontract(
+; UNSAFE-FP-MATH-SAME: ptr dereferenceable(8) [[PTR_X:%.*]], ptr dereferenceable(8) [[PTR_Y:%.*]], ptr dereferenceable(8) [[PTR_A:%.*]]) #[[ATTR0]] {
+; UNSAFE-FP-MATH-NEXT:    [[Y:%.*]] = load double, ptr [[PTR_Y]], align 8
+; UNSAFE-FP-MATH-NEXT:    [[CMP:%.*]] = fcmp oeq double [[Y]], 0.000000e+00
+; UNSAFE-FP-MATH-NEXT:    [[X:%.*]] = load double, ptr [[PTR_X]], align 8
+; UNSAFE-FP-MATH-NEXT:    [[A_1:%.*]] = load double, ptr [[PTR_A]], align 8
+; UNSAFE-FP-MATH-NEXT:    br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
+; UNSAFE-FP-MATH:       [[COMMON_RET:.*]]:
+; UNSAFE-FP-MATH-NEXT:    [[COMMON_RET_OP:%.*]] = phi double [ [[PTR_ADD:%.*]], %[[IF_THEN]] ], [ [[SUB:%.*]], %[[IF_ELSE]] ]
+; UNSAFE-FP-MATH-NEXT:    ret double [[COMMON_RET_OP]]
+; UNSAFE-FP-MATH:       [[IF_THEN]]:
+; UNSAFE-FP-MATH-NEXT:    [[MUL:%.*]] = fmul double [[X]], [[A_1]]
+; UNSAFE-FP-MATH-NEXT:    [[PTR_ADD]] = fadd double 1.000000e+00, [[MUL]]
+; UNSAFE-FP-MATH-NEXT:    br label %[[COMMON_RET]]
+; UNSAFE-FP-MATH:       [[IF_ELSE]]:
+; UNSAFE-FP-MATH-NEXT:    [[MUL1:%.*]] = fmul double [[X]], [[A_1]]
+; UNSAFE-FP-MATH-NEXT:    [[SUB]] = fsub double [[MUL1]], [[Y]]
+; UNSAFE-FP-MATH-NEXT:    br label %[[COMMON_RET]]
+;
+; NO-UNSAFE-FP-MATH-LABEL: define double @is_profitable_f64_nocontract(
+; NO-UNSAFE-FP-MATH-SAME: ptr dereferenceable(8) [[PTR_X:%.*]], ptr dereferenceable(8) [[PTR_Y:%.*]], ptr dereferenceable(8) [[PTR_A:%.*]]) #[[ATTR0]] {
+; NO-UNSAFE-FP-MATH-NEXT:    [[Y:%.*]] = load double, ptr [[PTR_Y]], align 8
+; NO-UNSAFE-FP-MATH-NEXT:    [[CMP:%.*]] = fcmp oeq double [[Y]], 0.000000e+00
+; NO-UNSAFE-FP-MATH-NEXT:    [[X:%.*]] = load double, ptr [[PTR_X]], align 8
+; NO-UNSAFE-FP-MATH-NEXT:    [[A_1:%.*]] = load double, ptr [[PTR_A]], align 8
+; NO-UNSAFE-FP-MATH-NEXT:    [[MUL:%.*]] = fmul double [[X]], [[A_1]]
+; NO-UNSAFE-FP-MATH-NEXT:    br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
+; NO-UNSAFE-FP-MATH:       [[COMMON_RET:.*]]:
+; NO-UNSAFE-FP-MATH-NEXT:    [[COMMON_RET_OP:%.*]] = phi double [ [[PTR_ADD:%.*]], %[[IF_THEN]] ], [ [[SUB:%.*]], %[[IF_ELSE]] ]
+; NO-UNSAFE-FP-MATH-NEXT:    ret double [[COMMON_RET_OP]]
+; NO-UNSAFE-FP-MATH:       [[IF_THEN]]:
+; NO-UNSAFE-FP-MATH-NEXT:    [[PTR_ADD]] = fadd double 1.000000e+00, [[MUL]]
+; NO-UNSAFE-FP-MATH-NEXT:    br label %[[COMMON_RET]]
+; NO-UNSAFE-FP-MATH:       [[IF_ELSE]]:
+; NO-UNSAFE-FP-MATH-NEXT:    [[SUB]] = fsub double [[MUL]], [[Y]]
+; NO-UNSAFE-FP-MATH-NEXT:    br label %[[COMMON_RET]]
+;
+  %y = load double, ptr %ptr_y, align 8
+  %cmp = fcmp oeq double %y, 0.000000e+00
+  %x = load double, ptr %ptr_x, align 8
+  br i1 %cmp, label %if.then, label %if.else
+
+if.then:                                          ; preds = %entry
+  %a_1 = load double, ptr %ptr_a, align 8
+  %mul = fmul double %x, %a_1
+  %ptr_add = fadd double 1.000000e+00, %mul
+  ret double %ptr_add
+
+if.else:                                          ; preds = %entry
+  %a_2 = load double, ptr %ptr_a, align 8
+  %mul1 = fmul double %x, %a_2
+  %sub = fsub double %mul1, %y
+  ret double %sub
+}
+
+attributes #0 = { nounwind "denormal-fp-math"="preserve-sign,preserve-sign" }
+attributes #1 = { nounwind "denormal-fp-math"="ieee,ieee" }



More information about the llvm-commits mailing list