Author: Ulrich Weigand Date: 2020-01-10T14:33:10+01:00 New Revision: 76e9c2a9870e36415eb343d28942a42296f85597
URL: https://github.com/llvm/llvm-project/commit/76e9c2a9870e36415eb343d28942a42296f85597 DIFF: https://github.com/llvm/llvm-project/commit/76e9c2a9870e36415eb343d28942a42296f85597.diff LOG: [FPEnv] Generate constrained FP comparisons from clang Update the IRBuilder to generate constrained FP comparisons in CreateFCmp when IsFPConstrained is true, similar to the other places in the IRBuilder. Also, add a new CreateFCmpS to emit signaling FP comparisons, and use it in clang where comparisons are supposed to be signaling (currently, only when emitting code for the <, <=, >, >= operators). Note that there is currently no way to add fast-math flags to a constrained FP comparison, since this is implemented as an intrinsic call that returns a boolean type, and FMF are only allowed for calls returning a floating-point type. However, given the discussion around https://bugs.llvm.org/show_bug.cgi?id=42179, it seems that FCmp itself really shouldn't have any FMF either, so this is probably OK. Reviewed by: craig.topper Differential Revision: https://reviews.llvm.org/D71467 Added: clang/test/CodeGen/fpconstrained-cmp-double.c clang/test/CodeGen/fpconstrained-cmp-float.c Modified: clang/lib/CodeGen/CGExprScalar.cpp llvm/include/llvm/IR/IRBuilder.h Removed: ################################################################################ diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 4adaca8ae571..4338d1c3dcdf 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -798,17 +798,17 @@ class ScalarExprEmitter // Comparisons. Value *EmitCompare(const BinaryOperator *E, llvm::CmpInst::Predicate UICmpOpc, llvm::CmpInst::Predicate SICmpOpc, - llvm::CmpInst::Predicate FCmpOpc); -#define VISITCOMP(CODE, UI, SI, FP) \ + llvm::CmpInst::Predicate FCmpOpc, bool IsSignaling); +#define VISITCOMP(CODE, UI, SI, FP, SIG) \ Value *VisitBin##CODE(const BinaryOperator *E) { \ return EmitCompare(E, llvm::ICmpInst::UI, llvm::ICmpInst::SI, \ - llvm::FCmpInst::FP); } - VISITCOMP(LT, ICMP_ULT, ICMP_SLT, FCMP_OLT) - VISITCOMP(GT, ICMP_UGT, ICMP_SGT, FCMP_OGT) - VISITCOMP(LE, ICMP_ULE, ICMP_SLE, FCMP_OLE) - VISITCOMP(GE, ICMP_UGE, ICMP_SGE, FCMP_OGE) - VISITCOMP(EQ, ICMP_EQ , ICMP_EQ , FCMP_OEQ) - VISITCOMP(NE, ICMP_NE , ICMP_NE , FCMP_UNE) + llvm::FCmpInst::FP, SIG); } + VISITCOMP(LT, ICMP_ULT, ICMP_SLT, FCMP_OLT, true) + VISITCOMP(GT, ICMP_UGT, ICMP_SGT, FCMP_OGT, true) + VISITCOMP(LE, ICMP_ULE, ICMP_SLE, FCMP_OLE, true) + VISITCOMP(GE, ICMP_UGE, ICMP_SGE, FCMP_OGE, true) + VISITCOMP(EQ, ICMP_EQ , ICMP_EQ , FCMP_OEQ, false) + VISITCOMP(NE, ICMP_NE , ICMP_NE , FCMP_UNE, false) #undef VISITCOMP Value *VisitBinAssign (const BinaryOperator *E); @@ -3804,7 +3804,8 @@ static llvm::Intrinsic::ID GetIntrinsic(IntrinsicType IT, Value *ScalarExprEmitter::EmitCompare(const BinaryOperator *E, llvm::CmpInst::Predicate UICmpOpc, llvm::CmpInst::Predicate SICmpOpc, - llvm::CmpInst::Predicate FCmpOpc) { + llvm::CmpInst::Predicate FCmpOpc, + bool IsSignaling) { TestAndClearIgnoreResultAssign(); Value *Result; QualType LHSTy = E->getLHS()->getType(); @@ -3900,7 +3901,10 @@ Value *ScalarExprEmitter::EmitCompare(const BinaryOperator *E, if (BOInfo.isFixedPointBinOp()) { Result = EmitFixedPointBinOp(BOInfo); } else if (LHS->getType()->isFPOrFPVectorTy()) { - Result = Builder.CreateFCmp(FCmpOpc, LHS, RHS, "cmp"); + if (!IsSignaling) + Result = Builder.CreateFCmp(FCmpOpc, LHS, RHS, "cmp"); + else + Result = Builder.CreateFCmpS(FCmpOpc, LHS, RHS, "cmp"); } else if (LHSTy->hasSignedIntegerRepresentation()) { Result = Builder.CreateICmp(SICmpOpc, LHS, RHS, "cmp"); } else { @@ -3957,6 +3961,8 @@ Value *ScalarExprEmitter::EmitCompare(const BinaryOperator *E, Value *ResultR, *ResultI; if (CETy->isRealFloatingType()) { + // As complex comparisons can only be equality comparisons, they + // are never signaling comparisons. ResultR = Builder.CreateFCmp(FCmpOpc, LHS.first, RHS.first, "cmp.r"); ResultI = Builder.CreateFCmp(FCmpOpc, LHS.second, RHS.second, "cmp.i"); } else { diff --git a/clang/test/CodeGen/fpconstrained-cmp-double.c b/clang/test/CodeGen/fpconstrained-cmp-double.c new file mode 100644 index 000000000000..2819970a3fcf --- /dev/null +++ b/clang/test/CodeGen/fpconstrained-cmp-double.c @@ -0,0 +1,151 @@ +// RUN: %clang_cc1 -ffp-exception-behavior=ignore -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK -check-prefix=FCMP +// RUN: %clang_cc1 -ffp-exception-behavior=strict -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK -check-prefix=EXCEPT +// RUN: %clang_cc1 -ffp-exception-behavior=maytrap -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK -check-prefix=MAYTRAP +// RUN: %clang_cc1 -frounding-math -ffp-exception-behavior=ignore -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK -check-prefix=IGNORE +// RUN: %clang_cc1 -frounding-math -ffp-exception-behavior=strict -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK -check-prefix=EXCEPT +// RUN: %clang_cc1 -frounding-math -ffp-exception-behavior=maytrap -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK -check-prefix=MAYTRAP + +_Bool QuietEqual(double f1, double f2) { + // CHECK-LABEL: define {{.*}}i1 @QuietEqual(double %f1, double %f2) + + // FCMP: fcmp oeq double %{{.*}}, %{{.*}} + // IGNORE: call i1 @llvm.experimental.constrained.fcmp.f64(double %{{.*}}, double %{{.*}}, metadata !"oeq", metadata !"fpexcept.ignore") + // EXCEPT: call i1 @llvm.experimental.constrained.fcmp.f64(double %{{.*}}, double %{{.*}}, metadata !"oeq", metadata !"fpexcept.strict") + // MAYTRAP: call i1 @llvm.experimental.constrained.fcmp.f64(double %{{.*}}, double %{{.*}}, metadata !"oeq", metadata !"fpexcept.maytrap") + return f1 == f2; + + // CHECK: ret +} + +_Bool QuietNotEqual(double f1, double f2) { + // CHECK-LABEL: define {{.*}}i1 @QuietNotEqual(double %f1, double %f2) + + // FCMP: fcmp une double %{{.*}}, %{{.*}} + // IGNORE: call i1 @llvm.experimental.constrained.fcmp.f64(double %{{.*}}, double %{{.*}}, metadata !"une", metadata !"fpexcept.ignore") + // EXCEPT: call i1 @llvm.experimental.constrained.fcmp.f64(double %{{.*}}, double %{{.*}}, metadata !"une", metadata !"fpexcept.strict") + // MAYTRAP: call i1 @llvm.experimental.constrained.fcmp.f64(double %{{.*}}, double %{{.*}}, metadata !"une", metadata !"fpexcept.maytrap") + return f1 != f2; + + // CHECK: ret +} + +_Bool SignalingLess(double f1, double f2) { + // CHECK-LABEL: define {{.*}}i1 @SignalingLess(double %f1, double %f2) + + // FCMP: fcmp olt double %{{.*}}, %{{.*}} + // IGNORE: call i1 @llvm.experimental.constrained.fcmps.f64(double %{{.*}}, double %{{.*}}, metadata !"olt", metadata !"fpexcept.ignore") + // EXCEPT: call i1 @llvm.experimental.constrained.fcmps.f64(double %{{.*}}, double %{{.*}}, metadata !"olt", metadata !"fpexcept.strict") + // MAYTRAP: call i1 @llvm.experimental.constrained.fcmps.f64(double %{{.*}}, double %{{.*}}, metadata !"olt", metadata !"fpexcept.maytrap") + return f1 < f2; + + // CHECK: ret +} + +_Bool SignalingLessEqual(double f1, double f2) { + // CHECK-LABEL: define {{.*}}i1 @SignalingLessEqual(double %f1, double %f2) + + // FCMP: fcmp ole double %{{.*}}, %{{.*}} + // IGNORE: call i1 @llvm.experimental.constrained.fcmps.f64(double %{{.*}}, double %{{.*}}, metadata !"ole", metadata !"fpexcept.ignore") + // EXCEPT: call i1 @llvm.experimental.constrained.fcmps.f64(double %{{.*}}, double %{{.*}}, metadata !"ole", metadata !"fpexcept.strict") + // MAYTRAP: call i1 @llvm.experimental.constrained.fcmps.f64(double %{{.*}}, double %{{.*}}, metadata !"ole", metadata !"fpexcept.maytrap") + return f1 <= f2; + + // CHECK: ret +} + +_Bool SignalingGreater(double f1, double f2) { + // CHECK-LABEL: define {{.*}}i1 @SignalingGreater(double %f1, double %f2) + + // FCMP: fcmp ogt double %{{.*}}, %{{.*}} + // IGNORE: call i1 @llvm.experimental.constrained.fcmps.f64(double %{{.*}}, double %{{.*}}, metadata !"ogt", metadata !"fpexcept.ignore") + // EXCEPT: call i1 @llvm.experimental.constrained.fcmps.f64(double %{{.*}}, double %{{.*}}, metadata !"ogt", metadata !"fpexcept.strict") + // MAYTRAP: call i1 @llvm.experimental.constrained.fcmps.f64(double %{{.*}}, double %{{.*}}, metadata !"ogt", metadata !"fpexcept.maytrap") + return f1 > f2; + + // CHECK: ret +} + +_Bool SignalingGreaterEqual(double f1, double f2) { + // CHECK-LABEL: define {{.*}}i1 @SignalingGreaterEqual(double %f1, double %f2) + + // FCMP: fcmp oge double %{{.*}}, %{{.*}} + // IGNORE: call i1 @llvm.experimental.constrained.fcmps.f64(double %{{.*}}, double %{{.*}}, metadata !"oge", metadata !"fpexcept.ignore") + // EXCEPT: call i1 @llvm.experimental.constrained.fcmps.f64(double %{{.*}}, double %{{.*}}, metadata !"oge", metadata !"fpexcept.strict") + // MAYTRAP: call i1 @llvm.experimental.constrained.fcmps.f64(double %{{.*}}, double %{{.*}}, metadata !"oge", metadata !"fpexcept.maytrap") + return f1 >= f2; + + // CHECK: ret +} + +_Bool QuietLess(double f1, double f2) { + // CHECK-LABEL: define {{.*}}i1 @QuietLess(double %f1, double %f2) + + // FCMP: fcmp olt double %{{.*}}, %{{.*}} + // IGNORE: call i1 @llvm.experimental.constrained.fcmp.f64(double %{{.*}}, double %{{.*}}, metadata !"olt", metadata !"fpexcept.ignore") + // EXCEPT: call i1 @llvm.experimental.constrained.fcmp.f64(double %{{.*}}, double %{{.*}}, metadata !"olt", metadata !"fpexcept.strict") + // MAYTRAP: call i1 @llvm.experimental.constrained.fcmp.f64(double %{{.*}}, double %{{.*}}, metadata !"olt", metadata !"fpexcept.maytrap") + return __builtin_isless(f1, f2); + + // CHECK: ret +} + +_Bool QuietLessEqual(double f1, double f2) { + // CHECK-LABEL: define {{.*}}i1 @QuietLessEqual(double %f1, double %f2) + + // FCMP: fcmp ole double %{{.*}}, %{{.*}} + // IGNORE: call i1 @llvm.experimental.constrained.fcmp.f64(double %{{.*}}, double %{{.*}}, metadata !"ole", metadata !"fpexcept.ignore") + // EXCEPT: call i1 @llvm.experimental.constrained.fcmp.f64(double %{{.*}}, double %{{.*}}, metadata !"ole", metadata !"fpexcept.strict") + // MAYTRAP: call i1 @llvm.experimental.constrained.fcmp.f64(double %{{.*}}, double %{{.*}}, metadata !"ole", metadata !"fpexcept.maytrap") + return __builtin_islessequal(f1, f2); + + // CHECK: ret +} + +_Bool QuietGreater(double f1, double f2) { + // CHECK-LABEL: define {{.*}}i1 @QuietGreater(double %f1, double %f2) + + // FCMP: fcmp ogt double %{{.*}}, %{{.*}} + // IGNORE: call i1 @llvm.experimental.constrained.fcmp.f64(double %{{.*}}, double %{{.*}}, metadata !"ogt", metadata !"fpexcept.ignore") + // EXCEPT: call i1 @llvm.experimental.constrained.fcmp.f64(double %{{.*}}, double %{{.*}}, metadata !"ogt", metadata !"fpexcept.strict") + // MAYTRAP: call i1 @llvm.experimental.constrained.fcmp.f64(double %{{.*}}, double %{{.*}}, metadata !"ogt", metadata !"fpexcept.maytrap") + return __builtin_isgreater(f1, f2); + + // CHECK: ret +} + +_Bool QuietGreaterEqual(double f1, double f2) { + // CHECK-LABEL: define {{.*}}i1 @QuietGreaterEqual(double %f1, double %f2) + + // FCMP: fcmp oge double %{{.*}}, %{{.*}} + // IGNORE: call i1 @llvm.experimental.constrained.fcmp.f64(double %{{.*}}, double %{{.*}}, metadata !"oge", metadata !"fpexcept.ignore") + // EXCEPT: call i1 @llvm.experimental.constrained.fcmp.f64(double %{{.*}}, double %{{.*}}, metadata !"oge", metadata !"fpexcept.strict") + // MAYTRAP: call i1 @llvm.experimental.constrained.fcmp.f64(double %{{.*}}, double %{{.*}}, metadata !"oge", metadata !"fpexcept.maytrap") + return __builtin_isgreaterequal(f1, f2); + + // CHECK: ret +} + +_Bool QuietLessGreater(double f1, double f2) { + // CHECK-LABEL: define {{.*}}i1 @QuietLessGreater(double %f1, double %f2) + + // FCMP: fcmp one double %{{.*}}, %{{.*}} + // IGNORE: call i1 @llvm.experimental.constrained.fcmp.f64(double %{{.*}}, double %{{.*}}, metadata !"one", metadata !"fpexcept.ignore") + // EXCEPT: call i1 @llvm.experimental.constrained.fcmp.f64(double %{{.*}}, double %{{.*}}, metadata !"one", metadata !"fpexcept.strict") + // MAYTRAP: call i1 @llvm.experimental.constrained.fcmp.f64(double %{{.*}}, double %{{.*}}, metadata !"one", metadata !"fpexcept.maytrap") + return __builtin_islessgreater(f1, f2); + + // CHECK: ret +} + +_Bool QuietUnordered(double f1, double f2) { + // CHECK-LABEL: define {{.*}}i1 @QuietUnordered(double %f1, double %f2) + + // FCMP: fcmp uno double %{{.*}}, %{{.*}} + // IGNORE: call i1 @llvm.experimental.constrained.fcmp.f64(double %{{.*}}, double %{{.*}}, metadata !"uno", metadata !"fpexcept.ignore") + // EXCEPT: call i1 @llvm.experimental.constrained.fcmp.f64(double %{{.*}}, double %{{.*}}, metadata !"uno", metadata !"fpexcept.strict") + // MAYTRAP: call i1 @llvm.experimental.constrained.fcmp.f64(double %{{.*}}, double %{{.*}}, metadata !"uno", metadata !"fpexcept.maytrap") + return __builtin_isunordered(f1, f2); + + // CHECK: ret +} + diff --git a/clang/test/CodeGen/fpconstrained-cmp-float.c b/clang/test/CodeGen/fpconstrained-cmp-float.c new file mode 100644 index 000000000000..0265fc54c02b --- /dev/null +++ b/clang/test/CodeGen/fpconstrained-cmp-float.c @@ -0,0 +1,151 @@ +// RUN: %clang_cc1 -ffp-exception-behavior=ignore -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK -check-prefix=FCMP +// RUN: %clang_cc1 -ffp-exception-behavior=strict -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK -check-prefix=EXCEPT +// RUN: %clang_cc1 -ffp-exception-behavior=maytrap -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK -check-prefix=MAYTRAP +// RUN: %clang_cc1 -frounding-math -ffp-exception-behavior=ignore -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK -check-prefix=IGNORE +// RUN: %clang_cc1 -frounding-math -ffp-exception-behavior=strict -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK -check-prefix=EXCEPT +// RUN: %clang_cc1 -frounding-math -ffp-exception-behavior=maytrap -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK -check-prefix=MAYTRAP + +_Bool QuietEqual(float f1, float f2) { + // CHECK-LABEL: define {{.*}}i1 @QuietEqual(float %f1, float %f2) + + // FCMP: fcmp oeq float %{{.*}}, %{{.*}} + // IGNORE: call i1 @llvm.experimental.constrained.fcmp.f32(float %{{.*}}, float %{{.*}}, metadata !"oeq", metadata !"fpexcept.ignore") + // EXCEPT: call i1 @llvm.experimental.constrained.fcmp.f32(float %{{.*}}, float %{{.*}}, metadata !"oeq", metadata !"fpexcept.strict") + // MAYTRAP: call i1 @llvm.experimental.constrained.fcmp.f32(float %{{.*}}, float %{{.*}}, metadata !"oeq", metadata !"fpexcept.maytrap") + return f1 == f2; + + // CHECK: ret +} + +_Bool QuietNotEqual(float f1, float f2) { + // CHECK-LABEL: define {{.*}}i1 @QuietNotEqual(float %f1, float %f2) + + // FCMP: fcmp une float %{{.*}}, %{{.*}} + // IGNORE: call i1 @llvm.experimental.constrained.fcmp.f32(float %{{.*}}, float %{{.*}}, metadata !"une", metadata !"fpexcept.ignore") + // EXCEPT: call i1 @llvm.experimental.constrained.fcmp.f32(float %{{.*}}, float %{{.*}}, metadata !"une", metadata !"fpexcept.strict") + // MAYTRAP: call i1 @llvm.experimental.constrained.fcmp.f32(float %{{.*}}, float %{{.*}}, metadata !"une", metadata !"fpexcept.maytrap") + return f1 != f2; + + // CHECK: ret +} + +_Bool SignalingLess(float f1, float f2) { + // CHECK-LABEL: define {{.*}}i1 @SignalingLess(float %f1, float %f2) + + // FCMP: fcmp olt float %{{.*}}, %{{.*}} + // IGNORE: call i1 @llvm.experimental.constrained.fcmps.f32(float %{{.*}}, float %{{.*}}, metadata !"olt", metadata !"fpexcept.ignore") + // EXCEPT: call i1 @llvm.experimental.constrained.fcmps.f32(float %{{.*}}, float %{{.*}}, metadata !"olt", metadata !"fpexcept.strict") + // MAYTRAP: call i1 @llvm.experimental.constrained.fcmps.f32(float %{{.*}}, float %{{.*}}, metadata !"olt", metadata !"fpexcept.maytrap") + return f1 < f2; + + // CHECK: ret +} + +_Bool SignalingLessEqual(float f1, float f2) { + // CHECK-LABEL: define {{.*}}i1 @SignalingLessEqual(float %f1, float %f2) + + // FCMP: fcmp ole float %{{.*}}, %{{.*}} + // IGNORE: call i1 @llvm.experimental.constrained.fcmps.f32(float %{{.*}}, float %{{.*}}, metadata !"ole", metadata !"fpexcept.ignore") + // EXCEPT: call i1 @llvm.experimental.constrained.fcmps.f32(float %{{.*}}, float %{{.*}}, metadata !"ole", metadata !"fpexcept.strict") + // MAYTRAP: call i1 @llvm.experimental.constrained.fcmps.f32(float %{{.*}}, float %{{.*}}, metadata !"ole", metadata !"fpexcept.maytrap") + return f1 <= f2; + + // CHECK: ret +} + +_Bool SignalingGreater(float f1, float f2) { + // CHECK-LABEL: define {{.*}}i1 @SignalingGreater(float %f1, float %f2) + + // FCMP: fcmp ogt float %{{.*}}, %{{.*}} + // IGNORE: call i1 @llvm.experimental.constrained.fcmps.f32(float %{{.*}}, float %{{.*}}, metadata !"ogt", metadata !"fpexcept.ignore") + // EXCEPT: call i1 @llvm.experimental.constrained.fcmps.f32(float %{{.*}}, float %{{.*}}, metadata !"ogt", metadata !"fpexcept.strict") + // MAYTRAP: call i1 @llvm.experimental.constrained.fcmps.f32(float %{{.*}}, float %{{.*}}, metadata !"ogt", metadata !"fpexcept.maytrap") + return f1 > f2; + + // CHECK: ret +} + +_Bool SignalingGreaterEqual(float f1, float f2) { + // CHECK-LABEL: define {{.*}}i1 @SignalingGreaterEqual(float %f1, float %f2) + + // FCMP: fcmp oge float %{{.*}}, %{{.*}} + // IGNORE: call i1 @llvm.experimental.constrained.fcmps.f32(float %{{.*}}, float %{{.*}}, metadata !"oge", metadata !"fpexcept.ignore") + // EXCEPT: call i1 @llvm.experimental.constrained.fcmps.f32(float %{{.*}}, float %{{.*}}, metadata !"oge", metadata !"fpexcept.strict") + // MAYTRAP: call i1 @llvm.experimental.constrained.fcmps.f32(float %{{.*}}, float %{{.*}}, metadata !"oge", metadata !"fpexcept.maytrap") + return f1 >= f2; + + // CHECK: ret +} + +_Bool QuietLess(float f1, float f2) { + // CHECK-LABEL: define {{.*}}i1 @QuietLess(float %f1, float %f2) + + // FCMP: fcmp olt float %{{.*}}, %{{.*}} + // IGNORE: call i1 @llvm.experimental.constrained.fcmp.f32(float %{{.*}}, float %{{.*}}, metadata !"olt", metadata !"fpexcept.ignore") + // EXCEPT: call i1 @llvm.experimental.constrained.fcmp.f32(float %{{.*}}, float %{{.*}}, metadata !"olt", metadata !"fpexcept.strict") + // MAYTRAP: call i1 @llvm.experimental.constrained.fcmp.f32(float %{{.*}}, float %{{.*}}, metadata !"olt", metadata !"fpexcept.maytrap") + return __builtin_isless(f1, f2); + + // CHECK: ret +} + +_Bool QuietLessEqual(float f1, float f2) { + // CHECK-LABEL: define {{.*}}i1 @QuietLessEqual(float %f1, float %f2) + + // FCMP: fcmp ole float %{{.*}}, %{{.*}} + // IGNORE: call i1 @llvm.experimental.constrained.fcmp.f32(float %{{.*}}, float %{{.*}}, metadata !"ole", metadata !"fpexcept.ignore") + // EXCEPT: call i1 @llvm.experimental.constrained.fcmp.f32(float %{{.*}}, float %{{.*}}, metadata !"ole", metadata !"fpexcept.strict") + // MAYTRAP: call i1 @llvm.experimental.constrained.fcmp.f32(float %{{.*}}, float %{{.*}}, metadata !"ole", metadata !"fpexcept.maytrap") + return __builtin_islessequal(f1, f2); + + // CHECK: ret +} + +_Bool QuietGreater(float f1, float f2) { + // CHECK-LABEL: define {{.*}}i1 @QuietGreater(float %f1, float %f2) + + // FCMP: fcmp ogt float %{{.*}}, %{{.*}} + // IGNORE: call i1 @llvm.experimental.constrained.fcmp.f32(float %{{.*}}, float %{{.*}}, metadata !"ogt", metadata !"fpexcept.ignore") + // EXCEPT: call i1 @llvm.experimental.constrained.fcmp.f32(float %{{.*}}, float %{{.*}}, metadata !"ogt", metadata !"fpexcept.strict") + // MAYTRAP: call i1 @llvm.experimental.constrained.fcmp.f32(float %{{.*}}, float %{{.*}}, metadata !"ogt", metadata !"fpexcept.maytrap") + return __builtin_isgreater(f1, f2); + + // CHECK: ret +} + +_Bool QuietGreaterEqual(float f1, float f2) { + // CHECK-LABEL: define {{.*}}i1 @QuietGreaterEqual(float %f1, float %f2) + + // FCMP: fcmp oge float %{{.*}}, %{{.*}} + // IGNORE: call i1 @llvm.experimental.constrained.fcmp.f32(float %{{.*}}, float %{{.*}}, metadata !"oge", metadata !"fpexcept.ignore") + // EXCEPT: call i1 @llvm.experimental.constrained.fcmp.f32(float %{{.*}}, float %{{.*}}, metadata !"oge", metadata !"fpexcept.strict") + // MAYTRAP: call i1 @llvm.experimental.constrained.fcmp.f32(float %{{.*}}, float %{{.*}}, metadata !"oge", metadata !"fpexcept.maytrap") + return __builtin_isgreaterequal(f1, f2); + + // CHECK: ret +} + +_Bool QuietLessGreater(float f1, float f2) { + // CHECK-LABEL: define {{.*}}i1 @QuietLessGreater(float %f1, float %f2) + + // FCMP: fcmp one float %{{.*}}, %{{.*}} + // IGNORE: call i1 @llvm.experimental.constrained.fcmp.f32(float %{{.*}}, float %{{.*}}, metadata !"one", metadata !"fpexcept.ignore") + // EXCEPT: call i1 @llvm.experimental.constrained.fcmp.f32(float %{{.*}}, float %{{.*}}, metadata !"one", metadata !"fpexcept.strict") + // MAYTRAP: call i1 @llvm.experimental.constrained.fcmp.f32(float %{{.*}}, float %{{.*}}, metadata !"one", metadata !"fpexcept.maytrap") + return __builtin_islessgreater(f1, f2); + + // CHECK: ret +} + +_Bool QuietUnordered(float f1, float f2) { + // CHECK-LABEL: define {{.*}}i1 @QuietUnordered(float %f1, float %f2) + + // FCMP: fcmp uno float %{{.*}}, %{{.*}} + // IGNORE: call i1 @llvm.experimental.constrained.fcmp.f32(float %{{.*}}, float %{{.*}}, metadata !"uno", metadata !"fpexcept.ignore") + // EXCEPT: call i1 @llvm.experimental.constrained.fcmp.f32(float %{{.*}}, float %{{.*}}, metadata !"uno", metadata !"fpexcept.strict") + // MAYTRAP: call i1 @llvm.experimental.constrained.fcmp.f32(float %{{.*}}, float %{{.*}}, metadata !"uno", metadata !"fpexcept.maytrap") + return __builtin_isunordered(f1, f2); + + // CHECK: ret +} + diff --git a/llvm/include/llvm/IR/IRBuilder.h b/llvm/include/llvm/IR/IRBuilder.h index 1abdf2242b58..90353365a964 100644 --- a/llvm/include/llvm/IR/IRBuilder.h +++ b/llvm/include/llvm/IR/IRBuilder.h @@ -1195,6 +1195,18 @@ class IRBuilder : public IRBuilderBase, public Inserter { return MetadataAsValue::get(Context, ExceptMDS); } + Value *getConstrainedFPPredicate(CmpInst::Predicate Predicate) { + assert(CmpInst::isFPPredicate(Predicate) && + Predicate != CmpInst::FCMP_FALSE && + Predicate != CmpInst::FCMP_TRUE && + "Invalid constrained FP comparison predicate!"); + + StringRef PredicateStr = CmpInst::getPredicateName(Predicate); + auto *PredicateMDS = MDString::get(Context, PredicateStr); + + return MetadataAsValue::get(Context, PredicateMDS); + } + public: Value *CreateAdd(Value *LHS, Value *RHS, const Twine &Name = "", bool HasNUW = false, bool HasNSW = false) { @@ -2351,12 +2363,41 @@ class IRBuilder : public IRBuilderBase, public Inserter { Value *CreateFCmp(CmpInst::Predicate P, Value *LHS, Value *RHS, const Twine &Name = "", MDNode *FPMathTag = nullptr) { + if (IsFPConstrained) + return CreateConstrainedFPCmp(Intrinsic::experimental_constrained_fcmp, + P, LHS, RHS, Name); + + if (auto *LC = dyn_cast<Constant>(LHS)) + if (auto *RC = dyn_cast<Constant>(RHS)) + return Insert(Folder.CreateFCmp(P, LC, RC), Name); + return Insert(setFPAttrs(new FCmpInst(P, LHS, RHS), FPMathTag, FMF), Name); + } + + Value *CreateFCmpS(CmpInst::Predicate P, Value *LHS, Value *RHS, + const Twine &Name = "", MDNode *FPMathTag = nullptr) { + if (IsFPConstrained) + return CreateConstrainedFPCmp(Intrinsic::experimental_constrained_fcmps, + P, LHS, RHS, Name); + if (auto *LC = dyn_cast<Constant>(LHS)) if (auto *RC = dyn_cast<Constant>(RHS)) return Insert(Folder.CreateFCmp(P, LC, RC), Name); return Insert(setFPAttrs(new FCmpInst(P, LHS, RHS), FPMathTag, FMF), Name); } + CallInst *CreateConstrainedFPCmp( + Intrinsic::ID ID, CmpInst::Predicate P, Value *L, Value *R, + const Twine &Name = "", + Optional<fp::ExceptionBehavior> Except = None) { + Value *PredicateV = getConstrainedFPPredicate(P); + Value *ExceptV = getConstrainedFPExcept(Except); + + CallInst *C = CreateIntrinsic(ID, {L->getType()}, + {L, R, PredicateV, ExceptV}, nullptr, Name); + setConstrainedFPCallAttr(C); + return C; + } + //===--------------------------------------------------------------------===// // Instruction creation methods: Other Instructions //===--------------------------------------------------------------------===// _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits