Skip to content

Commit

Permalink
[clang] Additional FP classification functions (#69041)
Browse files Browse the repository at this point in the history
C language standard defined library functions `iszero`, `issignaling`
and `issubnormal`, which did not have counterparts among clang builtin
functions. This change adds new functions:

    __builtin_iszero
    __builtin_issubnormal
    __builtin_issignaling

They provide builtin implementation for the missing standard functions.

Pull request: #69041
  • Loading branch information
spavloff committed Nov 1, 2023
1 parent 954af75 commit fc7198b
Show file tree
Hide file tree
Showing 10 changed files with 157 additions and 0 deletions.
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,8 @@ Floating Point Support in Clang
- Add ``__builtin_exp10``, ``__builtin_exp10f``,
``__builtin_exp10f16``, ``__builtin_exp10l`` and
``__builtin_exp10f128`` builtins.
- Add ``__builtin_iszero``, ``__builtin_issignaling`` and
``__builtin_issubnormal``.

AST Matchers
------------
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/Builtins.def
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,9 @@ BUILTIN(__builtin_isinf, "i.", "FnctE")
BUILTIN(__builtin_isinf_sign, "i.", "FnctE")
BUILTIN(__builtin_isnan, "i.", "FnctE")
BUILTIN(__builtin_isnormal, "i.", "FnctE")
BUILTIN(__builtin_issubnormal,"i.", "FnctE")
BUILTIN(__builtin_iszero, "i.", "FnctE")
BUILTIN(__builtin_issignaling,"i.", "FnctE")
BUILTIN(__builtin_isfpclass, "i.", "nctE")

// FP signbit builtins
Expand Down
18 changes: 18 additions & 0 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12398,6 +12398,24 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
Success(Val.isNormal() ? 1 : 0, E);
}

case Builtin::BI__builtin_issubnormal: {
APFloat Val(0.0);
return EvaluateFloat(E->getArg(0), Val, Info) &&
Success(Val.isDenormal() ? 1 : 0, E);
}

case Builtin::BI__builtin_iszero: {
APFloat Val(0.0);
return EvaluateFloat(E->getArg(0), Val, Info) &&
Success(Val.isZero() ? 1 : 0, E);
}

case Builtin::BI__builtin_issignaling: {
APFloat Val(0.0);
return EvaluateFloat(E->getArg(0), Val, Info) &&
Success(Val.isSignaling() ? 1 : 0, E);
}

case Builtin::BI__builtin_isfpclass: {
APSInt MaskVal;
if (!EvaluateInteger(E->getArg(1), MaskVal, Info))
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/Floating.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ class Floating final {
bool isMin() const { return F.isSmallest(); }
bool isMinusOne() const { return F.isExactlyValue(-1.0); }
bool isNan() const { return F.isNaN(); }
bool isSignaling() const { return F.isSignaling(); }
bool isInf() const { return F.isInfinity(); }
bool isFinite() const { return F.isFinite(); }
bool isNormal() const { return F.isNormal(); }
Expand Down
39 changes: 39 additions & 0 deletions clang/lib/AST/Interp/InterpBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,15 @@ static bool interp__builtin_isnan(InterpState &S, CodePtr OpPC,
return true;
}

static bool interp__builtin_issignaling(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *F) {
const Floating &Arg = S.Stk.peek<Floating>();

pushInt(S, Arg.isSignaling());
return true;
}

static bool interp__builtin_isinf(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame, const Function *F,
bool CheckSign) {
Expand Down Expand Up @@ -337,6 +346,24 @@ static bool interp__builtin_isnormal(InterpState &S, CodePtr OpPC,
return true;
}

static bool interp__builtin_issubnormal(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *F) {
const Floating &Arg = S.Stk.peek<Floating>();

pushInt(S, Arg.isDenormal());
return true;
}

static bool interp__builtin_iszero(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *F) {
const Floating &Arg = S.Stk.peek<Floating>();

pushInt(S, Arg.isZero());
return true;
}

/// First parameter to __builtin_isfpclass is the floating value, the
/// second one is an integral value.
static bool interp__builtin_isfpclass(InterpState &S, CodePtr OpPC,
Expand Down Expand Up @@ -491,6 +518,10 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
if (interp__builtin_isnan(S, OpPC, Frame, F))
return retInt(S, OpPC, Dummy);
break;
case Builtin::BI__builtin_issignaling:
if (interp__builtin_issignaling(S, OpPC, Frame, F))
return retInt(S, OpPC, Dummy);
break;

case Builtin::BI__builtin_isinf:
if (interp__builtin_isinf(S, OpPC, Frame, F, /*Sign=*/false))
Expand All @@ -510,6 +541,14 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
if (interp__builtin_isnormal(S, OpPC, Frame, F))
return retInt(S, OpPC, Dummy);
break;
case Builtin::BI__builtin_issubnormal:
if (interp__builtin_issubnormal(S, OpPC, Frame, F))
return retInt(S, OpPC, Dummy);
break;
case Builtin::BI__builtin_iszero:
if (interp__builtin_iszero(S, OpPC, Frame, F))
return retInt(S, OpPC, Dummy);
break;
case Builtin::BI__builtin_isfpclass:
if (interp__builtin_isfpclass(S, OpPC, Frame, F, Call))
return retInt(S, OpPC, Dummy);
Expand Down
24 changes: 24 additions & 0 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3356,6 +3356,14 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
ConvertType(E->getType())));
}

case Builtin::BI__builtin_issignaling: {
CodeGenFunction::CGFPOptionsRAII FPOptsRAII(*this, E);
Value *V = EmitScalarExpr(E->getArg(0));
return RValue::get(
Builder.CreateZExt(Builder.createIsFPClass(V, FPClassTest::fcSNan),
ConvertType(E->getType())));
}

case Builtin::BI__builtin_isinf: {
CodeGenFunction::CGFPOptionsRAII FPOptsRAII(*this, E);
Value *V = EmitScalarExpr(E->getArg(0));
Expand Down Expand Up @@ -3390,6 +3398,22 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
ConvertType(E->getType())));
}

