diff --git a/clang/lib/AST/Interp/InterpBuiltin.cpp b/clang/lib/AST/Interp/InterpBuiltin.cpp index b29747221ab55..a8758d8cc0569 100644 --- a/clang/lib/AST/Interp/InterpBuiltin.cpp +++ b/clang/lib/AST/Interp/InterpBuiltin.cpp @@ -9,6 +9,7 @@ #include "Interp.h" #include "PrimType.h" #include "clang/Basic/Builtins.h" +#include "clang/Basic/TargetInfo.h" namespace clang { namespace interp { @@ -59,6 +60,67 @@ static bool interp__builtin_strcmp(InterpState &S, CodePtr OpPC, return true; } +static bool interp__builtin_nan(InterpState &S, CodePtr OpPC, + const InterpFrame *Frame, const Function *F, + bool Signaling) { + const Pointer &Arg = getParam(Frame, 0); + + if (!CheckLoad(S, OpPC, Arg)) + return false; + + assert(Arg.getFieldDesc()->isPrimitiveArray()); + + // Convert the given string to an integer using StringRef's API. + llvm::APInt Fill; + std::string Str; + assert(Arg.getNumElems() >= 1); + for (unsigned I = 0;; ++I) { + const Pointer &Elem = Arg.atIndex(I); + + if (!CheckLoad(S, OpPC, Elem)) + return false; + + if (Elem.deref() == 0) + break; + + Str += Elem.deref(); + } + + // Treat empty strings as if they were zero. + if (Str.empty()) + Fill = llvm::APInt(32, 0); + else if (StringRef(Str).getAsInteger(0, Fill)) + return false; + + const llvm::fltSemantics &TargetSemantics = + S.getCtx().getFloatTypeSemantics(F->getDecl()->getReturnType()); + + Floating Result; + if (S.getCtx().getTargetInfo().isNan2008()) { + if (Signaling) + Result = Floating( + llvm::APFloat::getSNaN(TargetSemantics, /*Negative=*/false, &Fill)); + else + Result = Floating( + llvm::APFloat::getQNaN(TargetSemantics, /*Negative=*/false, &Fill)); + } else { + // Prior to IEEE 754-2008, architectures were allowed to choose whether + // the first bit of their significand was set for qNaN or sNaN. MIPS chose + // a different encoding to what became a standard in 2008, and for pre- + // 2008 revisions, MIPS interpreted sNaN-2008 as qNan and qNaN-2008 as + // sNaN. This is now known as "legacy NaN" encoding. + if (Signaling) + Result = Floating( + llvm::APFloat::getQNaN(TargetSemantics, /*Negative=*/false, &Fill)); + else + Result = Floating( + llvm::APFloat::getSNaN(TargetSemantics, /*Negative=*/false, &Fill)); + } + + S.Stk.push(Result); + return true; +} + bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F) { InterpFrame *Frame = S.Current; APValue Dummy; @@ -72,7 +134,24 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F) { case Builtin::BI__builtin_strcmp: if (interp__builtin_strcmp(S, OpPC, Frame)) return Ret(S, OpPC, Dummy); - return false; + break; + case Builtin::BI__builtin_nan: + case Builtin::BI__builtin_nanf: + case Builtin::BI__builtin_nanl: + case Builtin::BI__builtin_nanf16: + case Builtin::BI__builtin_nanf128: + if (interp__builtin_nan(S, OpPC, Frame, F, /*Signaling=*/false)) + return Ret(S, OpPC, Dummy); + break; + case Builtin::BI__builtin_nans: + case Builtin::BI__builtin_nansf: + case Builtin::BI__builtin_nansl: + case Builtin::BI__builtin_nansf16: + case Builtin::BI__builtin_nansf128: + if (interp__builtin_nan(S, OpPC, Frame, F, /*Signaling=*/true)) + return Ret(S, OpPC, Dummy); + break; + default: return false; } diff --git a/clang/test/AST/Interp/builtin-functions.cpp b/clang/test/AST/Interp/builtin-functions.cpp index eff8f80b7649a..ef28f003117dc 100644 --- a/clang/test/AST/Interp/builtin-functions.cpp +++ b/clang/test/AST/Interp/builtin-functions.cpp @@ -1,5 +1,8 @@ // RUN: %clang_cc1 -fexperimental-new-constant-interpreter %s -verify // RUN: %clang_cc1 -verify=ref %s -Wno-constant-evaluated +// RUN: %clang_cc1 -std=c++20 -fexperimental-new-constant-interpreter %s -verify +// RUN: %clang_cc1 -std=c++20 -verify=ref %s -Wno-constant-evaluated + namespace strcmp { constexpr char kFoobar[6] = {'f','o','o','b','a','r'}; @@ -34,3 +37,26 @@ namespace strcmp { // ref-error {{not an integral constant}} \ // ref-note {{dereferenced one-past-the-end}} } + +namespace nan { + constexpr double NaN1 = __builtin_nan(""); + + /// The current interpreter does not accept this, but it should. + constexpr float NaN2 = __builtin_nans([](){return "0xAE98";}()); // ref-error {{must be initialized by a constant expression}} + + constexpr double NaN3 = __builtin_nan("foo"); // expected-error {{must be initialized by a constant expression}} \ + // ref-error {{must be initialized by a constant expression}} + constexpr float NaN4 = __builtin_nanf(""); + constexpr long double NaN5 = __builtin_nanf128(""); + + /// FIXME: This should be accepted by the current interpreter as well. + constexpr char f[] = {'0', 'x', 'A', 'E', '\0'}; + constexpr double NaN6 = __builtin_nan(f); // ref-error {{must be initialized by a constant expression}} + + /// FIXME: Current interpreter misses diagnostics. + constexpr char f2[] = {'0', 'x', 'A', 'E'}; /// No trailing 0 byte. + constexpr double NaN7 = __builtin_nan(f2); // ref-error {{must be initialized by a constant expression}} \ + // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{read of dereferenced one-past-the-end pointer}} \ + // expected-note {{in call to}} +}