[PATCH] D155356: [clang][Interp] Implement __builtin_nan family of functions

2023-07-28 Thread Timm Bäder via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rGc2273e33bd13: [clang][Interp] Implement __builtin_nan family 
(authored by tbaeder).

Changed prior to commit:
  https://reviews.llvm.org/D155356?vs=544435=545055#toc

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D155356/new/

https://reviews.llvm.org/D155356

Files:
  clang/lib/AST/Interp/InterpBuiltin.cpp
  clang/test/AST/Interp/builtin-functions.cpp

Index: clang/test/AST/Interp/builtin-functions.cpp
===
--- clang/test/AST/Interp/builtin-functions.cpp
+++ 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 @@
 // 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}}
+}
Index: clang/lib/AST/Interp/InterpBuiltin.cpp
===
--- clang/lib/AST/Interp/InterpBuiltin.cpp
+++ 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 @@
   return true;
 }
 
+static bool interp__builtin_nan(InterpState , CodePtr OpPC,
+const InterpFrame *Frame, const Function *F,
+bool Signaling) {
+  const Pointer  = 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  = 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  =
+  S.getCtx().getFloatTypeSemantics(F->getDecl()->getReturnType());
+
+  Floating Result;
+  if (S.getCtx().getTargetInfo().isNan2008()) {
+if (Signaling)
+  Result = Floating(
+  llvm::APFloat::getSNaN(TargetSemantics, /*Negative=*/false, ));
+else
+  Result = Floating(
+  llvm::APFloat::getQNaN(TargetSemantics, /*Negative=*/false, ));
+  } 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, ));
+else
+  Result = 

[PATCH] D155356: [clang][Interp] Implement __builtin_nan family of functions

2023-07-26 Thread Timm Bäder via Phabricator via cfe-commits
tbaeder updated this revision to Diff 544435.

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D155356/new/

https://reviews.llvm.org/D155356

Files:
  clang/lib/AST/Interp/InterpBuiltin.cpp
  clang/test/AST/Interp/builtin-functions.cpp

Index: clang/test/AST/Interp/builtin-functions.cpp
===
--- clang/test/AST/Interp/builtin-functions.cpp
+++ 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 @@
 // 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: This should be accepted by the current interpreter as well.
+  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}}
+}
Index: clang/lib/AST/Interp/InterpBuiltin.cpp
===
--- clang/lib/AST/Interp/InterpBuiltin.cpp
+++ 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 @@
   return true;
 }
 
+static bool interp__builtin_nan(InterpState , CodePtr OpPC,
+const InterpFrame *Frame, const Function *F,
+bool Signaling) {
+  const Pointer  = 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  = 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  =
+  S.getCtx().getFloatTypeSemantics(F->getDecl()->getReturnType());
+
+  Floating Result;
+  if (S.getCtx().getTargetInfo().isNan2008()) {
+if (Signaling)
+  Result = Floating(
+  llvm::APFloat::getSNaN(TargetSemantics, /*Negative=*/false, ));
+else
+  Result = Floating(
+  llvm::APFloat::getQNaN(TargetSemantics, /*Negative=*/false, ));
+  } 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, ));
+else
+  Result = Floating(
+  llvm::APFloat::getSNaN(TargetSemantics, /*Negative=*/false, ));
+  }
+
+  S.Stk.push(Result);
+  return true;
+}
+
 bool InterpretBuiltin(InterpState , CodePtr OpPC, const Function *F) {
   InterpFrame *Frame = S.Current;
   APValue Dummy;
@@ -72,7 +134,24 @@
   case 

[PATCH] D155356: [clang][Interp] Implement __builtin_nan family of functions

2023-07-26 Thread Timm Bäder via Phabricator via cfe-commits
tbaeder added inline comments.



Comment at: clang/test/AST/Interp/builtin-functions.cpp:53
+  constexpr char f[] = {'0', 'x', 'A', 'E', '\0'};
+  constexpr double NaN6 = __builtin_nan(f); // ref-error {{must be initialized 
by a constant expression}}
+

aaron.ballman wrote:
> tbaeder wrote:
> > aaron.ballman wrote:
> > > This should be accepted, shouldn't it? The payload is a valid integer 
> > > constant (as a string).
> > So answer both your questions: Yes, but the current interpreter has a 
> > `isa()` check and rejects everything that isn't a string 
> > literal.
> Ah! Slap some FIXME comments around the two cases to explain what's going on 
> and that can be solved separately. Seem reasonable?
Sure, I can do that.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D155356/new/