case Builtin::BI__builtin_issubnormal: {
CodeGenFunction::CGFPOptionsRAII FPOptsRAII(*this, E);
Value *V = EmitScalarExpr(E->getArg(0));
return RValue::get(
Builder.CreateZExt(Builder.createIsFPClass(V, FPClassTest::fcSubnormal),
ConvertType(E->getType())));
}

case Builtin::BI__builtin_iszero: {
CodeGenFunction::CGFPOptionsRAII FPOptsRAII(*this, E);
Value *V = EmitScalarExpr(E->getArg(0));
return RValue::get(
Builder.CreateZExt(Builder.createIsFPClass(V, FPClassTest::fcZero),
ConvertType(E->getType())));
}

case Builtin::BI__builtin_isfpclass: {
Expr::EvalResult Result;
if (!E->getArg(1)->EvaluateAsInt(Result, CGM.getContext()))
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2255,7 +2255,10 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
case Builtin::BI__builtin_isinf:
case Builtin::BI__builtin_isinf_sign:
case Builtin::BI__builtin_isnan:
case Builtin::BI__builtin_issignaling:
case Builtin::BI__builtin_isnormal:
case Builtin::BI__builtin_issubnormal:
case Builtin::BI__builtin_iszero:
case Builtin::BI__builtin_signbit:
case Builtin::BI__builtin_signbitf:
case Builtin::BI__builtin_signbitl:
Expand Down
13 changes: 13 additions & 0 deletions clang/test/AST/Interp/builtin-functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ namespace nan {
// expected-error {{must be initialized by a constant expression}} \
// expected-note {{read of dereferenced one-past-the-end pointer}} \
// expected-note {{in call to}}
static_assert(!__builtin_issignaling(__builtin_nan("")), "");
static_assert(__builtin_issignaling(__builtin_nans("")), "");
}

namespace fmin {
Expand All @@ -153,6 +155,17 @@ namespace inf {

static_assert(__builtin_isnormal(1.0), "");
static_assert(!__builtin_isnormal(__builtin_inf()), "");

#ifndef __AVR__
static_assert(__builtin_issubnormal(0x1p-1070), "");
#endif
static_assert(!__builtin_issubnormal(__builtin_inf()), "");

static_assert(__builtin_iszero(0.0), "");
static_assert(!__builtin_iszero(__builtin_inf()), "");

static_assert(__builtin_issignaling(__builtin_nans("")), "");
static_assert(!__builtin_issignaling(__builtin_inf()), "");
}

namespace isfpclass {
Expand Down
15 changes: 15 additions & 0 deletions clang/test/CodeGen/builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ int main(void) {
P(isinf_sign, (1.));
P(isnan, (1.));
P(isfinite, (1.));
P(iszero, (1.));
P(issubnormal, (1.));
P(issignaling, (1.));
P(isfpclass, (1., 1));

// Bitwise & Numeric Functions
Expand Down Expand Up @@ -270,6 +273,18 @@ void test_float_builtins(__fp16 *H, float F, double D, long double LD) {
// CHECK: [[TMP:%.*]] = call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 264)
// CHECK: zext i1 [[TMP]] to i32

res = __builtin_issubnormal(F);
// CHECK: [[TMP:%.*]] = call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 144)
// CHECK: zext i1 [[TMP]] to i32

res = __builtin_iszero(F);
// CHECK: [[TMP:%.*]] = call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 96)
// CHECK: zext i1 [[TMP]] to i32

res = __builtin_issignaling(F);
// CHECK: [[TMP:%.*]] = call i1 @llvm.is.fpclass.f32(float {{.*}}, i32 1)
// CHECK: zext i1 [[TMP]] to i32

res = __builtin_flt_rounds();
// CHECK: call i32 @llvm.get.rounding(
}
Expand Down
39 changes: 39 additions & 0 deletions clang/test/Sema/constant-builtins-2.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,45 @@ char isnormal_inf_neg[!__builtin_isnormal(-__builtin_inf()) ? 1 : -1];
char isnormal_nan [!__builtin_isnormal(__builtin_nan("")) ? 1 : -1];
char isnormal_snan [!__builtin_isnormal(__builtin_nans("")) ? 1 : -1];

char iszero_inf_pos[!__builtin_iszero(__builtin_inf()) ? 1 : -1];
char iszero_pos [!__builtin_iszero(1.0) ? 1 : -1];
char iszero_normf [!__builtin_iszero(1e-37f) ? 1 : -1];
char iszero_denormf[!__builtin_iszero(1e-38f) ? 1 : -1];
char iszero_norm [!__builtin_iszero(1e-307) ? 1 : -1];
char iszero_denorm [!__builtin_iszero(1e-308) ? 1 : -1];
char iszero_zero [__builtin_iszero(0.0) ? 1 : -1];
char iszero_negzero[__builtin_iszero(-0.0) ? 1 : -1];
char iszero_neg [!__builtin_iszero(-1.0) ? 1 : -1];
char iszero_inf_neg[!__builtin_iszero(-__builtin_inf()) ? 1 : -1];
char iszero_nan [!__builtin_iszero(__builtin_nan("")) ? 1 : -1];
char iszero_snan [!__builtin_iszero(__builtin_nans("")) ? 1 : -1];

char issubnormal_inf_pos[!__builtin_issubnormal(__builtin_inf()) ? 1 : -1];
char issubnormal_pos [!__builtin_issubnormal(1.0) ? 1 : -1];
char issubnormal_normf [!__builtin_issubnormal(1e-37f) ? 1 : -1];
char issubnormal_denormf[__builtin_issubnormal(1e-38f) ? 1 : -1];
char issubnormal_norm [!__builtin_issubnormal(1e-307) ? 1 : -1];
char issubnormal_denorm [__builtin_issubnormal(1e-308) ? 1 : -1];
char issubnormal_zero [!__builtin_issubnormal(0.0) ? 1 : -1];
char issubnormal_negzero[!__builtin_issubnormal(-0.0) ? 1 : -1];
char issubnormal_neg [!__builtin_issubnormal(-1.0) ? 1 : -1];
char issubnormal_inf_neg[!__builtin_issubnormal(-__builtin_inf()) ? 1 : -1];
char issubnormal_nan [!__builtin_issubnormal(__builtin_nan("")) ? 1 : -1];
char issubnormal_snan [!__builtin_issubnormal(__builtin_nans("")) ? 1 : -1];

char issignaling_inf_pos[!__builtin_issignaling(__builtin_inf()) ? 1 : -1];
char issignaling_pos [!__builtin_issignaling(1.0) ? 1 : -1];
char issignaling_normf [!__builtin_issignaling(1e-37f) ? 1 : -1];
char issignaling_denormf[!__builtin_issignaling(1e-38f) ? 1 : -1];
char issignaling_norm [!__builtin_issignaling(1e-307) ? 1 : -1];
char issignaling_denorm [!__builtin_issignaling(1e-308) ? 1 : -1];
char issignaling_zero [!__builtin_issignaling(0.0) ? 1 : -1];
char issignaling_negzero[!__builtin_issignaling(-0.0) ? 1 : -1];
char issignaling_neg [!__builtin_issignaling(-1.0) ? 1 : -1];
char issignaling_inf_neg[!__builtin_issignaling(-__builtin_inf()) ? 1 : -1];
char issignaling_nan [!__builtin_issignaling(__builtin_nan("")) ? 1 : -1];
char issignaling_snan [__builtin_issignaling(__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
Expand Down

0 comments on commit fc7198b

Please sign in to comment.