diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -3496,18 +3496,15 @@ ``__builtin_isfpclass`` ----------------------- -``__builtin_isfpclass`` is used to test if the specified floating-point value -falls into one of the specified floating-point classes. +``__builtin_isfpclass`` is used to test if the specified floating-point values +fall 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. + bool __builtin_isfpclass(fp_type expr, int mask) + boolean_vector __builtin_isfpclass(fp_vector expr, int mask) **Example of use**: @@ -3515,7 +3512,7 @@ if (__builtin_isfpclass(x, 448)) { // `x` is positive finite value - ... + ... } **Description**: @@ -3523,8 +3520,9 @@ 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: +classes, specified by the second argument. The latter is an integer constant +expression, that represents a bitmask, in which each data class is represented by +a bit using the encoding: ========== =================== ====================== Mask value Data class Macro @@ -3552,6 +3550,13 @@ is identical to ``isnan``,``__builtin_isfpclass(x, 504)`` - to ``isfinite`` and so on. +If the first argument is a vector, the function is equivalent to the set of +scalar calls of ``__builtin_isfpclass`` applied to the input elementwise. + +The result of ``__builtin_isfpclass`` is a boolean value, if the first argument +is a scalar, or a boolean vector with the same vector length as the first +argument. + 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. diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -7687,14 +7687,15 @@ /// SemaBuiltinSemaBuiltinFPClassification - Handle functions like /// __builtin_isnan and friends. This is declared to take (...), so we have -/// to check everything. We expect the last argument to be a floating point -/// value. +/// to check everything. bool Sema::SemaBuiltinFPClassification(CallExpr *TheCall, unsigned NumArgs) { if (checkArgCount(*this, TheCall, NumArgs)) return true; + bool IsFPClass = NumArgs == 2; + // Find out position of floating-point argument. - unsigned FPArgNo = (NumArgs == 2) ? 0 : NumArgs - 1; + unsigned FPArgNo = IsFPClass ? 0 : NumArgs - 1; // We can count on all parameters preceding the floating-point just being int. // Try all of those. @@ -7725,18 +7726,36 @@ OrigArg = DefaultFunctionArrayLvalueConversion(OrigArg).get(); TheCall->setArg(FPArgNo, OrigArg); + const VectorType *VectorArgTy = nullptr; + QualType ElementTy = OrigArg->getType(); + // TODO: When all classification function are implemented with is_fpclass, + // vector argument can be supported in all of them. + if (ElementTy->isVectorType() && IsFPClass) { + VectorArgTy = ElementTy->getAs(); + ElementTy = VectorArgTy->getElementType(); + } + // This operation requires a non-_Complex floating-point number. - if (!OrigArg->getType()->isRealFloatingType()) + if (!ElementTy->isRealFloatingType()) return Diag(OrigArg->getBeginLoc(), 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 (IsFPClass) if (SemaBuiltinConstantArgRange(TheCall, 1, 0, llvm::fcAllFlags)) return true; + // TODO: enable this code to all classification functions. + if (IsFPClass) { + QualType ResultTy = Context.BoolTy; + if (VectorArgTy) + ResultTy = Context.getVectorType(ResultTy, VectorArgTy->getNumElements(), + VectorType::GenericVector); + TheCall->setType(ResultTy); + } + return false; } diff --git a/clang/test/CodeGen/isfpclass.c b/clang/test/CodeGen/isfpclass.c --- a/clang/test/CodeGen/isfpclass.c +++ b/clang/test/CodeGen/isfpclass.c @@ -1,5 +1,5 @@ // 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 +// RUN: %clang_cc1 -triple aarch64-linux-gnu -target-feature -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]+]] { @@ -84,3 +84,27 @@ #pragma STDC FENV_ACCESS ON return __builtin_isfpclass(x, 96 /*Zero*/); } + +typedef float __attribute__((ext_vector_type(4))) float4; +typedef _Bool __attribute__((ext_vector_type(4))) bool4; + +// CHECK-LABEL: define dso_local <4 x i1> @check_isfpclass_nan_v4f32 +// CHECK-SAME: (<4 x float> noundef [[X:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = fcmp uno <4 x float> [[X]], zeroinitializer +// CHECK-NEXT: ret <4 x i1> [[TMP0]] +// +bool4 check_isfpclass_nan_v4f32(float4 x) { + return __builtin_isfpclass(x, 3 /*NaN*/); +} + +// CHECK-LABEL: define dso_local <4 x i1> @check_isfpclass_nan_strict_v4f32 +// CHECK-SAME: (<4 x float> noundef [[X:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = tail call <4 x i1> @llvm.is.fpclass.v4f32(<4 x float> [[X]], i32 3) #[[ATTR4]] +// CHECK-NEXT: ret <4 x i1> [[TMP0]] +// +bool4 check_isfpclass_nan_strict_v4f32(float4 x) { +#pragma STDC FENV_ACCESS ON + return __builtin_isfpclass(x, 3 /*NaN*/); +}