https://reviews.llvm.org/D155356

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D155356: [clang][Interp] Implement __builtin_nan family of functions

2023-07-26 Thread Aaron Ballman via Phabricator via cfe-commits
aaron.ballman accepted this revision.
aaron.ballman added a comment.
This revision is now accepted and ready to land.

LG!




Comment at: clang/test/AST/Interp/builtin-functions.cpp:53
+  constexpr char f[] = {'0', 'x', 'A', 'E', '\0'};
+  constexpr double NaN6 = __builtin_nan(f); // ref-error {{must be initialized 
by a constant expression}}
+

tbaeder wrote:
> aaron.ballman wrote:
> > This should be accepted, shouldn't it? The payload is a valid integer 
> > constant (as a string).
> So answer both your questions: Yes, but the current interpreter has a 
> `isa()` check and rejects everything that isn't a string 
> literal.
Ah! Slap some FIXME comments around the two cases to explain what's going on 
and that can be solved separately. Seem reasonable?


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D155356/new/

https://reviews.llvm.org/D155356

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D155356: [clang][Interp] Implement __builtin_nan family of functions

2023-07-26 Thread Timm Bäder via Phabricator via cfe-commits
tbaeder added inline comments.



Comment at: clang/test/AST/Interp/builtin-functions.cpp:53
+  constexpr char f[] = {'0', 'x', 'A', 'E', '\0'};
+  constexpr double NaN6 = __builtin_nan(f); // ref-error {{must be initialized 
by a constant expression}}
+

aaron.ballman wrote:
> This should be accepted, shouldn't it? The payload is a valid integer 
> constant (as a string).
So answer both your questions: Yes, but the current interpreter has a 
`isa()` check and rejects everything that isn't a string literal.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D155356/new/

https://reviews.llvm.org/D155356

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D155356: [clang][Interp] Implement __builtin_nan family of functions

2023-07-26 Thread Aaron Ballman via Phabricator via cfe-commits
aaron.ballman added inline comments.



Comment at: clang/test/AST/Interp/builtin-functions.cpp:45
+  /// 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}}
+

This should be accepted by both the current and the new interpreter, right?



Comment at: clang/test/AST/Interp/builtin-functions.cpp:53
+  constexpr char f[] = {'0', 'x', 'A', 'E', '\0'};
+  constexpr double NaN6 = __builtin_nan(f); // ref-error {{must be initialized 
by a constant expression}}
+

This should be accepted, shouldn't it? The payload is a valid integer constant 
(as a string).


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D155356/new/

https://reviews.llvm.org/D155356

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D155356: [clang][Interp] Implement __builtin_nan family of functions

2023-07-26 Thread Timm Bäder via Phabricator via cfe-commits
tbaeder updated this revision to Diff 544258.

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D155356/new/

https://reviews.llvm.org/D155356

Files:
  clang/lib/AST/Interp/InterpBuiltin.cpp
  clang/test/AST/Interp/builtin-functions.cpp

Index: clang/test/AST/Interp/builtin-functions.cpp
===
--- clang/test/AST/Interp/builtin-functions.cpp
+++ 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,24 @@
 // 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("");
+
+  constexpr char f[] = {'0', 'x', 'A', 'E', '\0'};
+  constexpr double NaN6 = __builtin_nan(f); // ref-error {{must be initialized by a constant expression}}
+
+  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}}
+}
Index: clang/lib/AST/Interp/InterpBuiltin.cpp
===
--- clang/lib/AST/Interp/InterpBuiltin.cpp
+++ 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 {
@@ -57,6 +58,67 @@
   return true;
 }
 
