[llvm] d3919a8 - [ConstantFolding] Respect denormal handling mode attributes when folding instructions

David Candler via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 20 08:43:06 PDT 2022


Author: David Candler
Date: 2022-06-20T16:41:46+01:00
New Revision: d3919a8cc5031fd3d8c1ce3188abc0d7551306fb

URL: https://github.com/llvm/llvm-project/commit/d3919a8cc5031fd3d8c1ce3188abc0d7551306fb
DIFF: https://github.com/llvm/llvm-project/commit/d3919a8cc5031fd3d8c1ce3188abc0d7551306fb.diff

LOG: [ConstantFolding] Respect denormal handling mode attributes when folding instructions

Depending on the environment, a floating point instruction should
treat denormal inputs as zero, and/or flush a denormal output to zero.
Denormals are not currently accounted for when an instruction gets
folded to a constant, which can lead to differences in output between
a folded and a unfolded instruction when running on the target. The
denormal handling mode can be set by the function level attribute
denormal-fp-math, which this patch uses to determine whether any
denormal inputs to or outputs from folding should be zero, and that
the sign is set appropriately.

Reviewed By: spatel

Differential Revision: https://reviews.llvm.org/D116952

Added: 
    

Modified: 
    llvm/include/llvm/Analysis/ConstantFolding.h
    llvm/lib/Analysis/ConstantFolding.cpp
    llvm/lib/Analysis/InstructionSimplify.cpp
    llvm/test/Transforms/InstSimplify/constant-fold-fp-denormal.ll

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/Analysis/ConstantFolding.h b/llvm/include/llvm/Analysis/ConstantFolding.h
index 230a64ccd0894..4f62e89c4b44e 100644
--- a/llvm/include/llvm/Analysis/ConstantFolding.h
+++ b/llvm/include/llvm/Analysis/ConstantFolding.h
@@ -85,6 +85,13 @@ Constant *ConstantFoldUnaryOpOperand(unsigned Opcode, Constant *Op,
 Constant *ConstantFoldBinaryOpOperands(unsigned Opcode, Constant *LHS,
                                        Constant *RHS, const DataLayout &DL);
 
+/// Attempt to constant fold a floating point binary operation with the
+/// specified operands, applying the denormal handling mod to the operands.  If
+/// it fails, it returns a constant expression of the specified operands.
+Constant *ConstantFoldFPInstOperands(unsigned Opcode, Constant *LHS,
+                                     Constant *RHS, const DataLayout &DL,
+                                     const Instruction *I);
+
 /// Attempt to constant fold a select instruction with the specified
 /// operands. The constant result is returned if successful; if not, null is
 /// returned.

diff  --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index 7ca988697f032..6b8525a0414d8 100644
--- a/llvm/lib/Analysis/ConstantFolding.cpp
+++ b/llvm/lib/Analysis/ConstantFolding.cpp
@@ -999,8 +999,24 @@ Constant *ConstantFoldInstOperandsImpl(const Value *InstOrCE, unsigned Opcode,
   if (Instruction::isUnaryOp(Opcode))
     return ConstantFoldUnaryOpOperand(Opcode, Ops[0], DL);
 
-  if (Instruction::isBinaryOp(Opcode))
+  if (Instruction::isBinaryOp(Opcode)) {
+    switch (Opcode) {
+    default:
+      break;
+    case Instruction::FAdd:
+    case Instruction::FSub:
+    case Instruction::FMul:
+    case Instruction::FDiv:
+    case Instruction::FRem:
+      // Handle floating point instructions separately to account for denormals
+      // TODO: If a constant expression is being folded rather than an
+      // instruction, denormals will not be flushed/treated as zero
+      if (const auto *I = dyn_cast<Instruction>(InstOrCE)) {
+        return ConstantFoldFPInstOperands(Opcode, Ops[0], Ops[1], DL, I);
+      }
+    }
     return ConstantFoldBinaryOpOperands(Opcode, Ops[0], Ops[1], DL);
+  }
 
   if (Instruction::isCast(Opcode))
     return ConstantFoldCastOperand(Opcode, Ops[0], DestTy, DL);
@@ -1295,6 +1311,63 @@ Constant *llvm::ConstantFoldBinaryOpOperands(unsigned Opcode, Constant *LHS,
   return ConstantExpr::get(Opcode, LHS, RHS);
 }
 
+// Check whether a constant is a floating point denormal that should be flushed
+// to zero according to the denormal handling mode set in the function
+// attributes. If so, return a zero with the correct sign, otherwise return the
+// original constant. Inputs and outputs to floating point instructions can have
+// their mode set separately, so the direction is also needed.
+Constant *FlushFPConstant(Constant *Operand, const llvm::Function *F,
+                          bool IsOutput) {
+  if (F == nullptr)
+    return Operand;
+  if (auto *CFP = dyn_cast<ConstantFP>(Operand)) {
+    const APFloat &APF = CFP->getValueAPF();
+    Type *Ty = CFP->getType();
+    DenormalMode DenormMode = F->getDenormalMode(Ty->getFltSemantics());
+    DenormalMode::DenormalModeKind Mode =
+        IsOutput ? DenormMode.Output : DenormMode.Input;
+    switch (Mode) {
+    default:
+      llvm_unreachable("unknown denormal mode");
+      return Operand;
+    case DenormalMode::IEEE:
+      return Operand;
+    case DenormalMode::PreserveSign:
+      if (APF.isDenormal()) {
+        return ConstantFP::get(
+            Ty->getContext(),
+            APFloat::getZero(Ty->getFltSemantics(), APF.isNegative()));
+      }
+      return Operand;
+    case DenormalMode::PositiveZero:
+      if (APF.isDenormal()) {
+        return ConstantFP::get(Ty->getContext(),
+                               APFloat::getZero(Ty->getFltSemantics(), false));
+      }
+      return Operand;
+    }
+  }
+  return Operand;
+}
+
+Constant *llvm::ConstantFoldFPInstOperands(unsigned Opcode, Constant *LHS,
+                                           Constant *RHS, const DataLayout &DL,
+                                           const Instruction *I) {
+  if (auto *BB = I->getParent()) {
+    if (auto *F = BB->getParent()) {
+      if (Instruction::isBinaryOp(Opcode)) {
+        Constant *Op0 = FlushFPConstant(LHS, F, false);
+        Constant *Op1 = FlushFPConstant(RHS, F, false);
+        Constant *C = ConstantFoldBinaryOpOperands(Opcode, Op0, Op1, DL);
+        return FlushFPConstant(C, F, true);
+      }
+    }
+  }
+  // If instruction lacks a parent/function and the denormal mode cannot be
+  // determined, use the default (IEEE).
+  return ConstantFoldBinaryOpOperands(Opcode, LHS, RHS, DL);
+}
+
 Constant *llvm::ConstantFoldCastOperand(unsigned Opcode, Constant *C,
                                         Type *DestTy, const DataLayout &DL) {
   assert(Instruction::isCast(Opcode));

diff  --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 23ec186c2f572..07e3392c226e6 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -603,8 +603,20 @@ static Constant *foldOrCommuteConstant(Instruction::BinaryOps Opcode,
                                        Value *&Op0, Value *&Op1,
                                        const SimplifyQuery &Q) {
   if (auto *CLHS = dyn_cast<Constant>(Op0)) {
-    if (auto *CRHS = dyn_cast<Constant>(Op1))
+    if (auto *CRHS = dyn_cast<Constant>(Op1)) {
+      switch (Opcode) {
+      default:
+        break;
+      case Instruction::FAdd:
+      case Instruction::FSub:
+      case Instruction::FMul:
+      case Instruction::FDiv:
+      case Instruction::FRem:
+        if (Q.CxtI != nullptr)
+          return ConstantFoldFPInstOperands(Opcode, CLHS, CRHS, Q.DL, Q.CxtI);
+      }
       return ConstantFoldBinaryOpOperands(Opcode, CLHS, CRHS, Q.DL);
+    }
 
     // Canonicalize the constant to the RHS if this is a commutative operation.
     if (Instruction::isCommutative(Opcode))

diff  --git a/llvm/test/Transforms/InstSimplify/constant-fold-fp-denormal.ll b/llvm/test/Transforms/InstSimplify/constant-fold-fp-denormal.ll
index da1be758e30d7..24722c6560df5 100644
--- a/llvm/test/Transforms/InstSimplify/constant-fold-fp-denormal.ll
+++ b/llvm/test/Transforms/InstSimplify/constant-fold-fp-denormal.ll
@@ -21,39 +21,42 @@ define float @test_float_fadd_ieee() #0 {
 
 define float @test_float_fadd_pzero_out() #1 {
 ; CHECK-LABEL: @test_float_fadd_pzero_out(
-; CHECK-NEXT:    ret float 0xB800000000000000
-; default ieee mode leaves result as a denormal
+; CHECK-NEXT:    ret float 0.000000e+00
+; denormal result is flushed to positive zero
   %result = fadd float 0xB810000000000000, 0x3800000000000000
   ret float %result
 }
 
 define float @test_float_fadd_psign_out() #2 {
 ; CHECK-LABEL: @test_float_fadd_psign_out(
-; CHECK-NEXT:    ret float 0xB800000000000000
-; default ieee mode leaves result as a denormal
+; CHECK-NEXT:    ret float -0.000000e+00
+; denormal result is flushed to sign preserved zero
   %result = fadd float 0xB810000000000000, 0x3800000000000000
   ret float %result
 }
 
 define float @test_float_fadd_pzero_in() #3 {
 ; CHECK-LABEL: @test_float_fadd_pzero_in(
-; CHECK-NEXT:    ret float 0xB800000000000000
-; default ieee mode leaves result as a denormal
+; CHECK-NEXT:    ret float 0xB810000000000000
+; denormal operand is treated as zero
+; normal operand added to zero results in the same operand as a result
   %result = fadd float 0xB810000000000000, 0x3800000000000000
   ret float %result
 }
 
 define float @test_float_fadd_psign_in() #4 {
 ; CHECK-LABEL: @test_float_fadd_psign_in(
-; CHECK-NEXT:    ret float 0xB800000000000000
-; default ieee mode leaves result as a denormal
+; CHECK-NEXT:    ret float 0xB810000000000000
+; denormal operand is treated as zero
+; normal operand added to zero results in the same operand as a result
   %result = fadd float 0xB810000000000000, 0x3800000000000000
   ret float %result
 }
 
 define float @test_float_fadd_pzero_f32_out() #5 {
 ; CHECK-LABEL: @test_float_fadd_pzero_f32_out(
-; CHECK-NEXT:    ret float 0xB800000000000000
+; CHECK-NEXT:    ret float 0.000000e+00
+; f32 only attribute should flush float output
 ; default ieee mode leaves result as a denormal
   %result = fadd float 0xB810000000000000, 0x3800000000000000
   ret float %result
@@ -69,32 +72,34 @@ define double @test_double_fadd_ieee() #0 {
 
 define double @test_double_fadd_pzero_out() #1 {
 ; CHECK-LABEL: @test_double_fadd_pzero_out(
-; CHECK-NEXT:    ret double 0x8008000000000000
-; default ieee mode leaves result as a denormal
+; CHECK-NEXT:    ret double 0.000000e+00
+; denormal result is flushed to positive zero
   %result = fadd double 0x8010000000000000, 0x8000000000000
   ret double %result
 }
 
 define double @test_double_fadd_psign_out() #2 {
 ; CHECK-LABEL: @test_double_fadd_psign_out(
-; CHECK-NEXT:    ret double 0x8008000000000000
-; default ieee mode leaves result as a denormal
+; CHECK-NEXT:    ret double -0.000000e+00
+; denormal result is flushed to sign preserved zero
   %result = fadd double 0x8010000000000000, 0x8000000000000
   ret double %result
 }
 
 define double @test_double_fadd_pzero_in() #3 {
 ; CHECK-LABEL: @test_double_fadd_pzero_in(
-; CHECK-NEXT:    ret double 0x8008000000000000
-; default ieee mode leaves result as a denormal
+; CHECK-NEXT:    ret double 0x8010000000000000
+; denormal operand is treated as zero
+; normal operand added to zero results in the same operand as a result
   %result = fadd double 0x8010000000000000, 0x8000000000000
   ret double %result
 }
 
 define double @test_double_fadd_psign_in() #4 {
 ; CHECK-LABEL: @test_double_fadd_psign_in(
-; CHECK-NEXT:    ret double 0x8008000000000000
-; default ieee mode leaves result as a denormal
+; CHECK-NEXT:    ret double 0x8010000000000000
+; denormal operand is treated as zero
+; normal operand added to zero results in the same operand as a result
   %result = fadd double 0x8010000000000000, 0x8000000000000
   ret double %result
 }
@@ -102,6 +107,7 @@ define double @test_double_fadd_psign_in() #4 {
 define double @test_double_fadd_f32_ieee() #5 {
 ; CHECK-LABEL: @test_double_fadd_f32_ieee(
 ; CHECK-NEXT:    ret double 0x8008000000000000
+; f32 only attribute should not flush doubles
 ; default ieee mode leaves result as a denormal
   %result = fadd double 0x8010000000000000, 0x8000000000000
   ret double %result
@@ -125,40 +131,43 @@ define float @test_float_fsub_ieee() #0 {
 
 define float @test_float_fsub_pzero_out() #1 {
 ; CHECK-LABEL: @test_float_fsub_pzero_out(
-; CHECK-NEXT:    ret float 0xB800000000000000
-; default ieee mode leaves result as a denormal
+; CHECK-NEXT:    ret float 0.000000e+00
+; denormal result is flushed to positive zero
   %result = fsub float 0x3800000000000000, 0x3810000000000000
   ret float %result
 }
 
 define float @test_float_fsub_psign_out() #2 {
 ; CHECK-LABEL: @test_float_fsub_psign_out(
-; CHECK-NEXT:    ret float 0xB800000000000000
-; default ieee mode leaves result as a denormal
+; CHECK-NEXT:    ret float -0.000000e+00
+; denormal result is flushed to sign preserved zero
   %result = fsub float 0x3800000000000000, 0x3810000000000000
   ret float %result
 }
 
 define float @test_float_fsub_pzero_in() #3 {
 ; CHECK-LABEL: @test_float_fsub_pzero_in(
-; CHECK-NEXT:    ret float 0xB800000000000000
-; default ieee mode leaves result as a denormal
+; CHECK-NEXT:    ret float 0xB810000000000000
+; denormal operand is treated as zero
+; normal operand subtracted from zero produces the same operand, negated
   %result = fsub float 0x3800000000000000, 0x3810000000000000
   ret float %result
 }
 
 define float @test_float_fsub_psign_in() #4 {
 ; CHECK-LABEL: @test_float_fsub_psign_in(
-; CHECK-NEXT:    ret float 0xB800000000000000
-; default ieee mode leaves result as a denormal
+; CHECK-NEXT:    ret float 0xB810000000000000
+; denormal operand is treated as zero
+; normal operand subtracted from zero produces the same operand, negated
   %result = fsub float 0x3800000000000000, 0x3810000000000000
   ret float %result
 }
 
 define float @test_float_fsub_pzero_f32_out() #5 {
 ; CHECK-LABEL: @test_float_fsub_pzero_f32_out(
-; CHECK-NEXT:    ret float 0xB800000000000000
-; default ieee mode leaves result as a denormal
+; CHECK-NEXT:    ret float 0.000000e+00
+; f32 only attribute should flush float output
+; same as pzero_out above
   %result = fsub float 0x3800000000000000, 0x3810000000000000
   ret float %result
 }
@@ -173,32 +182,34 @@ define double @test_double_fsub_ieee() #0 {
 
 define double @test_double_fsub_pzero_out() #1 {
 ; CHECK-LABEL: @test_double_fsub_pzero_out(
-; CHECK-NEXT:    ret double 0x8008000000000000
-; default ieee mode leaves result as a denormal
+; CHECK-NEXT:    ret double 0.000000e+00
+; denormal result is flushed to positive zero
   %result = fsub double 0x8000000000000, 0x10000000000000
   ret double %result
 }
 
 define double @test_double_fsub_psign_out() #2 {
 ; CHECK-LABEL: @test_double_fsub_psign_out(
-; CHECK-NEXT:    ret double 0x8008000000000000
-; default ieee mode leaves result as a denormal
+; CHECK-NEXT:    ret double -0.000000e+00
+; denormal result is flushed to sign preserved zero
   %result = fsub double 0x8000000000000, 0x10000000000000
   ret double %result
 }
 
 define double @test_double_fsub_pzero_in() #3 {
 ; CHECK-LABEL: @test_double_fsub_pzero_in(
-; CHECK-NEXT:    ret double 0x8008000000000000
-; default ieee mode leaves result as a denormal
+; CHECK-NEXT:    ret double 0x8010000000000000
+; denormal operand is treated as zero
+; normal operand subtracted from zero produces the same operand, negated
   %result = fsub double 0x8000000000000, 0x10000000000000
   ret double %result
 }
 
 define double @test_double_fsub_psign_in() #4 {
 ; CHECK-LABEL: @test_double_fsub_psign_in(
-; CHECK-NEXT:    ret double 0x8008000000000000
-; default ieee mode leaves result as a denormal
+; CHECK-NEXT:    ret double 0x8010000000000000
+; denormal operand is treated as zero
+; normal operand subtracted from zero produces the same operand, negated
   %result = fsub double 0x8000000000000, 0x10000000000000
   ret double %result
 }
@@ -206,6 +217,7 @@ define double @test_double_fsub_psign_in() #4 {
 define double @test_double_fsub_f32_ieee() #5 {
 ; CHECK-LABEL: @test_double_fsub_f32_ieee(
 ; CHECK-NEXT:    ret double 0x8008000000000000
+; f32 only attribute should not flush doubles
 ; default ieee mode leaves result as a denormal
   %result = fsub double 0x8000000000000, 0x10000000000000
   ret double %result
@@ -231,40 +243,43 @@ define float @test_float_fmul_ieee() #0 {
 
 define float @test_float_fmul_pzero_out() #1 {
 ; CHECK-LABEL: @test_float_fmul_pzero_out(
-; CHECK-NEXT:    ret float 0xB800000000000000
-; default ieee mode leaves result as a denormal
+; CHECK-NEXT:    ret float 0.000000e+00
+; denormal result is flushed to positive zero
   %result = fmul float 0x3810000000000000, -5.000000e-01
   ret float %result
 }
 
 define float @test_float_fmul_psign_out() #2 {
 ; CHECK-LABEL: @test_float_fmul_psign_out(
-; CHECK-NEXT:    ret float 0xB800000000000000
-; default ieee mode leaves result as a denormal
+; CHECK-NEXT:    ret float -0.000000e+00
+; denormal result is flushed to sign preserved zero
   %result = fmul float 0x3810000000000000, -5.000000e-01
   ret float %result
 }
 
 define float @test_float_fmul_pzero_in() #3 {
 ; CHECK-LABEL: @test_float_fmul_pzero_in(
-; CHECK-NEXT:    ret float 0xB810000000000000
-; default ieee mode leaves result as a normal
+; CHECK-NEXT:    ret float 0.000000e+00
+; denormal operand is treated as positive zero
+; anything multiplied by zero gives a zero result
   %result = fmul float 0xB800000000000000, 2.000000e-00
   ret float %result
 }
 
 define float @test_float_fmul_psign_in() #4 {
 ; CHECK-LABEL: @test_float_fmul_psign_in(
-; CHECK-NEXT:    ret float 0xB810000000000000
-; default ieee mode leaves result as a normal
+; CHECK-NEXT:    ret float -0.000000e+00
+; denormal operand is treated as signed zero
+; anything multiplied by zero gives a zero result
   %result = fmul float 0xB800000000000000, 2.000000e-00
   ret float %result
 }
 
 define float @test_float_fmul_pzero_f32_out() #1 {
 ; CHECK-LABEL: @test_float_fmul_pzero_f32_out(
-; CHECK-NEXT:    ret float 0xB800000000000000
-; default ieee mode leaves result as a denormal
+; CHECK-NEXT:    ret float 0.000000e+00
+; f32 only attribute should flush float output
+; same as pzero_out above
   %result = fmul float 0x3810000000000000, -5.000000e-01
   ret float %result
 }
@@ -279,32 +294,34 @@ define double @test_double_fmul_ieee() #0 {
 
 define double @test_double_fmul_pzero_out() #1 {
 ; CHECK-LABEL: @test_double_fmul_pzero_out(
-; CHECK-NEXT:    ret double 0x8008000000000000
-; default ieee mode leaves result as a denormal
+; CHECK-NEXT:    ret double 0.000000e+00
+; denormal result is flushed to positive zero
   %result = fmul double 0x10000000000000, -5.000000e-01
   ret double %result
 }
 
 define double @test_double_fmul_psign_out() #2 {
 ; CHECK-LABEL: @test_double_fmul_psign_out(
-; CHECK-NEXT:    ret double 0x8008000000000000
-; default ieee mode leaves result as a denormal
+; CHECK-NEXT:    ret double -0.000000e+00
+; denormal result is flushed to sign preserved zero
   %result = fmul double 0x10000000000000, -5.000000e-01
   ret double %result
 }
 
 define double @test_double_fmul_pzero_in() #3 {
 ; CHECK-LABEL: @test_double_fmul_pzero_in(
-; CHECK-NEXT:    ret double 0x8010000000000000
-; default ieee mode leaves result as a normal
+; CHECK-NEXT:    ret double 0.000000e+00
+; denormal operand is treated as positive zero
+; anything multiplied by zero gives a zero result
   %result = fmul double 0x8008000000000000, 2.000000e-00
   ret double %result
 }
 
 define double @test_double_fmul_psign_in() #4 {
 ; CHECK-LABEL: @test_double_fmul_psign_in(
-; CHECK-NEXT:    ret double 0x8010000000000000
-; default ieee mode leaves result as a normal
+; CHECK-NEXT:    ret double -0.000000e+00
+; denormal operand is treated as signed zero
+; anything multiplied by zero gives a zero result
   %result = fmul double 0x8008000000000000, 2.000000e-00
   ret double %result
 }
@@ -312,6 +329,7 @@ define double @test_double_fmul_psign_in() #4 {
 define double @test_double_fmul_f32_ieee() #5 {
 ; CHECK-LABEL: @test_double_fmul_f32_ieee(
 ; CHECK-NEXT:    ret double 0x8008000000000000
+; f32 only attribute should not flush doubles
 ; default ieee mode leaves result as a denormal
   %result = fmul double 0x10000000000000, -5.000000e-01
   ret double %result
@@ -337,40 +355,43 @@ define float @test_float_fdiv_ieee() #0 {
 
 define float @test_float_fdiv_pzero_out() #1 {
 ; CHECK-LABEL: @test_float_fdiv_pzero_out(
-; CHECK-NEXT:    ret float 0xB800000000000000
-; default ieee mode leaves result as a denormal
+; CHECK-NEXT:    ret float 0.000000e+00
+; denormal result is flushed to positive zero
   %result = fdiv float 0x3810000000000000, -2.000000e-00
   ret float %result
 }
 
 define float @test_float_fdiv_psign_out() #2 {
 ; CHECK-LABEL: @test_float_fdiv_psign_out(
-; CHECK-NEXT:    ret float 0xB800000000000000
-; default ieee mode leaves result as a denormal
+; CHECK-NEXT:    ret float -0.000000e+00
+; denormal result is flushed to sign preserved zero
   %result = fdiv float 0x3810000000000000, -2.000000e-00
   ret float %result
 }
 
 define float @test_float_fdiv_pzero_in() #3 {
 ; CHECK-LABEL: @test_float_fdiv_pzero_in(
-; CHECK-NEXT:    ret float 0xB810000000000000
-; default ieee mode leaves result as a normal
+; CHECK-NEXT:    ret float 0.000000e+00
+; denormal operand is treated as zero
+; zero divided by anything gives a zero result
   %result = fdiv float 0xB800000000000000, 5.000000e-01
   ret float %result
 }
 
 define float @test_float_fdiv_psign_in() #4 {
 ; CHECK-LABEL: @test_float_fdiv_psign_in(
-; CHECK-NEXT:    ret float 0xB7F0000000000000
-; default ieee mode leaves result as a normal
+; CHECK-NEXT:    ret float -0.000000e+00
+; denormal operand is treated as zero
+; zero divided by anything gives a zero result
   %result = fmul float 0xB800000000000000, 5.000000e-01
   ret float %result
 }
 
 define float @test_float_fdiv_pzero_f32_out() #1 {
 ; CHECK-LABEL: @test_float_fdiv_pzero_f32_out(
-; CHECK-NEXT:    ret float 0xB800000000000000
-; default ieee mode leaves result as a denormal
+; CHECK-NEXT:    ret float 0.000000e+00
+; f32 only attribute should flush float output
+; same as pzero_out above
   %result = fdiv float 0x3810000000000000, -2.000000e-00
   ret float %result
 }
@@ -385,32 +406,34 @@ define double @test_double_fdiv_ieee() #0 {
 
 define double @test_double_fdiv_pzero_out() #1 {
 ; CHECK-LABEL: @test_double_fdiv_pzero_out(
-; CHECK-NEXT:    ret double 0x8008000000000000
-; default ieee mode leaves result as a denormal
+; CHECK-NEXT:    ret double 0.000000e+00
+; denormal result is flushed to positive zero
   %result = fdiv double 0x10000000000000, -2.000000e-00
   ret double %result
 }
 
 define double @test_double_fdiv_psign_out() #2 {
 ; CHECK-LABEL: @test_double_fdiv_psign_out(
-; CHECK-NEXT:    ret double 0x8008000000000000
-; default ieee mode leaves result as a denormal
+; CHECK-NEXT:    ret double -0.000000e+00
+; denormal result is flushed to sign preserved zero
   %result = fdiv double 0x10000000000000, -2.000000e-00
   ret double %result
 }
 
 define double @test_double_fdiv_pzero_in() #3 {
 ; CHECK-LABEL: @test_double_fdiv_pzero_in(
-; CHECK-NEXT:    ret double 0x8010000000000000
-; default ieee mode leaves result as a normal
+; CHECK-NEXT:    ret double 0.000000e+00
+; denormal operand is treated as zero
+; zero divided by anything gives a zero result
   %result = fdiv double 0x8008000000000000, 5.000000e-01
   ret double %result
 }
 
 define double @test_double_fdiv_psign_in() #4 {
 ; CHECK-LABEL: @test_double_fdiv_psign_in(
-; CHECK-NEXT:    ret double 0x8010000000000000
-; default ieee mode leaves result as a normal
+; CHECK-NEXT:    ret double -0.000000e+00
+; denormal operand is treated as zero
+; zero divided by anything gives a zero result
   %result = fdiv double 0x8008000000000000, 5.000000e-01
   ret double %result
 }
@@ -418,6 +441,7 @@ define double @test_double_fdiv_psign_in() #4 {
 define double @test_double_fdiv_f32_ieee() #5 {
 ; CHECK-LABEL: @test_double_fdiv_f32_ieee(
 ; CHECK-NEXT:    ret double 0x8008000000000000
+; f32 only attribute should not flush doubles
 ; default ieee mode leaves result as a denormal
   %result = fdiv double 0x10000000000000, -2.000000e-00
   ret double %result
@@ -443,16 +467,16 @@ define float @test_float_frem_ieee_out() #0 {
 
 define float @test_float_frem_pzero_out() #1 {
 ; CHECK-LABEL: @test_float_frem_pzero_out(
-; CHECK-NEXT:    ret float 0xB800000000000000
-; default ieee mode leaves result as a denormal
+; CHECK-NEXT:    ret float 0.000000e+00
+; denormal result is flushed to positive zero
   %result = frem float 0xB818000000000000, 0x3810000000000000
   ret float %result
 }
 
 define float @test_float_frem_psign_out() #2 {
 ; CHECK-LABEL: @test_float_frem_psign_out(
-; CHECK-NEXT:    ret float 0xB800000000000000
-; default ieee mode leaves result as a denormal
+; CHECK-NEXT:    ret float -0.000000e+00
+; denormal result is flushed to sign preserved zero
   %result = frem float 0xB818000000000000, 0x3810000000000000
   ret float %result
 }
@@ -467,24 +491,27 @@ define float @test_float_frem_ieee_in() #0 {
 
 define float @test_float_frem_pzero_in() #3 {
 ; CHECK-LABEL: @test_float_frem_pzero_in(
-; CHECK-NEXT:    ret float 0x3800000000000000
-; default ieee mode leaves result same as input
+; CHECK-NEXT:    ret float 0.000000e+00
+; denormal operand is treated as zero
+; remainder is now zero
   %result = frem float 0x3800000000000000, 2.000000e+00
   ret float %result
 }
 
 define float @test_float_frem_psign_in() #4 {
 ; CHECK-LABEL: @test_float_frem_psign_in(
-; CHECK-NEXT:    ret float 0x3800000000000000
-; default ieee mode leaves result same as input
+; CHECK-NEXT:    ret float 0.000000e+00
+; denormal operand is treated as zero
+; remainder is now zero
   %result = frem float 0x3800000000000000, 2.000000e+00
   ret float %result
 }
 
 define float @test_float_frem_pzero_f32_out() #1 {
 ; CHECK-LABEL: @test_float_frem_pzero_f32_out(
-; CHECK-NEXT:    ret float 0xB800000000000000
-; default ieee mode leaves result as a denormal
+; CHECK-NEXT:    ret float 0.000000e+00
+; f32 only attribute should flush float output
+; same as pzero_out above
   %result = frem float 0xB818000000000000, 0x3810000000000000
   ret float %result
 }
@@ -499,16 +526,16 @@ define double @test_double_frem_ieee_out() #0 {
 
 define double @test_double_frem_pzero_out() #1 {
 ; CHECK-LABEL: @test_double_frem_pzero_out(
-; CHECK-NEXT:    ret double 0x8008000000000000
-; default ieee mode leaves result as a denormal
+; CHECK-NEXT:    ret double 0.000000e+00
+; denormal result is flushed to positive zero
   %result = frem double 0x8018000000000000, 0x10000000000000
   ret double %result
 }
 
 define double @test_double_frem_psign_out() #2 {
 ; CHECK-LABEL: @test_double_frem_psign_out(
-; CHECK-NEXT:    ret double 0x8008000000000000
-; default ieee mode leaves result as a denormal
+; CHECK-NEXT:    ret double -0.000000e+00
+; denormal result is flushed to sign preserved zero
   %result = frem double 0x8018000000000000, 0x10000000000000
   ret double %result
 }
@@ -523,16 +550,18 @@ define double @test_double_frem_ieee_in() #0 {
 
 define double @test_double_frem_pzero_in() #3 {
 ; CHECK-LABEL: @test_double_frem_pzero_in( 
-; CHECK-NEXT:    ret double 0x8000000000000
-; default ieee mode leaves result same as input
+; CHECK-NEXT:    ret double 0.000000e+00
+; denormal operand is treated as zero
+; remainder is now zero
   %result = frem double 0x8000000000000, 2.000000e+00
   ret double %result
 }
 
 define double @test_double_frem_psign_in() #4 {
 ; CHECK-LABEL: @test_double_frem_psign_in(
-; CHECK-NEXT:    ret double 0x8000000000000
-; default ieee mode leaves result same as input
+; CHECK-NEXT:    ret double 0.000000e+00
+; denormal operand is treated as zero
+; remainder is now zero
   %result = frem double 0x8000000000000, 2.000000e+00
   ret double %result
 }
@@ -540,6 +569,7 @@ define double @test_double_frem_psign_in() #4 {
 define double @test_double_frem_f32_ieee() #5 {
 ; CHECK-LABEL: @test_double_frem_f32_ieee(
 ; CHECK-NEXT:    ret double 0x8008000000000000
+; f32 only attribute should not flush doubles
 ; default ieee mode leaves result as a denormal
   %result = frem double 0x8018000000000000, 0x10000000000000
   ret double %result


        


More information about the llvm-commits mailing list