EricWF updated this revision to Diff 183248.
EricWF marked 2 inline comments as done.
EricWF added a comment.

- Add or correct tests as requested.
- Fix code gen for initializers of reference types to correctly evaluate the 
initializer as a constant expression.


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

https://reviews.llvm.org/D55500

Files:
  include/clang/Basic/Builtins.def
  lib/AST/ExprConstant.cpp
  lib/Basic/Builtins.cpp
  lib/CodeGen/CGDecl.cpp
  test/CodeGenCXX/builtin-is-constant-evaluated.cpp
  test/Sema/builtins.c
  test/SemaCXX/builtin-is-constant-evaluated.cpp

Index: test/SemaCXX/builtin-is-constant-evaluated.cpp
===================================================================
--- /dev/null
+++ test/SemaCXX/builtin-is-constant-evaluated.cpp
@@ -0,0 +1,121 @@
+// RUN: %clang_cc1 -std=c++2a -verify %s -fcxx-exceptions -triple=x86_64-linux-gnu
+
+using size_t = decltype(sizeof(int));
+
+namespace std {
+inline constexpr bool is_constant_evaluated() noexcept {
+  return __builtin_is_constant_evaluated();
+}
+} // namespace std
+
+extern int dummy; // expected-note 1+ {{declared here}}
+
+static_assert(__builtin_is_constant_evaluated());
+static_assert(noexcept(__builtin_is_constant_evaluated()));
+
+constexpr bool b = __builtin_is_constant_evaluated();
+static_assert(b);
+
+const int n = __builtin_is_constant_evaluated() ? 4 : dummy;
+static_assert(n == 4);
+constexpr int cn = __builtin_is_constant_evaluated() ? 11 : dummy;
+static_assert(cn == 11);
+// expected-error@+1 {{'bn' must be initialized by a constant expression}}
+constexpr int bn = __builtin_is_constant_evaluated() ? dummy : 42; // expected-note {{non-const variable 'dummy' is not allowed}}
+
+const int n2 = __builtin_is_constant_evaluated() ? dummy : 42; // expected-note {{declared here}}
+static_assert(n2 == 42);                                       // expected-error {{static_assert expression is not an integral constant}}
+// expected-note@-1 {{initializer of 'n2' is not a constant expression}}
+
+template <bool V, bool Default = std::is_constant_evaluated()>
+struct Templ { static_assert(V); static_assert(Default); };
+Templ<__builtin_is_constant_evaluated()> x; // type X<true>
+
+template <class T>
+void test_if_constexpr() {
+  if constexpr (__builtin_is_constant_evaluated()) {
+    static_assert(__is_same(T, int));
+  } else {
+    using Test = typename T::DOES_NOT_EXIST;
+  }
+}
+template void test_if_constexpr<int>();
+
+void test_array_decl() {
+  char x[__builtin_is_constant_evaluated() + std::is_constant_evaluated()];
+  static_assert(sizeof(x) == 2, "");
+}
+
+void test_case_stmt(int x) {
+  switch (x) {
+  case 0:                                                                // OK
+  case __builtin_is_constant_evaluated():                                // expected-note {{previous case}}
+  case std::is_constant_evaluated() + __builtin_is_constant_evaluated(): // expected-note {{previous case}}
+  case 1:                                                                // expected-error {{duplicate case value '1'}}
+  case 2:                                                                // expected-error {{duplicate case value '2'}}
+    break;
+  }
+}
+
+constexpr size_t good_array_size() {
+  return std::is_constant_evaluated() ? 42 : static_cast<size_t>(-1);
+}
+
+constexpr size_t bad_array_size() {
+  return std::is_constant_evaluated() ? static_cast<size_t>(-1) : 13;
+}
+
+template <class T>
+constexpr T require_constexpr(T v) {
+  if (!std::is_constant_evaluated())
+    throw "BOOM";
+  return v;
+}
+
+void test_new_expr() {
+  constexpr size_t TooLarge = -1;
+  auto *x = new int[std::is_constant_evaluated() ? 1 : TooLarge];      // expected-error {{array is too large}}
+  auto *x2 = new int[std::is_constant_evaluated() ? TooLarge : 1];     // OK
+  auto *y = new int[1][std::is_constant_evaluated() ? TooLarge : 1]{}; // expected-error {{array is too large}}
+  auto *y2 = new int[1][require_constexpr(42)];
+}
+
+void test_alignas_operand() {
+  alignas(std::is_constant_evaluated() ? 8 : 2) char dummy;
+  static_assert(__alignof(dummy) == 8);
+}
+
+void test_static_assert_operand() {
+  static_assert(std::is_constant_evaluated(), "");
+}
+
+void test_enumerator() {
+  enum MyEnum {
+    ZERO = 0,
+    ONE = std::is_constant_evaluated()
+  };
+  static_assert(ONE == 1, "");
+}
+
+struct TestBitfieldWidth {
+  unsigned Bits : std::is_constant_evaluated();
+};
+
+void test_operand_of_noexcept_fn() noexcept(std::is_constant_evaluated());
+static_assert(noexcept(test_operand_of_noexcept_fn()), "");
+
+
+namespace test_ref_initialization {
+int x;
+int y;
+int &r = __builtin_is_constant_evaluated() ? x : y;
+static_assert(&r == &x);
+
+} // namespace test_ref_initialization
+
+#if defined(__cpp_conditional_explicit)
+struct TestConditionalExplicit {
+  explicit(__builtin_is_constant_evaluated()) TestConditionalExplicit(int) {}
+};
+TestConditionalExplicit e = 42;
+#endif
Index: test/Sema/builtins.c
===================================================================
--- test/Sema/builtins.c
+++ test/Sema/builtins.c
@@ -314,3 +314,9 @@
   memcpy(buf, src, 11); // expected-warning{{'memcpy' will always overflow; destination buffer has size 10, but size argument is 11}}
   my_memcpy(buf, src, 11); // expected-warning{{'__builtin___memcpy_chk' will always overflow; destination buffer has size 10, but size argument is 11}}
 }