+static bool interp__builtin_nan(InterpState , CodePtr OpPC,
+const InterpFrame *Frame, const Function *F,
+bool Signaling) {
+  const Pointer  = 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  = 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  =
+  S.getCtx().getFloatTypeSemantics(F->getDecl()->getReturnType());
+
+  Floating Result;
+  if (S.getCtx().getTargetInfo().isNan2008()) {
+if (Signaling)
+  Result = Floating(
+  llvm::APFloat::getSNaN(TargetSemantics, /*Negative=*/false, ));
+else
+  Result = Floating(
+  llvm::APFloat::getQNaN(TargetSemantics, /*Negative=*/false, ));
+  } 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, ));
+else
+  Result = Floating(
+  llvm::APFloat::getSNaN(TargetSemantics, /*Negative=*/false, ));
+  }
+
+  S.Stk.push(Result);
+  return true;
+}
+
 bool InterpretBuiltin(InterpState , CodePtr OpPC, const Function *F) {
   InterpFrame *Frame = S.Current;
   APValue Dummy;
@@ -70,7 +132,24 @@
   case Builtin::BI__builtin_strcmp:
 if (interp__builtin_strcmp(S, OpPC, Frame))
   return Ret(S, OpPC, Dummy);
-return false;
+break;
+  case 

[PATCH] D155356: [clang][Interp] Implement __builtin_nan family of functions

2023-07-25 Thread Aaron Ballman via Phabricator via cfe-commits
aaron.ballman added a comment.

In D155356#4513303 , @tbaeder wrote:

> One thing I'm wondering though is:
>
>   constexpr char A[] = {'1', '2', '3'};
>   constexpr double d = __builtin_nans(A);
>
> what's expected here? The given char array is not null-terminated.

The builtin expects a null terminated string, so that should be an invalid 
constant expression due to reading off the end of the array.

> Should the interpreter ignore the null byte at the end if it's there? 
> `StringRef::getAsInteger()` always returns an error if it encounters one, so 
> the currentcode in `InterpBuiltin.cpp` ignores the last byte and will pass 
> `12` along for the example above.

Yeah, we should not do that. :-D In GCC, this builtin exists to support the 
`nans()` function from 
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n965.htm (which was proposed 
but never adopted); Clang's support is for parity with GCC.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D155356/new/

https://reviews.llvm.org/D155356

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D155356: [clang][Interp] Implement __builtin_nan family of functions

2023-07-18 Thread Timm Bäder via Phabricator via cfe-commits
tbaeder added a comment.

One thing I'm wondering though is:

  constexpr char A[] = {'1', '2', '3'};
  constexpr double d = __builtin_nans(A);

what's expected here? The given char array is not null-terminated. 
Should the interpreter ignore the null byte at the end if it's there? 
`StringRef::getAsInteger()` always returns an error if it encounters one, so 
the currentcode in `InterpBuiltin.cpp` ignores the last byte and will pass `12` 
along for the example above.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D155356/new/

https://reviews.llvm.org/D155356

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D155356: [clang][Interp] Implement __builtin_nan family of functions

2023-07-18 Thread Timm Bäder via Phabricator via cfe-commits
tbaeder updated this revision to Diff 541833.
tbaeder marked an inline comment as done.

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D155356/new/

https://reviews.llvm.org/D155356

Files:
  clang/lib/AST/Interp/InterpBuiltin.cpp
  clang/test/AST/Interp/builtin-functions.cpp

Index: clang/test/AST/Interp/builtin-functions.cpp
===
--- clang/test/AST/Interp/builtin-functions.cpp
+++ 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,15 @@
 // 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("");
+}
Index: clang/lib/AST/Interp/InterpBuiltin.cpp
===
--- clang/lib/AST/Interp/InterpBuiltin.cpp
+++ 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 {
@@ -57,6 +58,64 @@
   return true;
 }
 
+static bool interp__builtin_nan(InterpState , CodePtr OpPC,
+const InterpFrame *Frame, const Function *F,
+bool Signaling) {
+  const Pointer  = 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, E = Arg.getNumElems() - 1; I != E; ++I) {
+const Pointer  = Arg.atIndex(I);
+
+if (!CheckLoad(S, OpPC, Elem))
+  return false;
+
+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  =
+  S.getCtx().getFloatTypeSemantics(F->getDecl()->getReturnType());
+
+  Floating Result;
+  if (S.getCtx().getTargetInfo().isNan2008()) {
+if (Signaling)
+  Result = Floating(
+  llvm::APFloat::getSNaN(TargetSemantics, /*Negative=*/false, ));
+else
+  Result = Floating(
+  llvm::APFloat::getQNaN(TargetSemantics, /*Negative=*/false, ));
+  } 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, ));
+else
+  Result = Floating(
+  llvm::APFloat::getSNaN(TargetSemantics, /*Negative=*/false, ));
+  }
+
+  S.Stk.push(Result);
+  return true;
+}
+
 bool InterpretBuiltin(InterpState , CodePtr OpPC, const Function *F) {
   InterpFrame *Frame = S.Current;
   APValue Dummy;
@@ -70,7 +129,24 @@
   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;
   }

