diff --git a/llvm/include/llvm/Analysis/ConstantFolding.h b/llvm/include/llvm/Analysis/ConstantFolding.h --- a/llvm/include/llvm/Analysis/ConstantFolding.h +++ b/llvm/include/llvm/Analysis/ConstantFolding.h @@ -85,6 +85,13 @@ 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 --- a/llvm/lib/Analysis/ConstantFolding.cpp +++ b/llvm/lib/Analysis/ConstantFolding.cpp @@ -999,8 +999,24 @@ 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(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 @@ 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(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 --- a/llvm/lib/Analysis/InstructionSimplify.cpp +++ b/llvm/lib/Analysis/InstructionSimplify.cpp @@ -603,8 +603,20 @@ Value *&Op0, Value *&Op1, const SimplifyQuery &Q) { if (auto *CLHS = dyn_cast(Op0)) { - if (auto *CRHS = dyn_cast(Op1)) + if (auto *CRHS = dyn_cast(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 --- 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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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