diff --git a/llvm/include/llvm/Analysis/InstructionSimplify.h b/llvm/include/llvm/Analysis/InstructionSimplify.h --- a/llvm/include/llvm/Analysis/InstructionSimplify.h +++ b/llvm/include/llvm/Analysis/InstructionSimplify.h @@ -158,22 +158,26 @@ /// Given operands for an FAdd, fold the result or return null. Value *SimplifyFAddInst(Value *LHS, Value *RHS, FastMathFlags FMF, - const SimplifyQuery &Q); + const SimplifyQuery &Q, bool IsDefaultFPEnv = true, + fp::ExceptionBehavior ExBehavior = fp::ebIgnore); /// Given operands for an FSub, fold the result or return null. Value *SimplifyFSubInst(Value *LHS, Value *RHS, FastMathFlags FMF, - const SimplifyQuery &Q); + const SimplifyQuery &Q, bool IsDefaultFPEnv = true, + fp::ExceptionBehavior ExBehavior = fp::ebIgnore); /// Given operands for an FMul, fold the result or return null. Value *SimplifyFMulInst(Value *LHS, Value *RHS, FastMathFlags FMF, - const SimplifyQuery &Q); + const SimplifyQuery &Q, bool IsDefaultFPEnv = true, + fp::ExceptionBehavior ExBehavior = fp::ebIgnore); /// Given operands for the multiplication of a FMA, fold the result or return /// null. In contrast to SimplifyFMulInst, this function will not perform /// simplifications whose unrounded results differ when rounded to the argument /// type. Value *SimplifyFMAFMul(Value *LHS, Value *RHS, FastMathFlags FMF, - const SimplifyQuery &Q); + const SimplifyQuery &Q, bool IsDefaultFPEnv = true, + fp::ExceptionBehavior ExBehavior = fp::ebIgnore); /// Given operands for a Mul, fold the result or return null. Value *SimplifyMulInst(Value *LHS, Value *RHS, const SimplifyQuery &Q); @@ -186,7 +190,8 @@ /// Given operands for an FDiv, fold the result or return null. Value *SimplifyFDivInst(Value *LHS, Value *RHS, FastMathFlags FMF, - const SimplifyQuery &Q); + const SimplifyQuery &Q, bool IsDefaultFPEnv = true, + fp::ExceptionBehavior ExBehavior = fp::ebIgnore); /// Given operands for an SRem, fold the result or return null. Value *SimplifySRemInst(Value *LHS, Value *RHS, const SimplifyQuery &Q); @@ -196,7 +201,8 @@ /// Given operands for an FRem, fold the result or return null. Value *SimplifyFRemInst(Value *LHS, Value *RHS, FastMathFlags FMF, - const SimplifyQuery &Q); + const SimplifyQuery &Q, bool IsDefaultFPEnv = true, + fp::ExceptionBehavior ExBehavior = fp::ebIgnore); /// Given operands for a Shl, fold the result or return null. Value *SimplifyShlInst(Value *Op0, Value *Op1, bool isNSW, bool isNUW, 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 @@ -4802,9 +4802,9 @@ /// Perform folds that are common to any floating-point operation. This implies /// transforms based on undef/NaN because the operation itself makes no /// difference to the result. -static Constant *simplifyFPOp(ArrayRef Ops, - FastMathFlags FMF, - const SimplifyQuery &Q) { +static Constant *simplifyFPOp(ArrayRef Ops, FastMathFlags FMF, + const SimplifyQuery &Q, + fp::ExceptionBehavior ExBehavior) { for (Value *V : Ops) { bool IsNan = match(V, m_NaN()); bool IsInf = match(V, m_Inf()); @@ -4818,7 +4818,7 @@ if (FMF.noInfs() && (IsInf || IsUndef)) return PoisonValue::get(V->getType()); - if (IsUndef || IsNan) + if ((IsUndef || IsNan) && ExBehavior != fp::ebStrict) return propagateNaN(cast(V)); } return nullptr; @@ -4826,14 +4826,21 @@ /// Given operands for an FAdd, see if we can fold the result. If not, this /// returns null. -static Value *SimplifyFAddInst(Value *Op0, Value *Op1, FastMathFlags FMF, - const SimplifyQuery &Q, unsigned MaxRecurse) { - if (Constant *C = foldOrCommuteConstant(Instruction::FAdd, Op0, Op1, Q)) - return C; +static Value * +SimplifyFAddInst(Value *Op0, Value *Op1, FastMathFlags FMF, + const SimplifyQuery &Q, unsigned MaxRecurse, + bool IsDefaultFPEnv = true, + fp::ExceptionBehavior ExBehavior = fp::ebIgnore) { + if (IsDefaultFPEnv) + if (Constant *C = foldOrCommuteConstant(Instruction::FAdd, Op0, Op1, Q)) + return C; - if (Constant *C = simplifyFPOp({Op0, Op1}, FMF, Q)) + if (Constant *C = simplifyFPOp({Op0, Op1}, FMF, Q, ExBehavior)) return C; + if (!IsDefaultFPEnv) + return nullptr; + // fadd X, -0 ==> X if (match(Op1, m_NegZeroFP())) return Op0; @@ -4873,14 +4880,21 @@ /// Given operands for an FSub, see if we can fold the result. If not, this /// returns null. -static Value *SimplifyFSubInst(Value *Op0, Value *Op1, FastMathFlags FMF, - const SimplifyQuery &Q, unsigned MaxRecurse) { - if (Constant *C = foldOrCommuteConstant(Instruction::FSub, Op0, Op1, Q)) - return C; +static Value * +SimplifyFSubInst(Value *Op0, Value *Op1, FastMathFlags FMF, + const SimplifyQuery &Q, unsigned MaxRecurse, + bool IsDefaultFPEnv = true, + fp::ExceptionBehavior ExBehavior = fp::ebIgnore) { + if (IsDefaultFPEnv) + if (Constant *C = foldOrCommuteConstant(Instruction::FSub, Op0, Op1, Q)) + return C; - if (Constant *C = simplifyFPOp({Op0, Op1}, FMF, Q)) + if (Constant *C = simplifyFPOp({Op0, Op1}, FMF, Q, ExBehavior)) return C; + if (!IsDefaultFPEnv) + return nullptr; + // fsub X, +0 ==> X if (match(Op1, m_PosZeroFP())) return Op0; @@ -4919,10 +4933,15 @@ } static Value *SimplifyFMAFMul(Value *Op0, Value *Op1, FastMathFlags FMF, - const SimplifyQuery &Q, unsigned MaxRecurse) { - if (Constant *C = simplifyFPOp({Op0, Op1}, FMF, Q)) + const SimplifyQuery &Q, unsigned MaxRecurse, + bool IsDefaultFPEnv, + fp::ExceptionBehavior ExBehavior) { + if (Constant *C = simplifyFPOp({Op0, Op1}, FMF, Q, ExBehavior)) return C; + if (!IsDefaultFPEnv) + return nullptr; + // fmul X, 1.0 ==> X if (match(Op1, m_FPOne())) return Op0; @@ -4953,43 +4972,60 @@ /// Given the operands for an FMul, see if we can fold the result static Value *SimplifyFMulInst(Value *Op0, Value *Op1, FastMathFlags FMF, - const SimplifyQuery &Q, unsigned MaxRecurse) { - if (Constant *C = foldOrCommuteConstant(Instruction::FMul, Op0, Op1, Q)) - return C; + const SimplifyQuery &Q, unsigned MaxRecurse, + bool IsDefaultFPEnv, + fp::ExceptionBehavior ExBehavior) { + if (IsDefaultFPEnv) + if (Constant *C = foldOrCommuteConstant(Instruction::FMul, Op0, Op1, Q)) + return C; // Now apply simplifications that do not require rounding. - return SimplifyFMAFMul(Op0, Op1, FMF, Q, MaxRecurse); + return SimplifyFMAFMul(Op0, Op1, FMF, Q, MaxRecurse, IsDefaultFPEnv, + ExBehavior); } Value *llvm::SimplifyFAddInst(Value *Op0, Value *Op1, FastMathFlags FMF, - const SimplifyQuery &Q) { - return ::SimplifyFAddInst(Op0, Op1, FMF, Q, RecursionLimit); + const SimplifyQuery &Q, bool IsDefaultFPEnv, + fp::ExceptionBehavior ExBehavior) { + return ::SimplifyFAddInst(Op0, Op1, FMF, Q, RecursionLimit, IsDefaultFPEnv, + ExBehavior); } - Value *llvm::SimplifyFSubInst(Value *Op0, Value *Op1, FastMathFlags FMF, - const SimplifyQuery &Q) { - return ::SimplifyFSubInst(Op0, Op1, FMF, Q, RecursionLimit); + const SimplifyQuery &Q, bool IsDefaultFPEnv, + fp::ExceptionBehavior ExBehavior) { + return ::SimplifyFSubInst(Op0, Op1, FMF, Q, RecursionLimit, IsDefaultFPEnv, + ExBehavior); } Value *llvm::SimplifyFMulInst(Value *Op0, Value *Op1, FastMathFlags FMF, - const SimplifyQuery &Q) { - return ::SimplifyFMulInst(Op0, Op1, FMF, Q, RecursionLimit); + const SimplifyQuery &Q, bool IsDefaultFPEnv, + fp::ExceptionBehavior ExBehavior) { + return ::SimplifyFMulInst(Op0, Op1, FMF, Q, RecursionLimit, IsDefaultFPEnv, + ExBehavior); } Value *llvm::SimplifyFMAFMul(Value *Op0, Value *Op1, FastMathFlags FMF, - const SimplifyQuery &Q) { - return ::SimplifyFMAFMul(Op0, Op1, FMF, Q, RecursionLimit); + const SimplifyQuery &Q, bool IsDefaultFPEnv, + fp::ExceptionBehavior ExBehavior) { + return ::SimplifyFMAFMul(Op0, Op1, FMF, Q, RecursionLimit, IsDefaultFPEnv, + ExBehavior); } static Value *SimplifyFDivInst(Value *Op0, Value *Op1, FastMathFlags FMF, - const SimplifyQuery &Q, unsigned) { - if (Constant *C = foldOrCommuteConstant(Instruction::FDiv, Op0, Op1, Q)) - return C; + const SimplifyQuery &Q, unsigned, + bool IsDefaultFPEnv, + fp::ExceptionBehavior ExBehavior) { + if (IsDefaultFPEnv) + if (Constant *C = foldOrCommuteConstant(Instruction::FDiv, Op0, Op1, Q)) + return C; - if (Constant *C = simplifyFPOp({Op0, Op1}, FMF, Q)) + if (Constant *C = simplifyFPOp({Op0, Op1}, FMF, Q, ExBehavior)) return C; + if (!IsDefaultFPEnv) + return nullptr; + // X / 1.0 -> X if (match(Op1, m_FPOne())) return Op0; @@ -5023,18 +5059,26 @@ } Value *llvm::SimplifyFDivInst(Value *Op0, Value *Op1, FastMathFlags FMF, - const SimplifyQuery &Q) { - return ::SimplifyFDivInst(Op0, Op1, FMF, Q, RecursionLimit); + const SimplifyQuery &Q, bool IsDefaultFPEnv, + fp::ExceptionBehavior ExBehavior) { + return ::SimplifyFDivInst(Op0, Op1, FMF, Q, RecursionLimit, IsDefaultFPEnv, + ExBehavior); } static Value *SimplifyFRemInst(Value *Op0, Value *Op1, FastMathFlags FMF, - const SimplifyQuery &Q, unsigned) { - if (Constant *C = foldOrCommuteConstant(Instruction::FRem, Op0, Op1, Q)) - return C; + const SimplifyQuery &Q, unsigned, + bool IsDefaultFPEnv, + fp::ExceptionBehavior ExBehavior) { + if (IsDefaultFPEnv) + if (Constant *C = foldOrCommuteConstant(Instruction::FRem, Op0, Op1, Q)) + return C; - if (Constant *C = simplifyFPOp({Op0, Op1}, FMF, Q)) + if (Constant *C = simplifyFPOp({Op0, Op1}, FMF, Q, ExBehavior)) return C; + if (!IsDefaultFPEnv) + return nullptr; + // Unlike fdiv, the result of frem always matches the sign of the dividend. // The constant match may include undef elements in a vector, so return a full // zero constant as the result. @@ -5051,8 +5095,10 @@ } Value *llvm::SimplifyFRemInst(Value *Op0, Value *Op1, FastMathFlags FMF, - const SimplifyQuery &Q) { - return ::SimplifyFRemInst(Op0, Op1, FMF, Q, RecursionLimit); + const SimplifyQuery &Q, bool IsDefaultFPEnv, + fp::ExceptionBehavior ExBehavior) { + return ::SimplifyFRemInst(Op0, Op1, FMF, Q, RecursionLimit, IsDefaultFPEnv, + ExBehavior); } //=== Helper functions for higher up the class hierarchy. @@ -5698,12 +5744,22 @@ } return nullptr; } + case Intrinsic::experimental_constrained_fma: { + Value *Op0 = Call->getArgOperand(0); + Value *Op1 = Call->getArgOperand(1); + Value *Op2 = Call->getArgOperand(2); + auto *FPI = cast(Call); + if (Value *V = simplifyFPOp({Op0, Op1, Op2}, {}, Q, + FPI->getExceptionBehavior().getValue())) + return V; + return nullptr; + } case Intrinsic::fma: case Intrinsic::fmuladd: { Value *Op0 = Call->getArgOperand(0); Value *Op1 = Call->getArgOperand(1); Value *Op2 = Call->getArgOperand(2); - if (Value *V = simplifyFPOp({ Op0, Op1, Op2 }, {}, Q)) + if (Value *V = simplifyFPOp({Op0, Op1, Op2}, {}, Q, fp::ebIgnore)) return V; return nullptr; } @@ -5755,6 +5811,46 @@ return nullptr; } + case Intrinsic::experimental_constrained_fadd: { + auto *FPI = cast(Call); + return SimplifyFAddInst(FPI->getArgOperand(0), FPI->getArgOperand(1), + FPI->getFastMathFlags(), Q, + FPI->isDefaultFPEnvironment(), + FPI->getExceptionBehavior().getValue()); + break; + } + case Intrinsic::experimental_constrained_fsub: { + auto *FPI = cast(Call); + return SimplifyFSubInst(FPI->getArgOperand(0), FPI->getArgOperand(1), + FPI->getFastMathFlags(), Q, + FPI->isDefaultFPEnvironment(), + FPI->getExceptionBehavior().getValue()); + break; + } + case Intrinsic::experimental_constrained_fmul: { + auto *FPI = cast(Call); + return SimplifyFMulInst(FPI->getArgOperand(0), FPI->getArgOperand(1), + FPI->getFastMathFlags(), Q, + FPI->isDefaultFPEnvironment(), + FPI->getExceptionBehavior().getValue()); + break; + } + case Intrinsic::experimental_constrained_fdiv: { + auto *FPI = cast(Call); + return SimplifyFDivInst(FPI->getArgOperand(0), FPI->getArgOperand(1), + FPI->getFastMathFlags(), Q, + FPI->isDefaultFPEnvironment(), + FPI->getExceptionBehavior().getValue()); + break; + } + case Intrinsic::experimental_constrained_frem: { + auto *FPI = cast(Call); + return SimplifyFRemInst(FPI->getArgOperand(0), FPI->getArgOperand(1), + FPI->getFastMathFlags(), Q, + FPI->isDefaultFPEnvironment(), + FPI->getExceptionBehavior().getValue()); + break; + } default: return nullptr; } diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp --- a/llvm/lib/Transforms/Utils/Local.cpp +++ b/llvm/lib/Transforms/Utils/Local.cpp @@ -473,6 +473,14 @@ return false; } + + if (auto *FPI = dyn_cast(I)) { + Optional ExBehavior = FPI->getExceptionBehavior(); + if (ExBehavior.hasValue()) + return (ExBehavior.getValue() != fp::ebStrict); + else + return true; + } } if (isAllocLikeFn(I, TLI)) diff --git a/llvm/test/Transforms/InstSimplify/fp-undef-poison-strictfp.ll b/llvm/test/Transforms/InstSimplify/fp-undef-poison-strictfp.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/InstSimplify/fp-undef-poison-strictfp.ll @@ -0,0 +1,895 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -instsimplify -S | FileCheck %s + +; TODO: the instructions with poison operands should return poison + +; +; constrained fadd +; + +define float @fadd_undef_op0_strict(float %x) #0 { +; CHECK-LABEL: @fadd_undef_op0_strict( +; CHECK-NEXT: [[R:%.*]] = call float @llvm.experimental.constrained.fadd.f32(float undef, float [[X:%.*]], metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0:[0-9]+]] +; CHECK-NEXT: ret float [[R]] +; + %r = call float @llvm.experimental.constrained.fadd.f32(float undef, float %x, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 + ret float %r +} + +define float @fadd_undef_op0_maytrap(float %x) #0 { +; CHECK-LABEL: @fadd_undef_op0_maytrap( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fadd.f32(float undef, float %x, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + ret float %r +} + +define float @fadd_undef_op0_upward(float %x) #0 { +; CHECK-LABEL: @fadd_undef_op0_upward( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fadd.f32(float undef, float %x, metadata !"round.upward", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fadd_undef_op0_defaultfp(float %x) #0 { +; CHECK-LABEL: @fadd_undef_op0_defaultfp( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fadd.f32(float undef, float %x, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fadd_poison_op0_strict(float %x) #0 { +; CHECK-LABEL: @fadd_poison_op0_strict( +; CHECK-NEXT: [[R:%.*]] = call float @llvm.experimental.constrained.fadd.f32(float poison, float [[X:%.*]], metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[R]] +; + %r = call float @llvm.experimental.constrained.fadd.f32(float poison, float %x, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 + ret float %r +} + +define float @fadd_poison_op0_maytrap(float %x) #0 { +; CHECK-LABEL: @fadd_poison_op0_maytrap( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fadd.f32(float poison, float %x, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + ret float %r +} + +define float @fadd_poison_op0_upward(float %x) #0 { +; CHECK-LABEL: @fadd_poison_op0_upward( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fadd.f32(float poison, float %x, metadata !"round.upward", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fadd_poison_op0_defaultfp(float %x) #0 { +; CHECK-LABEL: @fadd_poison_op0_defaultfp( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fadd.f32(float poison, float %x, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fadd_undef_op1_strict(float %x) #0 { +; CHECK-LABEL: @fadd_undef_op1_strict( +; CHECK-NEXT: [[R:%.*]] = call float @llvm.experimental.constrained.fadd.f32(float [[X:%.*]], float undef, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[R]] +; + %r = call float @llvm.experimental.constrained.fadd.f32(float %x, float undef, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 + ret float %r +} + +define float @fadd_undef_op1_maytrap(float %x) #0 { +; CHECK-LABEL: @fadd_undef_op1_maytrap( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fadd.f32(float %x, float undef, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + ret float %r +} + +define float @fadd_undef_op1_upward(float %x) #0 { +; CHECK-LABEL: @fadd_undef_op1_upward( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fadd.f32(float %x, float undef, metadata !"round.upward", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fadd_undef_op1_defaultfp(float %x) #0 { +; CHECK-LABEL: @fadd_undef_op1_defaultfp( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fadd.f32(float %x, float undef, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fadd_poison_op1_strict(float %x) #0 { +; CHECK-LABEL: @fadd_poison_op1_strict( +; CHECK-NEXT: [[R:%.*]] = call float @llvm.experimental.constrained.fadd.f32(float [[X:%.*]], float poison, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[R]] +; + %r = call float @llvm.experimental.constrained.fadd.f32(float %x, float poison, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 + ret float %r +} + +define float @fadd_poison_op1_maytrap(float %x) #0 { +; CHECK-LABEL: @fadd_poison_op1_maytrap( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fadd.f32(float %x, float poison, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + ret float %r +} + +define float @fadd_poison_op1_upward(float %x) #0 { +; CHECK-LABEL: @fadd_poison_op1_upward( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fadd.f32(float %x, float poison, metadata !"round.upward", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fadd_poison_op1_defaultfp(float %x) #0 { +; CHECK-LABEL: @fadd_poison_op1_defaultfp( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fadd.f32(float %x, float poison, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret float %r +} + +; +; constrained fsub +; + +define float @fsub_undef_op0_strict(float %x) { +; CHECK-LABEL: @fsub_undef_op0_strict( +; CHECK-NEXT: [[R:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float undef, float [[X:%.*]], metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[R]] +; + %r = call float @llvm.experimental.constrained.fsub.f32(float undef, float %x, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 + ret float %r +} + +define float @fsub_undef_op0_maytrap(float %x) { +; CHECK-LABEL: @fsub_undef_op0_maytrap( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fsub.f32(float undef, float %x, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + ret float %r +} + +define float @fsub_undef_op0_upward(float %x) { +; CHECK-LABEL: @fsub_undef_op0_upward( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fsub.f32(float undef, float %x, metadata !"round.upward", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fsub_undef_op0_defaultfp(float %x) { +; CHECK-LABEL: @fsub_undef_op0_defaultfp( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fsub.f32(float undef, float %x, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fsub_poison_op0_strict(float %x) { +; CHECK-LABEL: @fsub_poison_op0_strict( +; CHECK-NEXT: [[R:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float poison, float [[X:%.*]], metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[R]] +; + %r = call float @llvm.experimental.constrained.fsub.f32(float poison, float %x, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 + ret float %r +} + +define float @fsub_poison_op0_maytrap(float %x) { +; CHECK-LABEL: @fsub_poison_op0_maytrap( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fsub.f32(float poison, float %x, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + ret float %r +} + +define float @fsub_poison_op0_upward(float %x) { +; CHECK-LABEL: @fsub_poison_op0_upward( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fsub.f32(float poison, float %x, metadata !"round.upward", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fsub_poison_op0_defaultfp(float %x) { +; CHECK-LABEL: @fsub_poison_op0_defaultfp( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fsub.f32(float poison, float %x, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fsub_undef_op1_strict(float %x) { +; CHECK-LABEL: @fsub_undef_op1_strict( +; CHECK-NEXT: [[R:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float [[X:%.*]], float undef, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[R]] +; + %r = call float @llvm.experimental.constrained.fsub.f32(float %x, float undef, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 + ret float %r +} + +define float @fsub_undef_op1_maytrap(float %x) { +; CHECK-LABEL: @fsub_undef_op1_maytrap( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fsub.f32(float %x, float undef, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + ret float %r +} + +define float @fsub_undef_op1_upward(float %x) { +; CHECK-LABEL: @fsub_undef_op1_upward( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fsub.f32(float %x, float undef, metadata !"round.upward", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fsub_undef_op1_defaultfp(float %x) { +; CHECK-LABEL: @fsub_undef_op1_defaultfp( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fsub.f32(float %x, float undef, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fsub_poison_op1_strict(float %x) { +; CHECK-LABEL: @fsub_poison_op1_strict( +; CHECK-NEXT: [[R:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float [[X:%.*]], float poison, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[R]] +; + %r = call float @llvm.experimental.constrained.fsub.f32(float %x, float poison, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 + ret float %r +} + +define float @fsub_poison_op1_maytrap(float %x) { +; CHECK-LABEL: @fsub_poison_op1_maytrap( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fsub.f32(float %x, float poison, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + ret float %r +} + +define float @fsub_poison_op1_upward(float %x) { +; CHECK-LABEL: @fsub_poison_op1_upward( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fsub.f32(float %x, float poison, metadata !"round.upward", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fsub_poison_op1_defaultfp(float %x) { +; CHECK-LABEL: @fsub_poison_op1_defaultfp( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fsub.f32(float %x, float poison, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret float %r +} + +; +; constrained fmul +; + +define float @fmul_undef_op0_strict(float %x) { +; CHECK-LABEL: @fmul_undef_op0_strict( +; CHECK-NEXT: [[R:%.*]] = call float @llvm.experimental.constrained.fmul.f32(float undef, float [[X:%.*]], metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[R]] +; + %r = call float @llvm.experimental.constrained.fmul.f32(float undef, float %x, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 + ret float %r +} + +define float @fmul_undef_op0_maytrap(float %x) { +; CHECK-LABEL: @fmul_undef_op0_maytrap( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fmul.f32(float undef, float %x, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + ret float %r +} + +define float @fmul_undef_op0_upward(float %x) { +; CHECK-LABEL: @fmul_undef_op0_upward( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fmul.f32(float undef, float %x, metadata !"round.upward", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fmul_undef_op0_defaultfp(float %x) { +; CHECK-LABEL: @fmul_undef_op0_defaultfp( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fmul.f32(float undef, float %x, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fmul_poison_op0_strict(float %x) { +; CHECK-LABEL: @fmul_poison_op0_strict( +; CHECK-NEXT: [[R:%.*]] = call float @llvm.experimental.constrained.fmul.f32(float poison, float [[X:%.*]], metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[R]] +; + %r = call float @llvm.experimental.constrained.fmul.f32(float poison, float %x, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 + ret float %r +} + +define float @fmul_poison_op0_maytrap(float %x) { +; CHECK-LABEL: @fmul_poison_op0_maytrap( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fmul.f32(float poison, float %x, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + ret float %r +} + +define float @fmul_poison_op0_upward(float %x) { +; CHECK-LABEL: @fmul_poison_op0_upward( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fmul.f32(float poison, float %x, metadata !"round.upward", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fmul_poison_op0_defaultfp(float %x) { +; CHECK-LABEL: @fmul_poison_op0_defaultfp( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fmul.f32(float poison, float %x, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fmul_undef_op1_strict(float %x) { +; CHECK-LABEL: @fmul_undef_op1_strict( +; CHECK-NEXT: [[R:%.*]] = call float @llvm.experimental.constrained.fmul.f32(float [[X:%.*]], float undef, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[R]] +; + %r = call float @llvm.experimental.constrained.fmul.f32(float %x, float undef, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 + ret float %r +} + +define float @fmul_undef_op1_maytrap(float %x) { +; CHECK-LABEL: @fmul_undef_op1_maytrap( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fmul.f32(float %x, float undef, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + ret float %r +} + +define float @fmul_undef_op1_upward(float %x) { +; CHECK-LABEL: @fmul_undef_op1_upward( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fmul.f32(float %x, float undef, metadata !"round.upward", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fmul_undef_op1_defaultfp(float %x) { +; CHECK-LABEL: @fmul_undef_op1_defaultfp( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fmul.f32(float %x, float undef, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fmul_poison_op1_strict(float %x) { +; CHECK-LABEL: @fmul_poison_op1_strict( +; CHECK-NEXT: [[R:%.*]] = call float @llvm.experimental.constrained.fmul.f32(float [[X:%.*]], float poison, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[R]] +; + %r = call float @llvm.experimental.constrained.fmul.f32(float %x, float poison, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 + ret float %r +} + +define float @fmul_poison_op1_maytrap(float %x) { +; CHECK-LABEL: @fmul_poison_op1_maytrap( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fmul.f32(float %x, float poison, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + ret float %r +} + +define float @fmul_poison_op1_upward(float %x) { +; CHECK-LABEL: @fmul_poison_op1_upward( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fmul.f32(float %x, float poison, metadata !"round.upward", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fmul_poison_op1_defaultfp(float %x) { +; CHECK-LABEL: @fmul_poison_op1_defaultfp( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fmul.f32(float %x, float poison, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret float %r +} + +; +; constrained fdiv +; + +define float @fdiv_undef_op0_strict(float %x) { +; CHECK-LABEL: @fdiv_undef_op0_strict( +; CHECK-NEXT: [[R:%.*]] = call float @llvm.experimental.constrained.fdiv.f32(float undef, float [[X:%.*]], metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[R]] +; + %r = call float @llvm.experimental.constrained.fdiv.f32(float undef, float %x, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 + ret float %r +} + +define float @fdiv_undef_op0_maytrap(float %x) { +; CHECK-LABEL: @fdiv_undef_op0_maytrap( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fdiv.f32(float undef, float %x, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + ret float %r +} + +define float @fdiv_undef_op0_upward(float %x) { +; CHECK-LABEL: @fdiv_undef_op0_upward( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fdiv.f32(float undef, float %x, metadata !"round.upward", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fdiv_undef_op0_defaultfp(float %x) { +; CHECK-LABEL: @fdiv_undef_op0_defaultfp( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fdiv.f32(float undef, float %x, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fdiv_poison_op0_strict(float %x) { +; CHECK-LABEL: @fdiv_poison_op0_strict( +; CHECK-NEXT: [[R:%.*]] = call float @llvm.experimental.constrained.fdiv.f32(float poison, float [[X:%.*]], metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[R]] +; + %r = call float @llvm.experimental.constrained.fdiv.f32(float poison, float %x, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 + ret float %r +} + +define float @fdiv_poison_op0_maytrap(float %x) { +; CHECK-LABEL: @fdiv_poison_op0_maytrap( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fdiv.f32(float poison, float %x, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + ret float %r +} + +define float @fdiv_poison_op0_upward(float %x) { +; CHECK-LABEL: @fdiv_poison_op0_upward( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fdiv.f32(float poison, float %x, metadata !"round.upward", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fdiv_poison_op0_defaultfp(float %x) { +; CHECK-LABEL: @fdiv_poison_op0_defaultfp( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fdiv.f32(float poison, float %x, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fdiv_undef_op1_strict(float %x) { +; CHECK-LABEL: @fdiv_undef_op1_strict( +; CHECK-NEXT: [[R:%.*]] = call float @llvm.experimental.constrained.fdiv.f32(float [[X:%.*]], float undef, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[R]] +; + %r = call float @llvm.experimental.constrained.fdiv.f32(float %x, float undef, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 + ret float %r +} + +define float @fdiv_undef_op1_maytrap(float %x) { +; CHECK-LABEL: @fdiv_undef_op1_maytrap( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fdiv.f32(float %x, float undef, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + ret float %r +} + +define float @fdiv_undef_op1_upward(float %x) { +; CHECK-LABEL: @fdiv_undef_op1_upward( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fdiv.f32(float %x, float undef, metadata !"round.upward", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fdiv_undef_op1_defaultfp(float %x) { +; CHECK-LABEL: @fdiv_undef_op1_defaultfp( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fdiv.f32(float %x, float undef, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fdiv_poison_op1_strict(float %x) { +; CHECK-LABEL: @fdiv_poison_op1_strict( +; CHECK-NEXT: [[R:%.*]] = call float @llvm.experimental.constrained.fdiv.f32(float [[X:%.*]], float poison, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[R]] +; + %r = call float @llvm.experimental.constrained.fdiv.f32(float %x, float poison, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 + ret float %r +} + +define float @fdiv_poison_op1_maytrap(float %x) { +; CHECK-LABEL: @fdiv_poison_op1_maytrap( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fdiv.f32(float %x, float poison, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + ret float %r +} + +define float @fdiv_poison_op1_upward(float %x) { +; CHECK-LABEL: @fdiv_poison_op1_upward( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fdiv.f32(float %x, float poison, metadata !"round.upward", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fdiv_poison_op1_defaultfp(float %x) { +; CHECK-LABEL: @fdiv_poison_op1_defaultfp( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fdiv.f32(float %x, float poison, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret float %r +} + +; +; constrained frem +; + +define float @frem_undef_op0_strict(float %x) { +; CHECK-LABEL: @frem_undef_op0_strict( +; CHECK-NEXT: [[R:%.*]] = call float @llvm.experimental.constrained.frem.f32(float undef, float [[X:%.*]], metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[R]] +; + %r = call float @llvm.experimental.constrained.frem.f32(float undef, float %x, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 + ret float %r +} + +define float @frem_undef_op0_maytrap(float %x) { +; CHECK-LABEL: @frem_undef_op0_maytrap( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.frem.f32(float undef, float %x, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + ret float %r +} + +define float @frem_undef_op0_upward(float %x) { +; CHECK-LABEL: @frem_undef_op0_upward( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.frem.f32(float undef, float %x, metadata !"round.upward", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @frem_undef_op0_defaultfp(float %x) { +; CHECK-LABEL: @frem_undef_op0_defaultfp( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.frem.f32(float undef, float %x, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @frem_poison_op0_strict(float %x) { +; CHECK-LABEL: @frem_poison_op0_strict( +; CHECK-NEXT: [[R:%.*]] = call float @llvm.experimental.constrained.frem.f32(float poison, float [[X:%.*]], metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[R]] +; + %r = call float @llvm.experimental.constrained.frem.f32(float poison, float %x, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 + ret float %r +} + +define float @frem_poison_op0_maytrap(float %x) { +; CHECK-LABEL: @frem_poison_op0_maytrap( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.frem.f32(float poison, float %x, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + ret float %r +} + +define float @frem_poison_op0_upward(float %x) { +; CHECK-LABEL: @frem_poison_op0_upward( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.frem.f32(float poison, float %x, metadata !"round.upward", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @frem_poison_op0_defaultfp(float %x) { +; CHECK-LABEL: @frem_poison_op0_defaultfp( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.frem.f32(float poison, float %x, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @frem_undef_op1_strict(float %x) { +; CHECK-LABEL: @frem_undef_op1_strict( +; CHECK-NEXT: [[R:%.*]] = call float @llvm.experimental.constrained.frem.f32(float [[X:%.*]], float undef, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[R]] +; + %r = call float @llvm.experimental.constrained.frem.f32(float %x, float undef, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 + ret float %r +} + +define float @frem_undef_op1_maytrap(float %x) { +; CHECK-LABEL: @frem_undef_op1_maytrap( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.frem.f32(float %x, float undef, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + ret float %r +} + +define float @frem_undef_op1_upward(float %x) { +; CHECK-LABEL: @frem_undef_op1_upward( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.frem.f32(float %x, float undef, metadata !"round.upward", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @frem_undef_op1_defaultfp(float %x) { +; CHECK-LABEL: @frem_undef_op1_defaultfp( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.frem.f32(float %x, float undef, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @frem_poison_op1_strict(float %x) { +; CHECK-LABEL: @frem_poison_op1_strict( +; CHECK-NEXT: [[R:%.*]] = call float @llvm.experimental.constrained.frem.f32(float [[X:%.*]], float poison, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[R]] +; + %r = call float @llvm.experimental.constrained.frem.f32(float %x, float poison, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 + ret float %r +} + +define float @frem_poison_op1_maytrap(float %x) { +; CHECK-LABEL: @frem_poison_op1_maytrap( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.frem.f32(float %x, float poison, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + ret float %r +} + +define float @frem_poison_op1_upward(float %x) { +; CHECK-LABEL: @frem_poison_op1_upward( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.frem.f32(float %x, float poison, metadata !"round.upward", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @frem_poison_op1_defaultfp(float %x) { +; CHECK-LABEL: @frem_poison_op1_defaultfp( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.frem.f32(float %x, float poison, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret float %r +} + +; +; constrained fma +; + +define float @fma_undef_op0_strict(float %x, float %y) { +; CHECK-LABEL: @fma_undef_op0_strict( +; CHECK-NEXT: [[R:%.*]] = call float @llvm.experimental.constrained.fma.f32(float undef, float [[X:%.*]], float [[Y:%.*]], metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[R]] +; + %r = call float @llvm.experimental.constrained.fma.f32(float undef, float %x, float %y, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 + ret float %r +} + +define float @fma_undef_op0_maytrap(float %x, float %y) { +; CHECK-LABEL: @fma_undef_op0_maytrap( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fma.f32(float undef, float %x, float %y, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + ret float %r +} + +define float @fma_undef_op0_upward(float %x, float %y) { +; CHECK-LABEL: @fma_undef_op0_upward( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fma.f32(float undef, float %x, float %y, metadata !"round.upward", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fma_undef_op0_defaultfp(float %x, float %y) { +; CHECK-LABEL: @fma_undef_op0_defaultfp( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fma.f32(float undef, float %x, float %y, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fma_poison_op0_strict(float %x, float %y) { +; CHECK-LABEL: @fma_poison_op0_strict( +; CHECK-NEXT: [[R:%.*]] = call float @llvm.experimental.constrained.fma.f32(float poison, float [[X:%.*]], float [[Y:%.*]], metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[R]] +; + %r = call float @llvm.experimental.constrained.fma.f32(float poison, float %x, float %y, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 + ret float %r +} + +define float @fma_poison_op0_maytrap(float %x, float %y) { +; CHECK-LABEL: @fma_poison_op0_maytrap( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fma.f32(float poison, float %x, float %y, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + ret float %r +} + +define float @fma_poison_op0_upward(float %x, float %y) { +; CHECK-LABEL: @fma_poison_op0_upward( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fma.f32(float poison, float %x, float %y, metadata !"round.upward", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fma_poison_op0_defaultfp(float %x, float %y) { +; CHECK-LABEL: @fma_poison_op0_defaultfp( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fma.f32(float poison, float %x, float %y, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fma_undef_op1_strict(float %x, float %y) { +; CHECK-LABEL: @fma_undef_op1_strict( +; CHECK-NEXT: [[R:%.*]] = call float @llvm.experimental.constrained.fma.f32(float [[X:%.*]], float undef, float [[Y:%.*]], metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[R]] +; + %r = call float @llvm.experimental.constrained.fma.f32(float %x, float undef, float %y, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 + ret float %r +} + +define float @fma_undef_op1_maytrap(float %x, float %y) { +; CHECK-LABEL: @fma_undef_op1_maytrap( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fma.f32(float %x, float undef, float %y, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + ret float %r +} + +define float @fma_undef_op1_upward(float %x, float %y) { +; CHECK-LABEL: @fma_undef_op1_upward( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fma.f32(float %x, float undef, float %y, metadata !"round.upward", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fma_undef_op1_defaultfp(float %x, float %y) { +; CHECK-LABEL: @fma_undef_op1_defaultfp( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fma.f32(float %x, float undef, float %y, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fma_poison_op1_strict(float %x, float %y) { +; CHECK-LABEL: @fma_poison_op1_strict( +; CHECK-NEXT: [[R:%.*]] = call float @llvm.experimental.constrained.fma.f32(float [[X:%.*]], float poison, float [[Y:%.*]], metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[R]] +; + %r = call float @llvm.experimental.constrained.fma.f32(float %x, float poison, float %y, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 + ret float %r +} + +define float @fma_poison_op1_maytrap(float %x, float %y) { +; CHECK-LABEL: @fma_poison_op1_maytrap( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fma.f32(float %x, float poison, float %y, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + ret float %r +} + +define float @fma_poison_op1_upward(float %x, float %y) { +; CHECK-LABEL: @fma_poison_op1_upward( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fma.f32(float %x, float poison, float %y, metadata !"round.upward", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fma_poison_op1_defaultfp(float %x, float %y) { +; CHECK-LABEL: @fma_poison_op1_defaultfp( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fma.f32(float %x, float poison, float %y, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fma_undef_op2_strict(float %x, float %y) { +; CHECK-LABEL: @fma_undef_op2_strict( +; CHECK-NEXT: [[R:%.*]] = call float @llvm.experimental.constrained.fma.f32(float [[X:%.*]], float [[Y:%.*]], float undef, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[R]] +; + %r = call float @llvm.experimental.constrained.fma.f32(float %x, float %y, float undef, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 + ret float %r +} + +define float @fma_undef_op2_maytrap(float %x, float %y) { +; CHECK-LABEL: @fma_undef_op2_maytrap( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fma.f32(float %x, float %y, float undef, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + ret float %r +} + +define float @fma_undef_op2_upward(float %x, float %y) { +; CHECK-LABEL: @fma_undef_op2_upward( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fma.f32(float %x, float %y, float undef, metadata !"round.upward", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fma_undef_op2_defaultfp(float %x, float %y) { +; CHECK-LABEL: @fma_undef_op2_defaultfp( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fma.f32(float %x, float %y, float undef, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fma_poison_op2_strict(float %x, float %y) { +; CHECK-LABEL: @fma_poison_op2_strict( +; CHECK-NEXT: [[R:%.*]] = call float @llvm.experimental.constrained.fma.f32(float [[X:%.*]], float [[Y:%.*]], float poison, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[R]] +; + %r = call float @llvm.experimental.constrained.fma.f32(float %x, float %y, float poison, metadata !"round.dynamic", metadata !"fpexcept.strict") #0 + ret float %r +} + +define float @fma_poison_op2_maytrap(float %x, float %y) { +; CHECK-LABEL: @fma_poison_op2_maytrap( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fma.f32(float %x, float %y, float poison, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + ret float %r +} + +define float @fma_poison_op2_upward(float %x, float %y) { +; CHECK-LABEL: @fma_poison_op2_upward( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fma.f32(float %x, float %y, float poison, metadata !"round.upward", metadata !"fpexcept.ignore") #0 + ret float %r +} + +define float @fma_poison_op2_defaultfp(float %x, float %y) { +; CHECK-LABEL: @fma_poison_op2_defaultfp( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %r = call float @llvm.experimental.constrained.fma.f32(float %x, float %y, float poison, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret float %r +} + +declare float @llvm.experimental.constrained.fadd.f32(float, float, metadata, metadata) #0 +declare float @llvm.experimental.constrained.fsub.f32(float, float, metadata, metadata) #0 +declare float @llvm.experimental.constrained.fmul.f32(float, float, metadata, metadata) #0 +declare float @llvm.experimental.constrained.fdiv.f32(float, float, metadata, metadata) #0 +declare float @llvm.experimental.constrained.frem.f32(float, float, metadata, metadata) #0 +declare float @llvm.experimental.constrained.fma.f32(float, float, float, metadata, metadata) #0 + +attributes #0 = { strictfp }