[PATCH] D155356: [clang][Interp] Implement __builtin_nan family of functions

2023-07-18 Thread Timm Bäder via Phabricator via cfe-commits
tbaeder marked 2 inline comments as done.
tbaeder added inline comments.



Comment at: clang/lib/AST/Interp/InterpBuiltin.cpp:106
+// 2008 revisions, MIPS interpreted sNaN-2008 as qNan and qNaN-2008 as
+// sNaN. This is now known as "legacy NaN" encoding.
+if (Signaling)

aaron.ballman wrote:
> LOL, thank you for this comment and wow. :-D
That's just copy/paste from `ExprConst.cpp` :)



Comment at: clang/test/AST/Interp/builtin-functions.cpp:44
+
+  constexpr float Nan2 = __builtin_nans([](){return "0xAE98";}()); // 
ref-error {{must be initialized by a constant expression}}
+}

aaron.ballman wrote:
> tbaeder wrote:
> > aaron.ballman wrote:
> > > Probably worth a comment here mentioning that the ref-error is a 
> > > rejects-valid issue that the experimental compiler accepts correctly.
> > I thought you said on Discord that the current interpreter is correct? I 
> > made the new one reject anything but string literals in 
> > https://reviews.llvm.org/D155545.
> Oops, crossing streams as I just mentioned this in another review of yours in 
> the same area.
> 
> I think I misunderstood the question on Discord when we were chatting. I was 
> thinking the situation was more that Sema was allowing through something like 
> `__builtin_nan(12);` and diagnosing it later during CodeGen. I think 
> constexpr builtins should generally behave the same as regular constexpr 
> functions the user could write.
> 
> I'm really sorry if I made extra work for you with this confusion!
No problem, passing the `CallExpr` along still makes sense.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D155356/new/

https://reviews.llvm.org/D155356

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D155356: [clang][Interp] Implement __builtin_nan family of functions

2023-07-18 Thread Timm Bäder via Phabricator via cfe-commits
tbaeder updated this revision to Diff 541832.
tbaeder marked 3 inline comments as done.

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D155356/new/

https://reviews.llvm.org/D155356

Files:
  clang/lib/AST/Interp/InterpBuiltin.cpp
  clang/test/AST/Interp/builtin-functions.cpp

Index: clang/test/AST/Interp/builtin-functions.cpp
===
--- clang/test/AST/Interp/builtin-functions.cpp
+++ 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,14 @@
 // ref-error {{not an integral constant}} \
 // ref-note {{dereferenced one-past-the-end}}
 }
+
+namespace nan {
+  constexpr double NaN1 = __builtin_nan("");
+
+  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("");
+}
Index: clang/lib/AST/Interp/InterpBuiltin.cpp
===
--- clang/lib/AST/Interp/InterpBuiltin.cpp
+++ 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 {
@@ -57,6 +58,64 @@
   return true;
 }
 
+static bool interp__builtin_nan(InterpState , CodePtr OpPC,
+const InterpFrame *Frame, const Function *F,
+bool Signaling) {
+  const Pointer  = 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, E = Arg.getNumElems() - 1; I != E; ++I) {
+const Pointer  = Arg.atIndex(I);
+
+if (!CheckLoad(S, OpPC, Elem))
+  return false;
+
+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  =
+  S.getCtx().getFloatTypeSemantics(F->getDecl()->getReturnType());
+
+  Floating Result;
+  if (S.getCtx().getTargetInfo().isNan2008()) {
+if (Signaling)
+  Result = Floating(
+  llvm::APFloat::getSNaN(TargetSemantics, /*Negative=*/false, ));
+else
+  Result = Floating(
+  llvm::APFloat::getQNaN(TargetSemantics, /*Negative=*/false, ));
+  } 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, ));
+else
+  Result = Floating(
+  llvm::APFloat::getSNaN(TargetSemantics, /*Negative=*/false, ));
+  }
+
+  S.Stk.push(Result);
+  return true;
+}
+
 bool InterpretBuiltin(InterpState , CodePtr OpPC, const Function *F) {
   InterpFrame *Frame = S.Current;
   APValue Dummy;
@@ -70,7 +129,24 @@
   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;
   }
___
cfe-commits mailing 

[PATCH] D155356: [clang][Interp] Implement __builtin_nan family of functions

