Floating point builtins
---------------------------------------
+``__builtin_isfpclass``
+-----------------------
+
+``__builtin_isfpclass`` is used to test if the specified floating-point value
+falls into one of the specified floating-point classes.
+
+**Syntax**:
+
+.. code-block:: c++
+
+ int __builtin_isfpclass(fp_type expr, int mask)
+
+``fp_type`` is a floating-point type supported by the target. ``mask`` is an
+integer constant expression, where each bit represents floating-point class to
+test. The function returns boolean value.
+
+**Example of use**:
+
+.. code-block:: c++
+
+ if (__builtin_isfpclass(x, 448)) {
+ // `x` is positive finite value
+ ...
+ }
+
+**Description**:
+
+The ``__builtin_isfpclass()`` builtin is a generalization of functions ``isnan``,
+``isinf``, ``isfinite`` and some others defined by the C standard. It tests if
+the floating-point value, specified by the first argument, falls into any of data
+classes, specified by the second argument. The later is a bitmask, in which each
+data class is represented by a bit using the encoding:
+
+========== =================== ======================
+Mask value Data class Macro
+========== =================== ======================
+0x0001 Signaling NaN __FPCLASS_SNAN
+0x0002 Quiet NaN __FPCLASS_QNAN
+0x0004 Negative infinity __FPCLASS_NEGINF
+0x0008 Negative normal __FPCLASS_NEGNORMAL
+0x0010 Negative subnormal __FPCLASS_NEGSUBNORMAL
+0x0020 Negative zero __FPCLASS_NEGZERO
+0x0040 Positive zero __FPCLASS_POSZERO
+0x0080 Positive subnormal __FPCLASS_POSSUBNORMAL
+0x0100 Positive normal __FPCLASS_POSNORMAL
+0x0200 Positive infinity __FPCLASS_POSINF
+
+For convenience preprocessor defines macros for these values. The function
+returns 1 if ``expr`` falls into one of the specified data classes, 0 otherwise.
+
+In the example above the mask value 448 (0x1C0) contains the bits selecting
+positive zero, positive subnormal and positive normal classes.
+``__builtin_isfpclass(x, 448)`` would return true only if ``x`` if of any of
+these data classes. Using suitable mask value, the function can implement any of
+the standard classification functions, for example, ``__builtin_isfpclass(x, 3)``
+is identical to ``isnan``,``__builtin_isfpclass(x, 504)`` - to ``isfinite``
+and so on.
+
+This function never raises floating-point exceptions and does not canonicalize
+its input. The floating-point argument is not promoted, its data class is
+determined based on its representation in its actual semantic type.
+
``__builtin_canonicalize``
--------------------------
This fixes (`#58951 <https://github.com/llvm/llvm-project/issues/58951>`_).
- Clang now supports the `NO_COLOR <https://no-color.org/>`_ environment
variable as a way to disable color diagnostics.
+- Clang now supports ``__builtin_isfpclass``, which checks if the specified
+ floating-point value falls into any of the specified data classes.
New Compiler Flags
------------------
BUILTIN(__builtin_isinf_sign, "i.", "FnctE")
BUILTIN(__builtin_isnan, "i.", "FnctE")
BUILTIN(__builtin_isnormal, "i.", "FnctE")
+BUILTIN(__builtin_isfpclass, "i.", "nctE")
// FP signbit builtins
BUILTIN(__builtin_signbit, "i.", "Fnct")
Success(Val.isNormal() ? 1 : 0, E);
}
+ case Builtin::BI__builtin_isfpclass: {
+ APSInt MaskVal;
+ if (!EvaluateInteger(E->getArg(1), MaskVal, Info))
+ return false;
+ unsigned Test = static_cast<llvm::FPClassTest>(MaskVal.getZExtValue());
+ APFloat Val(0.0);
+ return EvaluateFloat(E->getArg(0), Val, Info) &&
+ Success((Val.classify() & Test) ? 1 : 0, E);
+ }
+
case Builtin::BI__builtin_parity:
case Builtin::BI__builtin_parityl:
case Builtin::BI__builtin_parityll: {
return RValue::get(V);
}
+ case Builtin::BI__builtin_isfpclass: {
+ Expr::EvalResult Result;
+ if (!E->getArg(1)->EvaluateAsInt(Result, CGM.getContext()))
+ break;
+ uint64_t Test = Result.Val.getInt().getLimitedValue();
+ CodeGenFunction::CGFPOptionsRAII FPOptsRAII(*this, E);
+ Value *V = EmitScalarExpr(E->getArg(0));
+ return RValue::get(Builder.CreateZExt(Builder.createIsFPClass(V, Test),
+ ConvertType(E->getType())));
+ }
+
case Builtin::BI__builtin_nondeterministic_value: {
llvm::Type *Ty = ConvertType(E->getArg(0)->getType());
Builder.defineMacro("__OPENCL_MEMORY_SCOPE_ALL_SVM_DEVICES", "3");
Builder.defineMacro("__OPENCL_MEMORY_SCOPE_SUB_GROUP", "4");
+ // Define macros for floating-point data classes, used in __builtin_isfpclass.
+ Builder.defineMacro("__FPCLASS_SNAN", "0x0001");
+ Builder.defineMacro("__FPCLASS_QNAN", "0x0002");
+ Builder.defineMacro("__FPCLASS_NEGINF", "0x0004");
+ Builder.defineMacro("__FPCLASS_NEGNORMAL", "0x0008");
+ Builder.defineMacro("__FPCLASS_NEGSUBNORMAL", "0x0010");
+ Builder.defineMacro("__FPCLASS_NEGZERO", "0x0020");
+ Builder.defineMacro("__FPCLASS_POSZERO", "0x0040");
+ Builder.defineMacro("__FPCLASS_POSSUBNORMAL", "0x0080");
+ Builder.defineMacro("__FPCLASS_POSNORMAL", "0x0100");
+ Builder.defineMacro("__FPCLASS_POSINF", "0x0200");
+
// Support for #pragma redefine_extname (Sun compatibility)
Builder.defineMacro("__PRAGMA_REDEFINE_EXTNAME", "1");
if (SemaBuiltinFPClassification(TheCall, 6))
return ExprError();
break;
+ case Builtin::BI__builtin_isfpclass:
+ if (SemaBuiltinFPClassification(TheCall, 2))
+ return ExprError();
+ break;
case Builtin::BI__builtin_isfinite:
case Builtin::BI__builtin_isinf:
case Builtin::BI__builtin_isinf_sign:
if (checkArgCount(*this, TheCall, NumArgs))
return true;
- // __builtin_fpclassify is the only case where NumArgs != 1, so we can count
- // on all preceding parameters just being int. Try all of those.
- for (unsigned i = 0; i < NumArgs - 1; ++i) {
+ // Find out position of floating-point argument.
+ unsigned FPArgNo = (NumArgs == 2) ? 0 : NumArgs - 1;
+
+ // We can count on all parameters preceding the floating-point just being int.
+ // Try all of those.
+ for (unsigned i = 0; i < FPArgNo; ++i) {
Expr *Arg = TheCall->getArg(i);
if (Arg->isTypeDependent())
TheCall->setArg(i, Res.get());
}
- Expr *OrigArg = TheCall->getArg(NumArgs-1);
+ Expr *OrigArg = TheCall->getArg(FPArgNo);
if (OrigArg->isTypeDependent())
return false;
OrigArg = UsualUnaryConversions(OrigArg).get();
else
OrigArg = DefaultFunctionArrayLvalueConversion(OrigArg).get();
- TheCall->setArg(NumArgs - 1, OrigArg);
+ TheCall->setArg(FPArgNo, OrigArg);
// This operation requires a non-_Complex floating-point number.
if (!OrigArg->getType()->isRealFloatingType())
diag::err_typecheck_call_invalid_unary_fp)
<< OrigArg->getType() << OrigArg->getSourceRange();
+ // __builtin_isfpclass has integer parameter that specify test mask. It is
+ // passed in (...), so it should be analyzed completely here.
+ if (NumArgs == 2)
+ if (SemaBuiltinConstantArgRange(TheCall, 1, 0, llvm::fcAllFlags))
+ return true;
+
return false;
}
P(isinf, (1.));
P(isinf_sign, (1.));
P(isnan, (1.));
+ P(isfpclass, (1., 1));
// Bitwise & Numeric Functions
--- /dev/null
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 2
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -target-feature +avx512fp16 -S -O1 -emit-llvm %s -o - | FileCheck %s
+
+// CHECK-LABEL: define dso_local i1 @check_isfpclass_finite
+// CHECK-SAME: (float noundef [[X:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[TMP0:%.*]] = tail call i1 @llvm.is.fpclass.f32(float [[X]], i32 504)
+// CHECK-NEXT: ret i1 [[TMP0]]
+//
+_Bool check_isfpclass_finite(float x) {
+ return __builtin_isfpclass(x, 504 /*Finite*/);
+}
+
+// CHECK-LABEL: define dso_local i1 @check_isfpclass_finite_strict
+// CHECK-SAME: (float noundef [[X:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[TMP0:%.*]] = tail call i1 @llvm.is.fpclass.f32(float [[X]], i32 504) #[[ATTR4:[0-9]+]]
+// CHECK-NEXT: ret i1 [[TMP0]]
+//
+_Bool check_isfpclass_finite_strict(float x) {
+#pragma STDC FENV_ACCESS ON
+ return __builtin_isfpclass(x, 504 /*Finite*/);
+}
+
+// CHECK-LABEL: define dso_local i1 @check_isfpclass_nan_f32
+// CHECK-SAME: (float noundef [[X:%.*]]) local_unnamed_addr #[[ATTR3:[0-9]+]] {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[TMP0:%.*]] = fcmp uno float [[X]], 0.000000e+00
+// CHECK-NEXT: ret i1 [[TMP0]]
+//
+_Bool check_isfpclass_nan_f32(float x) {
+ return __builtin_isfpclass(x, 3 /*NaN*/);
+}
+
+// CHECK-LABEL: define dso_local i1 @check_isfpclass_nan_f32_strict
+// CHECK-SAME: (float noundef [[X:%.*]]) local_unnamed_addr #[[ATTR2]] {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[TMP0:%.*]] = tail call i1 @llvm.is.fpclass.f32(float [[X]], i32 3) #[[ATTR4]]
+// CHECK-NEXT: ret i1 [[TMP0]]
+//
+_Bool check_isfpclass_nan_f32_strict(float x) {
+#pragma STDC FENV_ACCESS ON
+ return __builtin_isfpclass(x, 3 /*NaN*/);
+}
+
+// CHECK-LABEL: define dso_local i1 @check_isfpclass_snan_f64
+// CHECK-SAME: (double noundef [[X:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[TMP0:%.*]] = tail call i1 @llvm.is.fpclass.f64(double [[X]], i32 1)
+// CHECK-NEXT: ret i1 [[TMP0]]
+//
+_Bool check_isfpclass_snan_f64(double x) {
+ return __builtin_isfpclass(x, 1 /*SNaN*/);
+}
+
+// CHECK-LABEL: define dso_local i1 @check_isfpclass_snan_f64_strict
+// CHECK-SAME: (double noundef [[X:%.*]]) local_unnamed_addr #[[ATTR2]] {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[TMP0:%.*]] = tail call i1 @llvm.is.fpclass.f64(double [[X]], i32 1) #[[ATTR4]]
+// CHECK-NEXT: ret i1 [[TMP0]]
+//
+_Bool check_isfpclass_snan_f64_strict(double x) {
+#pragma STDC FENV_ACCESS ON
+ return __builtin_isfpclass(x, 1 /*NaN*/);
+}
+
+// CHECK-LABEL: define dso_local i1 @check_isfpclass_zero_f16
+// CHECK-SAME: (half noundef [[X:%.*]]) local_unnamed_addr #[[ATTR3]] {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[TMP0:%.*]] = fcmp oeq half [[X]], 0xH0000
+// CHECK-NEXT: ret i1 [[TMP0]]
+//
+_Bool check_isfpclass_zero_f16(_Float16 x) {
+ return __builtin_isfpclass(x, 96 /*Zero*/);
+}
+
+// CHECK-LABEL: define dso_local i1 @check_isfpclass_zero_f16_strict
+// CHECK-SAME: (half noundef [[X:%.*]]) local_unnamed_addr #[[ATTR2]] {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[TMP0:%.*]] = tail call i1 @llvm.is.fpclass.f16(half [[X]], i32 96) #[[ATTR4]]
+// CHECK-NEXT: ret i1 [[TMP0]]
+//
+_Bool check_isfpclass_zero_f16_strict(_Float16 x) {
+#pragma STDC FENV_ACCESS ON
+ return __builtin_isfpclass(x, 96 /*Zero*/);
+}
// AARCH64-NEXT: #define __FLT_MIN_EXP__ (-125)
// AARCH64-NEXT: #define __FLT_MIN__ 1.17549435e-38F
// AARCH64-NEXT: #define __FLT_RADIX__ 2
+// AARCH64-NEXT: #define __FPCLASS_NEGINF 0x0004
+// AARCH64-NEXT: #define __FPCLASS_NEGNORMAL 0x0008
+// AARCH64-NEXT: #define __FPCLASS_NEGSUBNORMAL 0x0010
+// AARCH64-NEXT: #define __FPCLASS_NEGZERO 0x0020
+// AARCH64-NEXT: #define __FPCLASS_POSINF 0x0200
+// AARCH64-NEXT: #define __FPCLASS_POSNORMAL 0x0100
+// AARCH64-NEXT: #define __FPCLASS_POSSUBNORMAL 0x0080
+// AARCH64-NEXT: #define __FPCLASS_POSZERO 0x0040
+// AARCH64-NEXT: #define __FPCLASS_QNAN 0x0002
+// AARCH64-NEXT: #define __FPCLASS_SNAN 0x0001
// AARCH64-NEXT: #define __FP_FAST_FMA 1
// AARCH64-NEXT: #define __FP_FAST_FMAF 1
// AARCH64-NEXT: #define __GCC_ASM_FLAG_OUTPUTS__ 1
// WEBASSEMBLY-NEXT:#define __FLT_MIN_EXP__ (-125)
// WEBASSEMBLY-NEXT:#define __FLT_MIN__ 1.17549435e-38F
// WEBASSEMBLY-NEXT:#define __FLT_RADIX__ 2
+// WEBASSEMBLY-NEXT:#define __FPCLASS_NEGINF 0x0004
+// WEBASSEMBLY-NEXT:#define __FPCLASS_NEGNORMAL 0x0008
+// WEBASSEMBLY-NEXT:#define __FPCLASS_NEGSUBNORMAL 0x0010
+// WEBASSEMBLY-NEXT:#define __FPCLASS_NEGZERO 0x0020
+// WEBASSEMBLY-NEXT:#define __FPCLASS_POSINF 0x0200
+// WEBASSEMBLY-NEXT:#define __FPCLASS_POSNORMAL 0x0100
+// WEBASSEMBLY-NEXT:#define __FPCLASS_POSSUBNORMAL 0x0080
+// WEBASSEMBLY-NEXT:#define __FPCLASS_POSZERO 0x0040
+// WEBASSEMBLY-NEXT:#define __FPCLASS_QNAN 0x0002
+// WEBASSEMBLY-NEXT:#define __FPCLASS_SNAN 0x0001
// WEBASSEMBLY-NEXT:#define __GCC_ATOMIC_BOOL_LOCK_FREE 2
// WEBASSEMBLY-NEXT:#define __GCC_ATOMIC_CHAR16_T_LOCK_FREE 2
// WEBASSEMBLY-NEXT:#define __GCC_ATOMIC_CHAR32_T_LOCK_FREE 2
}
_Complex double builtin_complex_static_init = __builtin_complex(1.0, 2.0);
+
+int test_is_fpclass(float x, int mask) {
+ int x1 = __builtin_isfpclass(x, 1024); // expected-error {{argument value 1024 is outside the valid range [0, 1023]}}
+ int x2 = __builtin_isfpclass(3, 3); // expected-error{{floating point classification requires argument of floating point type (passed in 'int')}}
+ int x3 = __builtin_isfpclass(x, 3, x); // expected-error{{too many arguments to function call, expected 2, have 3}}
+ int x4 = __builtin_isfpclass(x); // expected-error{{too few arguments to function call, expected 2, have 1}}
+ int x5 = __builtin_isfpclass(x, mask); // expected-error{{argument to '__builtin_isfpclass' must be a constant integer}}
+ int x6 = __builtin_isfpclass(x, -1); // expected-error{{argument value -1 is outside the valid range [0, 1023]}}
+ float _Complex c = x;
+ int x7 = __builtin_isfpclass(c, 3); // expected-error{{floating point classification requires argument of floating point type (passed in '_Complex float')}}
+}
char isnormal_nan [!__builtin_isnormal(__builtin_nan("")) ? 1 : -1];
char isnormal_snan [!__builtin_isnormal(__builtin_nans("")) ? 1 : -1];
+char isfpclass_inf_pos_0[__builtin_isfpclass(__builtin_inf(), 0x0200) ? 1 : -1]; // fcPosInf
+char isfpclass_inf_pos_1[!__builtin_isfpclass(__builtin_inff(), 0x0004) ? 1 : -1]; // fcNegInf
+char isfpclass_inf_pos_2[__builtin_isfpclass(__builtin_infl(), 0x0207) ? 1 : -1]; // fcSNan|fcQNan|fcNegInf|fcPosInf
+char isfpclass_inf_pos_3[!__builtin_isfpclass(__builtin_inf(), 0x01F8) ? 1 : -1]; // fcFinite
+char isfpclass_pos_0 [__builtin_isfpclass(1.0, 0x0100) ? 1 : -1]; // fcPosNormal
+char isfpclass_pos_1 [!__builtin_isfpclass(1.0f, 0x0008) ? 1 : -1]; // fcNegNormal
+char isfpclass_pos_2 [__builtin_isfpclass(1.0L, 0x01F8) ? 1 : -1]; // fcFinite
+char isfpclass_pos_3 [!__builtin_isfpclass(1.0, 0x0003) ? 1 : -1]; // fcSNan|fcQNan
+char isfpclass_pdenorm_0[__builtin_isfpclass(1.0e-40f, 0x0080) ? 1 : -1]; // fcPosSubnormal
+char isfpclass_pdenorm_1[__builtin_isfpclass(1.0e-310, 0x01F8) ? 1 : -1]; // fcFinite
+char isfpclass_pdenorm_2[!__builtin_isfpclass(1.0e-40f, 0x003C) ? 1 : -1]; // fcNegative
+char isfpclass_pdenorm_3[!__builtin_isfpclass(1.0e-310, 0x0207) ? 1 : -1]; // ~fcFinite
+char isfpclass_pzero_0 [__builtin_isfpclass(0.0f, 0x0060) ? 1 : -1]; // fcZero
+char isfpclass_pzero_1 [__builtin_isfpclass(0.0, 0x01F8) ? 1 : -1]; // fcFinite
+char isfpclass_pzero_2 [!__builtin_isfpclass(0.0L, 0x0020) ? 1 : -1]; // fcNegZero
+char isfpclass_pzero_3 [!__builtin_isfpclass(0.0, 0x0003) ? 1 : -1]; // fcNan
+char isfpclass_nzero_0 [__builtin_isfpclass(-0.0f, 0x0060) ? 1 : -1]; // fcZero
+char isfpclass_nzero_1 [__builtin_isfpclass(-0.0, 0x01F8) ? 1 : -1]; // fcFinite
+char isfpclass_nzero_2 [!__builtin_isfpclass(-0.0L, 0x0040) ? 1 : -1]; // fcPosZero
+char isfpclass_nzero_3 [!__builtin_isfpclass(-0.0, 0x0003) ? 1 : -1]; // fcNan
+char isfpclass_ndenorm_0[__builtin_isfpclass(-1.0e-40f, 0x0010) ? 1 : -1]; // fcNegSubnormal
+char isfpclass_ndenorm_1[__builtin_isfpclass(-1.0e-310, 0x01F8) ? 1 : -1]; // fcFinite
+char isfpclass_ndenorm_2[!__builtin_isfpclass(-1.0e-40f, 0x03C0) ? 1 : -1]; // fcPositive
+char isfpclass_ndenorm_3[!__builtin_isfpclass(-1.0e-310, 0x0207) ? 1 : -1]; // ~fcFinite
+char isfpclass_neg_0 [__builtin_isfpclass(-1.0, 0x0008) ? 1 : -1]; // fcNegNormal
+char isfpclass_neg_1 [!__builtin_isfpclass(-1.0f, 0x00100) ? 1 : -1]; // fcPosNormal
+char isfpclass_neg_2 [__builtin_isfpclass(-1.0L, 0x01F8) ? 1 : -1]; // fcFinite
+char isfpclass_neg_3 [!__builtin_isfpclass(-1.0, 0x0003) ? 1 : -1]; // fcSNan|fcQNan
+char isfpclass_inf_neg_0[__builtin_isfpclass(-__builtin_inf(), 0x0004) ? 1 : -1]; // fcNegInf
+char isfpclass_inf_neg_1[!__builtin_isfpclass(-__builtin_inff(), 0x0200) ? 1 : -1]; // fcPosInf
+char isfpclass_inf_neg_2[__builtin_isfpclass(-__builtin_infl(), 0x0207) ? 1 : -1]; // ~fcFinite
+char isfpclass_inf_neg_3[!__builtin_isfpclass(-__builtin_inf(), 0x03C0) ? 1 : -1]; // fcPositive
+char isfpclass_qnan_0 [__builtin_isfpclass(__builtin_nan(""), 0x0002) ? 1 : -1]; // fcQNan
+char isfpclass_qnan_1 [!__builtin_isfpclass(__builtin_nanf(""), 0x0001) ? 1 : -1]; // fcSNan
+char isfpclass_qnan_2 [__builtin_isfpclass(__builtin_nanl(""), 0x0207) ? 1 : -1]; // ~fcFinite
+char isfpclass_qnan_3 [!__builtin_isfpclass(__builtin_nan(""), 0x01F8) ? 1 : -1]; // fcFinite
+char isfpclass_snan_0 [__builtin_isfpclass(__builtin_nansf(""), 0x0001) ? 1 : -1]; // fcSNan
+char isfpclass_snan_1 [!__builtin_isfpclass(__builtin_nans(""), 0x0002) ? 1 : -1]; // fcQNan
+char isfpclass_snan_2 [__builtin_isfpclass(__builtin_nansl(""), 0x0207) ? 1 : -1]; // ~fcFinite
+char isfpclass_snan_3 [!__builtin_isfpclass(__builtin_nans(""), 0x01F8) ? 1 : -1]; // fcFinite
+
//double g19 = __builtin_powi(2.0, 4);
//float g20 = __builtin_powif(2.0f, 4);
//long double g21 = __builtin_powil(2.0L, 4);
unsigned Index, unsigned FieldIndex,
MDNode *DbgInfo);
+ Value *createIsFPClass(Value *FPNum, unsigned Test);
+
private:
/// Helper function that creates an assume intrinsic call that
/// represents an alignment assumption on the provided pointer \p PtrValue
return Fn;
}
+Value *IRBuilderBase::createIsFPClass(Value *FPNum, unsigned Test) {
+ ConstantInt *TestV = getInt32(Test);
+ Module *M = BB->getParent()->getParent();
+ Function *FnIsFPClass =
+ Intrinsic::getDeclaration(M, Intrinsic::is_fpclass, {FPNum->getType()});
+ return CreateCall(FnIsFPClass, {FPNum, TestV});
+}
+
CallInst *IRBuilderBase::CreateAlignmentAssumptionHelper(const DataLayout &DL,
Value *PtrValue,
Value *AlignValue,