+
+// Test that __builtin_is_constant_evaluated() is not allowed in C
+int test_cxx_builtin() {
+  // expected-error@+1 {{use of unknown builtin '__builtin_is_constant_evaluated'}}
+  return __builtin_is_constant_evaluated();
+}
Index: test/CodeGenCXX/builtin-is-constant-evaluated.cpp
===================================================================
--- /dev/null
+++ test/CodeGenCXX/builtin-is-constant-evaluated.cpp
@@ -0,0 +1,133 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm %s -std=c++2a -o %t.ll
+// RUN: FileCheck -check-prefix=CHECK-FN-CG -input-file=%t.ll %s
+// RUN: FileCheck -check-prefix=CHECK-STATIC -input-file=%t.ll %s
+// RUN: FileCheck -check-prefix=CHECK-DYN -input-file=%t.ll %s
+// RUN: FileCheck -check-prefix=CHECK-ARR -input-file=%t.ll %s
+// RUN: FileCheck -check-prefix=CHECK-FOLD -input-file=%t.ll %s
+
+using size_t = decltype(sizeof(int));
+
+#define CONSTINIT __attribute__((require_constant_initialization))
+
+extern "C" [[noreturn]] void BOOM();
+extern "C" void OK();
+extern "C" size_t RANDU();
+
+namespace std {
+inline constexpr bool is_constant_evaluated() noexcept {
+  return __builtin_is_constant_evaluated();
+}
+} // namespace std
+
+// CHECK-FN-CG-LABEL: define zeroext i1 @_Z3foov()
+// CHECK-FN-CG: ret i1 false
+bool foo() {
+  return __builtin_is_constant_evaluated();
+}
+
+// CHECK-FN-CG-LABEL: define linkonce_odr i32 @_Z1fv()
+constexpr int f() {
+  // CHECK-FN-CG: store i32 13, i32* %n, align 4
+  // CHECK-FN-CG: store i32 17, i32* %m, align 4
+  // CHECK-FN-CG:  %1 = load i32, i32* %m, align 4
+  // CHECK-FN-CG: %add = add nsw i32 %1, 13
+  // CHECK-FN-CG: ret i32 %add
+  const int n = __builtin_is_constant_evaluated() && std::is_constant_evaluated() ? 13 : 17; // n == 13
+  int m = __builtin_is_constant_evaluated() ? 13 : 17;       // m might be 13 or 17 (see below)
+  char arr[n] = {};                                          // char[13]
+  return m + int(sizeof(arr));
+}
+
+// CHECK-STATIC-DAG: @p = global i32 26,
+CONSTINIT int p = f(); // f().m == 13; initialized to 26
+// CHECK-STATIC-DAG: @p2 = global i32 26,
+int p2 = f(); // same result without CONSTINIT
+
+// CHECK-DYN-LABEL: define internal void @__cxx_global_var_init()
+// CHECK-DYN: %0 = load i32, i32* @p, align 4
+// CHECK-DYN-NEXT: %call = call i32 @_Z1fv()
+// CHECK-DYN-NEXT: %add = add nsw i32 %0, %call
+// CHECK-DYN-NEXT: store i32 %add, i32* @q, align 4
+// CHECK-DYN-NEXT: ret void
+int q = p + f(); // m == 17 for this call; initialized to 56
+
+int y;
+
+// CHECK-STATIC-DAG: @b = global i32 2,
+CONSTINIT int b = __builtin_is_constant_evaluated() ? 2 : y; // static initialization to 2
+
+// CHECK-DYN-LABEL: define internal void @__cxx_global_var_init.1()
+// CHECK-DYN: %0 = load i32, i32* @y, align 4
+// CHECK-DYN: %1 = load i32, i32* @y, align 4
+// CHECK-DYN-NEXT: %add = add
+// CHECK-DYN-NEXT: store i32 %add, i32* @c,
+int c = y + (__builtin_is_constant_evaluated() ? 2 : y); // dynamic initialization to y+y
+
+// CHECK-DYN-LABEL: define internal void @__cxx_global_var_init.2()
+// CHECK-DYN: store i32 1, i32* @_ZL1a, align 4
+// CHECK-DYN-NEXT: ret void
+const int a = __builtin_is_constant_evaluated() ? y : 1; // dynamic initialization to 1
+const int *a_sink = &a;
+
+// CHECK-ARR-LABEL: define void @_Z13test_arr_exprv
+void test_arr_expr() {
+  // CHECK-ARR: %x1 = alloca [101 x i8],
+  char x1[std::is_constant_evaluated() && __builtin_is_constant_evaluated() ? 101 : 1];
+
+  // CHECK-ARR: %x2 = alloca [42 x i8],
+  char x2[std::is_constant_evaluated() && __builtin_is_constant_evaluated() ? 42 : RANDU()];
+
+  // CHECK-ARR: call i8* @llvm.stacksave()
+  // CHECK-ARR: %vla = alloca i8, i64 13,
+  char x3[std::is_constant_evaluated() || __builtin_is_constant_evaluated() ? RANDU() : 13];
+}
+
+// CHECK-ARR-LABEL: define void @_Z17test_new_arr_exprv
+void test_new_arr_expr() {
+  // CHECK-ARR: call i8* @_Znam(i64 17)
+  new char[std::is_constant_evaluated() || __builtin_is_constant_evaluated() ? 1 : 17];
+}
+
+// CHECK-FOLD-LABEL: @_Z31test_constant_initialized_locali(
+bool test_constant_initialized_local(int k) {
+  // CHECK-FOLD: store i8 1, i8* %n,
+  // CHECK-FOLD: store volatile i8* %n, i8** %p,
+  const bool n = __builtin_is_constant_evaluated() && std::is_constant_evaluated();
+  const bool *volatile p = &n;
+  return *p;
+}
+
+// CHECK-FOLD-LABEL: define void @_Z21test_ir_constant_foldv()
+void test_ir_constant_fold() {
+  // CHECK-FOLD-NEXT: entry:
+  // CHECK-FOLD-NEXT: call void @OK()
+  // CHECK-FOLD-NEXT: call void @OK()
+  // CHECK-FOLD-NEXT: ret void
+  if (std::is_constant_evaluated()) {
+    BOOM();
+  } else {
+    OK();
+  }
+  std::is_constant_evaluated() ? BOOM() : OK();
+}
+
+// CHECK-STATIC-DAG: @ir = constant i32* @i_constant,
+int i_constant;
+int i_not_constant;
+int &ir = __builtin_is_constant_evaluated() ? i_constant : i_not_constant;
+
+// CHECK-FOLD-LABEL: @_Z35test_ref_initialization_local_scopev()
+void test_ref_initialization_local_scope() {
+  const int i_constant = 42;
+  const int i_non_constant = 101;
+  // CHECK-FOLD: store i32* %i_non_constant, i32** %r,
+  const int &r = __builtin_is_constant_evaluated() ? i_constant : i_non_constant;
+}
+
+// CHECK-FOLD-LABEL: @_Z22test_ref_to_static_varv()
+void test_ref_to_static_var() {
+  static int i_constant = 42;
+  static int i_non_constant = 101;
+  // CHECK-FOLD: store i32* @_ZZ22test_ref_to_static_varvE10i_constant, i32** %r,
+  int &r = __builtin_is_constant_evaluated() ? i_constant : i_non_constant;
+}
\ No newline at end of file
Index: lib/CodeGen/CGDecl.cpp
===================================================================
--- lib/CodeGen/CGDecl.cpp
+++ lib/CodeGen/CGDecl.cpp
@@ -1717,7 +1717,8 @@
   }
 
   llvm::Constant *constant = nullptr;