2023-07-18 Thread Aaron Ballman via Phabricator via cfe-commits
aaron.ballman added inline comments.



Comment at: clang/test/AST/Interp/builtin-functions.cpp:44
+
+  constexpr float Nan2 = __builtin_nans([](){return "0xAE98";}()); // 
ref-error {{must be initialized by a constant expression}}
+}

tbaeder wrote:
> aaron.ballman wrote:
> > Probably worth a comment here mentioning that the ref-error is a 
> > rejects-valid issue that the experimental compiler accepts correctly.
> I thought you said on Discord that the current interpreter is correct? I made 
> the new one reject anything but string literals in 
> https://reviews.llvm.org/D155545.
Oops, crossing streams as I just mentioned this in another review of yours in 
the same area.

I think I misunderstood the question on Discord when we were chatting. I was 
thinking the situation was more that Sema was allowing through something like 
`__builtin_nan(12);` and diagnosing it later during CodeGen. I think constexpr 
builtins should generally behave the same as regular constexpr functions the 
user could write.

I'm really sorry if I made extra work for you with this confusion!


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D155356/new/

https://reviews.llvm.org/D155356

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D155356: [clang][Interp] Implement __builtin_nan family of functions

2023-07-18 Thread Timm Bäder via Phabricator via cfe-commits
tbaeder added inline comments.



Comment at: clang/test/AST/Interp/builtin-functions.cpp:44
+
+  constexpr float Nan2 = __builtin_nans([](){return "0xAE98";}()); // 
ref-error {{must be initialized by a constant expression}}
+}

aaron.ballman wrote:
> Probably worth a comment here mentioning that the ref-error is a 
> rejects-valid issue that the experimental compiler accepts correctly.
I thought you said on Discord that the current interpreter is correct? I made 
the new one reject anything but string literals in 
https://reviews.llvm.org/D155545.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D155356/new/

https://reviews.llvm.org/D155356

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D155356: [clang][Interp] Implement __builtin_nan family of functions

2023-07-18 Thread Aaron Ballman via Phabricator via cfe-commits
aaron.ballman added inline comments.



Comment at: clang/lib/AST/Interp/InterpBuiltin.cpp:88
+  else if (StringRef(Str).getAsInteger(0, Fill))
+return false;
+

Please add test coverage for a case like `__builtin_nan("derp")` to show it's 
not a valid constant expression.



Comment at: clang/lib/AST/Interp/InterpBuiltin.cpp:106
+// 2008 revisions, MIPS interpreted sNaN-2008 as qNan and qNaN-2008 as
+// sNaN. This is now known as "legacy NaN" encoding.
+if (Signaling)

LOL, thank you for this comment and wow. :-D



Comment at: clang/lib/AST/Interp/InterpBuiltin.cpp:135-137
+  case Builtin::BI__builtin_nanl:
+  case Builtin::BI__builtin_nanf16:
+  case Builtin::BI__builtin_nanf128:

We should have test coverage for these odd sizes just to ensure we're properly 
pushing/popping what we expect off the stack.



Comment at: clang/test/AST/Interp/builtin-functions.cpp:44
+
+  constexpr float Nan2 = __builtin_nans([](){return "0xAE98";}()); // 
ref-error {{must be initialized by a constant expression}}
+}

Probably worth a comment here mentioning that the ref-error is a rejects-valid 
issue that the experimental compiler accepts correctly.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D155356/new/

https://reviews.llvm.org/D155356

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D155356: [clang][Interp] Implement __builtin_nan family of functions

2023-07-17 Thread Timm Bäder via Phabricator via cfe-commits
tbaeder updated this revision to Diff 541318.

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D155356/new/

https://reviews.llvm.org/D155356

Files:
  clang/lib/AST/Interp/InterpBuiltin.cpp
  clang/test/AST/Interp/builtin-functions.cpp

Index: clang/test/AST/Interp/builtin-functions.cpp
===
--- clang/test/AST/Interp/builtin-functions.cpp
+++ 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,9 @@
 // ref-error {{not an integral constant}} \
 // ref-note {{dereferenced one-past-the-end}}
 }
+
+namespace nan {
+  constexpr double NaN1 = __builtin_nan("");
+
+  constexpr float Nan2 = __builtin_nans([](){return "0xAE98";}()); // ref-error {{must be initialized by a constant expression}}
+}
Index: clang/lib/AST/Interp/InterpBuiltin.cpp
===
--- clang/lib/AST/Interp/InterpBuiltin.cpp
+++ 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 {
@@ -57,6 +58,64 @@
   return true;
 }
 
