diff options
Diffstat (limited to 'llvm/lib/Analysis/ConstantFolding.cpp')
-rw-r--r-- | llvm/lib/Analysis/ConstantFolding.cpp | 111 |
1 files changed, 109 insertions, 2 deletions
diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp index af25dab..b28a0d6 100644 --- a/llvm/lib/Analysis/ConstantFolding.cpp +++ b/llvm/lib/Analysis/ConstantFolding.cpp @@ -1593,6 +1593,13 @@ bool llvm::canConstantFoldCallTo(const CallBase *Call, const Function *F) { case Intrinsic::rint: // Constrained intrinsics can be folded if FP environment is known // to compiler. + case Intrinsic::experimental_constrained_fma: + case Intrinsic::experimental_constrained_fmuladd: + case Intrinsic::experimental_constrained_fadd: + case Intrinsic::experimental_constrained_fsub: + case Intrinsic::experimental_constrained_fmul: + case Intrinsic::experimental_constrained_fdiv: + case Intrinsic::experimental_constrained_frem: case Intrinsic::experimental_constrained_ceil: case Intrinsic::experimental_constrained_floor: case Intrinsic::experimental_constrained_round: @@ -1854,6 +1861,56 @@ static bool getConstIntOrUndef(Value *Op, const APInt *&C) { return false; } +/// Checks if the given intrinsic call, which evaluates to constant, is allowed +/// to be folded. +/// +/// \param CI Constrained intrinsic call. +/// \param St Exception flags raised during constant evaluation. +static bool mayFoldConstrained(ConstrainedFPIntrinsic *CI, + APFloat::opStatus St) { + Optional<RoundingMode> ORM = CI->getRoundingMode(); + Optional<fp::ExceptionBehavior> EB = CI->getExceptionBehavior(); + + // If the operation does not change exception status flags, it is safe + // to fold. + if (St == APFloat::opStatus::opOK) { + // When FP exceptions are not ignored, intrinsic call will not be + // eliminated, because it is considered as having side effect. But we + // know that its evaluation does not raise exceptions, so side effect + // is absent. To allow removing the call, mark it as not accessing memory. + if (EB && *EB != fp::ExceptionBehavior::ebIgnore) + CI->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone); + return true; + } + + // If evaluation raised FP exception, the result can depend on rounding + // mode. If the latter is unknown, folding is not possible. + if (!ORM || *ORM == RoundingMode::Dynamic) + return false; + + // If FP exceptions are ignored, fold the call, even if such exception is + // raised. + if (!EB || *EB != fp::ExceptionBehavior::ebStrict) + return true; + + // Leave the calculation for runtime so that exception flags be correctly set + // in hardware. + return false; +} + +/// Returns the rounding mode that should be used for constant evaluation. +static RoundingMode +getEvaluationRoundingMode(const ConstrainedFPIntrinsic *CI) { + Optional<RoundingMode> ORM = CI->getRoundingMode(); + if (!ORM || *ORM == RoundingMode::Dynamic) + // Even if the rounding mode is unknown, try evaluating the operation. + // If it does not raise inexact exception, rounding was not applied, + // so the result is exact and does not depend on rounding mode. Whether + // other FP exceptions are raised, it does not depend on rounding mode. + return RoundingMode::NearestTiesToEven; + return *ORM; +} + static Constant *ConstantFoldScalarCall1(StringRef Name, Intrinsic::ID IntrinsicID, Type *Ty, @@ -2356,16 +2413,45 @@ static Constant *ConstantFoldScalarCall2(StringRef Name, } } - if (auto *Op1 = dyn_cast<ConstantFP>(Operands[0])) { + if (const auto *Op1 = dyn_cast<ConstantFP>(Operands[0])) { if (!Ty->isFloatingPointTy()) return nullptr; APFloat Op1V = Op1->getValueAPF(); - if (auto *Op2 = dyn_cast<ConstantFP>(Operands[1])) { + if (const auto *Op2 = dyn_cast<ConstantFP>(Operands[1])) { if (Op2->getType() != Op1->getType()) return nullptr; APFloat Op2V = Op2->getValueAPF(); + if (const auto *ConstrIntr = dyn_cast<ConstrainedFPIntrinsic>(Call)) { + RoundingMode RM = getEvaluationRoundingMode(ConstrIntr); + APFloat Res = Op1V; + APFloat::opStatus St; + switch (IntrinsicID) { + default: + return nullptr; + case Intrinsic::experimental_constrained_fadd: + St = Res.add(Op2V, RM); + break; + case Intrinsic::experimental_constrained_fsub: + St = Res.subtract(Op2V, RM); + break; + case Intrinsic::experimental_constrained_fmul: + St = Res.multiply(Op2V, RM); + break; + case Intrinsic::experimental_constrained_fdiv: + St = Res.divide(Op2V, RM); + break; + case Intrinsic::experimental_constrained_frem: + St = Res.mod(Op2V); + break; + } + if (mayFoldConstrained(const_cast<ConstrainedFPIntrinsic *>(ConstrIntr), + St)) + return ConstantFP::get(Ty->getContext(), Res); + return nullptr; + } + switch (IntrinsicID) { default: break; @@ -2437,6 +2523,8 @@ static Constant *ConstantFoldScalarCall2(StringRef Name, break; } } else if (auto *Op2C = dyn_cast<ConstantInt>(Operands[1])) { + if (!Ty->isHalfTy() && !Ty->isFloatTy() && !Ty->isDoubleTy()) + return nullptr; if (IntrinsicID == Intrinsic::powi && Ty->isHalfTy()) return ConstantFP::get( Ty->getContext(), @@ -2772,6 +2860,25 @@ static Constant *ConstantFoldScalarCall3(StringRef Name, const APFloat &C1 = Op1->getValueAPF(); const APFloat &C2 = Op2->getValueAPF(); const APFloat &C3 = Op3->getValueAPF(); + + if (const auto *ConstrIntr = dyn_cast<ConstrainedFPIntrinsic>(Call)) { + RoundingMode RM = getEvaluationRoundingMode(ConstrIntr); + APFloat Res = C1; + APFloat::opStatus St; + switch (IntrinsicID) { + default: + return nullptr; + case Intrinsic::experimental_constrained_fma: + case Intrinsic::experimental_constrained_fmuladd: + St = Res.fusedMultiplyAdd(C2, C3, RM); + break; + } + if (mayFoldConstrained( + const_cast<ConstrainedFPIntrinsic *>(ConstrIntr), St)) + return ConstantFP::get(Ty->getContext(), Res); + return nullptr; + } + switch (IntrinsicID) { default: break; case Intrinsic::amdgcn_fma_legacy: { |