-  if (emission.IsConstantAggregate || D.isConstexpr()) {
+  if (emission.IsConstantAggregate || D.isConstexpr() ||
+      D.isUsableInConstantExpressions(getContext())) {
     assert(!capturedByInit && "constant init contains a capturing block?");
     constant = ConstantEmitter(*this).tryEmitAbstractForInitializer(D);
     if (constant && trivialAutoVarInit !=
Index: lib/Basic/Builtins.cpp
===================================================================
--- lib/Basic/Builtins.cpp
+++ lib/Basic/Builtins.cpp
@@ -75,9 +75,12 @@
   bool OclCUnsupported = !LangOpts.OpenCL &&
                          (BuiltinInfo.Langs & ALL_OCLC_LANGUAGES);
   bool OpenMPUnsupported = !LangOpts.OpenMP && BuiltinInfo.Langs == OMP_LANG;
+  bool CPlusPlusUnsupported =
+      !LangOpts.CPlusPlus && BuiltinInfo.Langs == CXX_LANG;
   return !BuiltinsUnsupported && !MathBuiltinsUnsupported && !OclCUnsupported &&
          !OclC1Unsupported && !OclC2Unsupported && !OpenMPUnsupported &&
-         !GnuModeUnsupported && !MSModeUnsupported && !ObjCUnsupported;
+         !GnuModeUnsupported && !MSModeUnsupported && !ObjCUnsupported &&
+         !CPlusPlusUnsupported;
 }
 
 /// initializeBuiltins - Mark the identifiers for all the builtins with their
Index: lib/AST/ExprConstant.cpp
===================================================================
--- lib/AST/ExprConstant.cpp
+++ lib/AST/ExprConstant.cpp
@@ -8279,6 +8279,9 @@
     return Success(false, E);
   }
 
+  case Builtin::BI__builtin_is_constant_evaluated:
+    return Success(Info.InConstantContext, E);
+
   case Builtin::BI__builtin_ctz:
   case Builtin::BI__builtin_ctzl:
   case Builtin::BI__builtin_ctzll:
@@ -11087,6 +11090,8 @@
                                   const ASTContext &Ctx) const {
   EvalInfo::EvaluationMode EM = EvalInfo::EM_ConstantExpression;
   EvalInfo Info(Ctx, Result, EM);
+  Info.InConstantContext = true;
+
   if (!::Evaluate(Result.Val, Info, this))
     return false;
 
Index: include/clang/Basic/Builtins.def
===================================================================
--- include/clang/Basic/Builtins.def
+++ include/clang/Basic/Builtins.def
@@ -500,6 +500,7 @@
 BUILTIN(__builtin_vsnprintf, "ic*zcC*a", "nFP:2:")
 BUILTIN(__builtin_thread_pointer, "v*", "nc")
 BUILTIN(__builtin_launder, "v*v*", "nt")
+LANGBUILTIN(__builtin_is_constant_evaluated, "b", "n", CXX_LANG)
 
 // GCC exception builtins
 BUILTIN(__builtin_eh_return, "vzv*", "r") // FIXME: Takes intptr_t, not size_t!
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D55500: [... Richard Smith - zygoloid via Phabricator via cfe-commits
    • [PATCH] D555... Eric Fiselier via Phabricator via cfe-commits
    • [PATCH] D555... Eric Fiselier via Phabricator via cfe-commits

Reply via email to