diff --git a/clang/lib/AST/Interp/Floating.h b/clang/lib/AST/Interp/Floating.h index c997323837c1c..d8e0108cb082f 100644 --- a/clang/lib/AST/Interp/Floating.h +++ b/clang/lib/AST/Interp/Floating.h @@ -90,6 +90,7 @@ class Floating final { bool isInf() const { return F.isInfinity(); } bool isFinite() const { return F.isFinite(); } bool isNormal() const { return F.isNormal(); } + llvm::FPClassTest classify() const { return F.classify(); } ComparisonCategoryResult compare(const Floating &RHS) const { llvm::APFloatBase::cmpResult CmpRes = F.compare(RHS.F); diff --git a/clang/lib/AST/Interp/InterpBuiltin.cpp b/clang/lib/AST/Interp/InterpBuiltin.cpp index 8c71f2f016252..146f047d7ee8f 100644 --- a/clang/lib/AST/Interp/InterpBuiltin.cpp +++ b/clang/lib/AST/Interp/InterpBuiltin.cpp @@ -21,6 +21,17 @@ static T getParam(const InterpFrame *Frame, unsigned Index) { return Frame->getParam(Offset); } +/// Peek an integer value from the stack into an APSInt. +static APSInt peekToAPSInt(InterpStack &Stk, PrimType T) { + APSInt R; + INT_TYPE_SWITCH(T, { + T Val = Stk.peek(); + R = APSInt(APInt(T::bitWidth(), static_cast(Val), T::isSigned())); + }); + + return R; +} + static bool interp__builtin_strcmp(InterpState &S, CodePtr OpPC, const InterpFrame *Frame) { const Pointer &A = getParam(Frame, 0); @@ -205,6 +216,25 @@ static bool interp__builtin_isnormal(InterpState &S, CodePtr OpPC, 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, + const InterpFrame *Frame, + const Function *Func) { + const Expr *E = S.Current->getExpr(OpPC); + const CallExpr *CE = cast(E); + PrimType FPClassArgT = *S.getContext().classify(CE->getArgs()[1]->getType()); + APSInt FPClassArg = peekToAPSInt(S.Stk, FPClassArgT); + const Floating &F = + S.Stk.peek(align(primSize(FPClassArgT) + primSize(PT_Float))); + + int32_t Result = + static_cast((F.classify() & FPClassArg).getZExtValue()); + S.Stk.push>(Integral<32, true>::from(Result)); + + return true; +} + bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F) { InterpFrame *Frame = S.Current; APValue Dummy; @@ -289,6 +319,10 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F) { if (interp__builtin_isnormal(S, OpPC, Frame, F)) return Ret(S, OpPC, Dummy); break; + case Builtin::BI__builtin_isfpclass: + if (interp__builtin_isfpclass(S, OpPC, Frame, F)) + return Ret(S, OpPC, Dummy); + break; default: return false; diff --git a/clang/lib/AST/Interp/PrimType.h b/clang/lib/AST/Interp/PrimType.h index ba4f630b12643..a4e2ae4355b54 100644 --- a/clang/lib/AST/Interp/PrimType.h +++ b/clang/lib/AST/Interp/PrimType.h @@ -114,6 +114,24 @@ static inline bool aligned(const void *P) { TYPE_SWITCH_CASE(PT_FnPtr, B) \ } \ } while (0) + +#define INT_TYPE_SWITCH(Expr, B) \ + do { \ + switch (Expr) { \ + TYPE_SWITCH_CASE(PT_Sint8, B) \ + TYPE_SWITCH_CASE(PT_Uint8, B) \ + TYPE_SWITCH_CASE(PT_Sint16, B) \ + TYPE_SWITCH_CASE(PT_Uint16, B) \ + TYPE_SWITCH_CASE(PT_Sint32, B) \ + TYPE_SWITCH_CASE(PT_Uint32, B) \ + TYPE_SWITCH_CASE(PT_Sint64, B) \ + TYPE_SWITCH_CASE(PT_Uint64, B) \ + TYPE_SWITCH_CASE(PT_Bool, B) \ + default: \ + llvm_unreachable("Not an integer value"); \ + } \ + } while (0) + #define COMPOSITE_TYPE_SWITCH(Expr, B, D) \ do { \ switch (Expr) { \ diff --git a/clang/test/AST/Interp/builtin-functions.cpp b/clang/test/AST/Interp/builtin-functions.cpp index 924914f9f942b..a7e55bd0e159e 100644 --- a/clang/test/AST/Interp/builtin-functions.cpp +++ b/clang/test/AST/Interp/builtin-functions.cpp @@ -83,3 +83,46 @@ namespace inf { static_assert(__builtin_isnormal(1.0), ""); static_assert(!__builtin_isnormal(__builtin_inf()), ""); } + +namespace isfpclass { + 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 +}