+static bool interp__builtin_nan(InterpState , CodePtr OpPC,
+const InterpFrame *Frame, const Function *F,
+bool Signaling) {
+  const Pointer  = 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, E = Arg.getNumElems() - 1; I != E; ++I) {
+const Pointer  = Arg.atIndex(I);
+
+if (!CheckLoad(S, OpPC, Elem))
+  return false;
+
+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  =
+  S.getCtx().getFloatTypeSemantics(F->getDecl()->getReturnType());
+
+  Floating Result;
+  if (S.getCtx().getTargetInfo().isNan2008()) {
+if (Signaling)
+  Result = Floating(
+  llvm::APFloat::getSNaN(TargetSemantics, /*Negative=*/false, ));
+else
+  Result = Floating(
+  llvm::APFloat::getQNaN(TargetSemantics, /*Negative=*/false, ));
+  } 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, ));
+else
+  Result = Floating(
+  llvm::APFloat::getSNaN(TargetSemantics, /*Negative=*/false, ));
+  }
+
+  S.Stk.push(Result);
+  return true;
+}
+
 bool InterpretBuiltin(InterpState , CodePtr OpPC, const Function *F) {
   InterpFrame *Frame = S.Current;
   APValue Dummy;
@@ -70,7 +129,24 @@
   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;
   }
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D155356: [clang][Interp] Implement __builtin_nan family of functions

2023-07-17 Thread Timm Bäder via Phabricator via cfe-commits
tbaeder updated this revision to Diff 541314.

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D155356/new/

https://reviews.llvm.org/D155356

Files:
  clang/lib/AST/Interp/InterpBuiltin.cpp
  clang/test/AST/Interp/builtin-functions.cpp

Index: clang/test/AST/Interp/builtin-functions.cpp
===
--- clang/test/AST/Interp/builtin-functions.cpp
+++ 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,28 @@
 // ref-error {{not an integral constant}} \
 // ref-note {{dereferenced one-past-the-end}}
 }
+
+namespace nan {
+  constexpr double NaN1 = __builtin_nan("");
+
+#if __cplusplus >= 202002
+  /// Reading an uninitialized value as part of the argument.
+  constexpr int foo() { // ref-error {{never produces a constant expression}}
+char f[2];
+f[0] = '1';
+
+double d = __builtin_nan(f); // ref-note 2{{subexpression not valid in a constant expression}} \
+ // expected-note {{read of uninitialized object}} \
+ // expected-note {{in call to '__builtin_nan}}
+return 1;
+  }
+  static_assert(foo() == 1); // ref-error {{not an integral constant expression}} \
+ // ref-note {{in call to}} \
+ // expected-error {{not an integral constant expression}} \
+ // expected-note {{in call to}}
+#endif
+
+
+  constexpr float Nan2 = __builtin_nans([](){return "0xAE98";}()); // ref-error {{must be initialized by a constant expression}}
+
+}
Index: clang/lib/AST/Interp/InterpBuiltin.cpp
===
--- clang/lib/AST/Interp/InterpBuiltin.cpp
+++ 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 {
@@ -57,6 +58,60 @@
   return true;
 }
 
