Index: llvm/lib/Analysis/ConstantFolding.cpp =================================================================== --- llvm/lib/Analysis/ConstantFolding.cpp +++ llvm/lib/Analysis/ConstantFolding.cpp @@ -1000,6 +1000,45 @@ return C; } +// 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 seperately, so this information 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()); + llvm::DenormalMode::DenormalModeKind Mode = + IsOutput ? DenormMode.Output : DenormMode.Input; + switch (Mode) { + default: + llvm_unreachable("unknown denormal mode"); + return Operand; + case llvm::DenormalMode::IEEE: + return Operand; + case llvm::DenormalMode::PreserveSign: + if (APF.isDenormal()) { + return ConstantFP::get( + Ty->getContext(), + APFloat::getZero(Ty->getFltSemantics(), APF.isNegative())); + } + return Operand; + case llvm::DenormalMode::PositiveZero: + if (APF.isDenormal()) { + return ConstantFP::get(Ty->getContext(), + APFloat::getZero(Ty->getFltSemantics(), false)); + } + return Operand; + } + } + return Operand; +} + /// Attempt to constant fold an instruction with the /// specified opcode and operands. If successful, the constant result is /// returned, if not, null is returned. Note that this function can fail when @@ -1011,6 +1050,39 @@ const TargetLibraryInfo *TLI) { Type *DestTy = InstOrCE->getType(); + switch (Opcode) { + default: + break; + case Instruction::FNeg: + case Instruction::FAdd: + case Instruction::FSub: + case Instruction::FMul: + case Instruction::FDiv: + case Instruction::FRem: + // For floating point instructions, use the denormal handling mode set by + // function attributes to check whether the inputs to constant folding + // should be treated as zero, and/or whether the outputs should be flushed + // to zero. + if (auto *I = dyn_cast(InstOrCE)) { + if (auto *BB = I->getParent()) { + if (auto *F = BB->getParent()) { + if (Instruction::isUnaryOp(Opcode)) { + Constant *Op0 = FlushFPConstant(Ops[0], F, false); + Constant *C = ConstantFoldUnaryOpOperand(Opcode, Op0, DL); + return FlushFPConstant(C, F, true); + } + if (Instruction::isBinaryOp(Opcode)) { + Constant *Op0 = FlushFPConstant(Ops[0], F, false); + Constant *Op1 = FlushFPConstant(Ops[1], F, false); + Constant *C = ConstantFoldBinaryOpOperands(Opcode, Op0, Op1, DL); + return FlushFPConstant(C, F, true); + } + } + } + } + break; + } + if (Instruction::isUnaryOp(Opcode)) return ConstantFoldUnaryOpOperand(Opcode, Ops[0], DL); Index: llvm/test/Transforms/InstCombine/AArch64/constant-fold-fp-denormal.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/InstCombine/AArch64/constant-fold-fp-denormal.ll @@ -0,0 +1,72 @@ +; RUN: opt -S -instcombine < %s | FileCheck %s + +target triple = "aarch64--linux-gnu" + +define float @test_float() #0 { +; CHECK-LABEL: @test_float( +; CHECK-NEXT: ret float 0x3800000000000000 + %mul = fmul float 0x3810000000000000, 5.000000e-01 + ret float %mul +} + +define double @test_double() #0 { +; CHECK-LABEL: @test_double( +; CHECK-NEXT: ret double 0x8000000000000 + %mul = fmul double 0x10000000000000, 5.000000e-01 + ret double %mul +} + +define float @test_float_positive() #1 { +; CHECK-LABEL: @test_float_positive( +; CHECK-NEXT: ret float 0.000000e+00 + %mul = fmul float 0x3810000000000000, 5.000000e-01 + ret float %mul +} + +define double @test_double_positive() #1 { +; CHECK-LABEL: @test_double_positive( +; CHECK-NEXT: ret double 0.000000e+00 + %mul = fmul double 0x10000000000000, 5.000000e-01 + ret double %mul +} + +define float @test_float_preserve() #2 { +; CHECK-LABEL: @test_float_preserve( +; CHECK-NEXT: ret float -0.000000e+00 + %mul = fmul float 0x3810000000000000, -5.000000e-01 + ret float %mul +} + +define double @test_double_preserve() #2 { +; CHECK-LABEL: @test_double_preserve( +; CHECK-NEXT: ret double -0.000000e+00 + %mul = fmul double 0x10000000000000, -5.000000e-01 + ret double %mul +} + +define float @test_float_positive_f32() #3 { +; CHECK-LABEL: @test_float_positive_f32( +; CHECK-NEXT: ret float 0.000000e+00 + %mul = fmul float 0x3810000000000000, 5.000000e-01 + ret float %mul +} + +define double @test_double_positive_f32() #3 { +; CHECK-LABEL: @test_double_positive_f32( +; CHECK-NEXT: ret double 0x8000000000000 + %mul = fmul double 0x10000000000000, 5.000000e-01 + ret double %mul +} + +define double @test_double_input() #4 { +; CHECK-LABEL: @test_double_input( +; CHECK-NEXT: ret double 0.000000e+00 + %mul = fmul double 0x8000000000000, 1.000000e+10 + ret double %mul +} + +attributes #0 = { nounwind "denormal-fp-math"="ieee,ieee" } +attributes #1 = { nounwind "denormal-fp-math"="positive-zero,ieee" } +attributes #2 = { nounwind "denormal-fp-math"="preserve-sign,ieee" } +attributes #3 = { nounwind "denormal-fp-math"="ieee,ieee" "denormal-fp-math-f32"="positive-zero,ieee" } +attributes #4 = { nounwind "denormal-fp-math"="ieee,positive-zero" } Index: llvm/test/Transforms/InstCombine/ARM/constant-fold-fp-denormal.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/InstCombine/ARM/constant-fold-fp-denormal.ll @@ -0,0 +1,72 @@ +; RUN: opt -S -instcombine < %s | FileCheck %s + +target triple = "armv8-arm-none-eabi" + +define float @test_float() #0 { +; CHECK-LABEL: @test_float( +; CHECK-NEXT: ret float 0x3800000000000000 + %mul = fmul float 0x3810000000000000, 5.000000e-01 + ret float %mul +} + +define double @test_double() #0 { +; CHECK-LABEL: @test_double( +; CHECK-NEXT: ret double 0x8000000000000 + %mul = fmul double 0x10000000000000, 5.000000e-01 + ret double %mul +} + +define float @test_float_positive() #1 { +; CHECK-LABEL: @test_float_positive( +; CHECK-NEXT: ret float 0.000000e+00 + %mul = fmul float 0x3810000000000000, 5.000000e-01 + ret float %mul +} + +define double @test_double_positive() #1 { +; CHECK-LABEL: @test_double_positive( +; CHECK-NEXT: ret double 0.000000e+00 + %mul = fmul double 0x10000000000000, 5.000000e-01 + ret double %mul +} + +define float @test_float_preserve() #2 { +; CHECK-LABEL: @test_float_preserve( +; CHECK-NEXT: ret float -0.000000e+00 + %mul = fmul float 0x3810000000000000, -5.000000e-01 + ret float %mul +} + +define double @test_double_preserve() #2 { +; CHECK-LABEL: @test_double_preserve( +; CHECK-NEXT: ret double -0.000000e+00 + %mul = fmul double 0x10000000000000, -5.000000e-01 + ret double %mul +} + +define float @test_float_positive_f32() #3 { +; CHECK-LABEL: @test_float_positive_f32( +; CHECK-NEXT: ret float 0.000000e+00 + %mul = fmul float 0x3810000000000000, 5.000000e-01 + ret float %mul +} + +define double @test_double_positive_f32() #3 { +; CHECK-LABEL: @test_double_positive_f32( +; CHECK-NEXT: ret double 0x8000000000000 + %mul = fmul double 0x10000000000000, 5.000000e-01 + ret double %mul +} + +define double @test_double_input() #4 { +; CHECK-LABEL: @test_double_input( +; CHECK-NEXT: ret double 0.000000e+00 + %mul = fmul double 0x8000000000000, 1.000000e+10 + ret double %mul +} + +attributes #0 = { nounwind "denormal-fp-math"="ieee,ieee" } +attributes #1 = { nounwind "denormal-fp-math"="positive-zero,ieee" } +attributes #2 = { nounwind "denormal-fp-math"="preserve-sign,ieee" } +attributes #3 = { nounwind "denormal-fp-math"="ieee,ieee" "denormal-fp-math-f32"="positive-zero,ieee" } +attributes #4 = { nounwind "denormal-fp-math"="ieee,positive-zero" }