+static bool interp__builtin_nan(InterpState , CodePtr OpPC,
+const InterpFrame *Frame, const Function *F,
+bool Signaling) {
+  const Pointer  = 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, E = Arg.getNumElems() - 1; I != E; ++I) {
+const Pointer  = Arg.atIndex(I);
+
+if (!CheckLoad(S, OpPC, Elem))
+  return false;
+
+Str += Elem.deref();
+  }
+  if (StringRef(Str).getAsInteger(0, Fill))
+return false;
+
+  const llvm::fltSemantics  =
+  S.getCtx().getFloatTypeSemantics(F->getDecl()->getReturnType());
+
+  Floating Result;
+  if (S.getCtx().getTargetInfo().isNan2008()) {
+if (Signaling)
+  Result = Floating(
+  llvm::APFloat::getSNaN(TargetSemantics, /*Negative=*/false, ));
+else
+  Result = Floating(
+  llvm::APFloat::getQNaN(TargetSemantics, /*Negative=*/false, ));
+  } 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, ));
+else
+  Result = Floating(
+  llvm::APFloat::getSNaN(TargetSemantics, /*Negative=*/false, ));
+  }
+
+  S.Stk.push(Result);
+  return true;
+}
+
 bool InterpretBuiltin(InterpState , CodePtr OpPC, const Function *F) {
   InterpFrame *Frame = S.Current;
   APValue Dummy;
@@ -70,7 +125,24 @@
   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 

[PATCH] D155356: [clang][Interp] Implement __builtin_nan family of functions

2023-07-15 Thread Timm Bäder via Phabricator via cfe-commits
tbaeder created this revision.
tbaeder added reviewers: aaron.ballman, erichkeane, shafik, cor3ntin.
Herald added a project: All.
tbaeder requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

One existing problem is that we can't do much with the values since comparing 
with nans is broken ATM and I don't want to make this patch depend on the 
`__builtin_bit_cast` patch.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D155356

Files:
  clang/lib/AST/Interp/InterpBuiltin.cpp
  clang/test/AST/Interp/builtin-functions.cpp

Index: clang/test/AST/Interp/builtin-functions.cpp
===
--- clang/test/AST/Interp/builtin-functions.cpp
+++ 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,28 @@
 // ref-error {{not an integral constant}} \
 // ref-note {{dereferenced one-past-the-end}}
 }
+
+namespace nan {
+  constexpr double NaN1 = __builtin_nan("");
+
+#if __cplusplus >= 202002
+  /// Reading an uninitialized value as part of the argument.
+  constexpr int foo() { // ref-error {{never produces a constant expression}}
+char f[2];
+f[0] = '1';
+
+double d = __builtin_nan(f); // ref-note 2{{subexpression not valid in a constant expression}} \
+ // expected-note {{read of uninitialized object}} \
+ // expected-note {{in call to '__builtin_nan}}
+return 1;
+  }
+  static_assert(foo() == 1); // ref-error {{not an integral constant expression}} \
+ // ref-note {{in call to}} \
+ // expected-error {{not an integral constant expression}} \
+ // expected-note {{in call to}}
+#endif
+
+
+  constexpr float Nan2 = __builtin_nans([](){return "0xAE98";}()); // ref-error {{must be initialized by a constant expression}}
+
+}
Index: clang/lib/AST/Interp/InterpBuiltin.cpp
===
--- clang/lib/AST/Interp/InterpBuiltin.cpp
+++ 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 {
@@ -57,6 +58,63 @@
   return true;
 }
 
+static bool interp__builtin_nan(InterpState , CodePtr OpPC,
+const InterpFrame *Frame, const Function *F,
+bool Signaling) {
+  const Pointer  = getParam(Frame, 0);
+
+  if (!CheckLoad(S, OpPC, Arg))
+return false;
+
+  assert(Arg.getFieldDesc()->isPrimitiveArray());
+
+  llvm::APInt Fill;
+  if (Arg.getNumElems() == 0)
+Fill = llvm::APInt(32, 0);
+  else {
+// Convert the given string to an integer using StringRef's API.
+std::string Str;
+
+for (unsigned I = 0, E = Arg.getNumElems(); I != E; ++I) {
+  const Pointer  = Arg.atIndex(I);
+
+  if (!CheckLoad(S, OpPC, Elem))
+return false;
+
+  Str += Elem.deref();
+}
+StringRef(Str).getAsInteger(0, Fill);
+  }
+
+  const llvm::fltSemantics  =
+  S.getCtx().getFloatTypeSemantics(F->getDecl()->getReturnType());
+
+  Floating Result;
+  if (S.getCtx().getTargetInfo().isNan2008()) {
+if (Signaling)
+  Result = Floating(
+  llvm::APFloat::getSNaN(TargetSemantics, /*Negative=*/false, ));
+else
+  Result = Floating(
+  llvm::APFloat::getQNaN(TargetSemantics, /*Negative=*/false, ));
+  } 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, ));
+else
+  Result = Floating(
+  llvm::APFloat::getSNaN(TargetSemantics, /*Negative=*/false, ));
+  }
+
+  S.Stk.push(Result);
+  return true;
+}
+
 bool InterpretBuiltin(InterpState , CodePtr OpPC, const Function *F) {
   InterpFrame *Frame = S.Current;
   APValue Dummy;
@@ -70,7 +128,24 @@
   case Builtin::BI__builtin_strcmp:
 if (interp__builtin_strcmp(S, OpPC, Frame))
   return Ret(S, OpPC, Dummy);
-return false;
+