1 // RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm %s -std=c++2a -o %t.ll
2 // RUN: FileCheck -check-prefix=CHECK-FN-CG -input-file=%t.ll %s
3 // RUN: FileCheck -check-prefix=CHECK-STATIC -input-file=%t.ll %s
4 // RUN: FileCheck -check-prefix=CHECK-DYN -input-file=%t.ll %s
5 // RUN: FileCheck -check-prefix=CHECK-ARR -input-file=%t.ll %s
6 // RUN: FileCheck -check-prefix=CHECK-FOLD -input-file=%t.ll %s
7 // RUN: FileCheck -check-prefix=CHECK-DTOR -input-file=%t.ll %s
8 
9 using size_t = decltype(sizeof(int));
10 
11 #define CONSTINIT __attribute__((require_constant_initialization))
12 
13 extern "C" [[noreturn]] void BOOM();
14 extern "C" void OK();
15 extern "C" size_t RANDU();
16 
17 namespace std {
is_constant_evaluated()18 inline constexpr bool is_constant_evaluated() noexcept {
19   return __builtin_is_constant_evaluated();
20 }
21 } // namespace std
22 
23 // CHECK-FN-CG-LABEL: define{{.*}} zeroext i1 @_Z3foov()
24 // CHECK-FN-CG: ret i1 false
foo()25 bool foo() {
26   return __builtin_is_constant_evaluated();
27 }
28 
29 // CHECK-FN-CG-LABEL: define linkonce_odr i32 @_Z1fv()
f()30 constexpr int f() {
31   // CHECK-FN-CG: store i32 13, i32* %n, align 4
32   // CHECK-FN-CG: store i32 17, i32* %m, align 4
33   // CHECK-FN-CG:  %1 = load i32, i32* %m, align 4
34   // CHECK-FN-CG: %add = add nsw i32 %1, 13
35   // CHECK-FN-CG: ret i32 %add
36   const int n = __builtin_is_constant_evaluated() && std::is_constant_evaluated() ? 13 : 17; // n == 13
37   int m = __builtin_is_constant_evaluated() ? 13 : 17;       // m might be 13 or 17 (see below)
38   char arr[n] = {};                                          // char[13]
39   return m + int(sizeof(arr));
40 }
41 
42 // CHECK-STATIC-DAG: @p ={{.*}} global i32 26,
43 CONSTINIT int p = f(); // f().m == 13; initialized to 26
44 // CHECK-STATIC-DAG: @p2 ={{.*}} global i32 26,
45 int p2 = f(); // same result without CONSTINIT
46 
47 // CHECK-DYN-LABEL: define internal void @__cxx_global_var_init()
48 // CHECK-DYN: %0 = load i32, i32* @p, align 4
49 // CHECK-DYN-NEXT: %call = call i32 @_Z1fv()
50 // CHECK-DYN-NEXT: %add = add nsw i32 %0, %call
51 // CHECK-DYN-NEXT: store i32 %add, i32* @q, align 4
52 // CHECK-DYN-NEXT: ret void
53 int q = p + f(); // m == 17 for this call; initialized to 56
54 
55 int y;
56 
57 // CHECK-STATIC-DAG: @b ={{.*}} global i32 2,
58 CONSTINIT int b = __builtin_is_constant_evaluated() ? 2 : y; // static initialization to 2
59 
60 // CHECK-DYN-LABEL: define internal void @__cxx_global_var_init.1()
61 // CHECK-DYN: %0 = load i32, i32* @y, align 4
62 // CHECK-DYN: %1 = load i32, i32* @y, align 4
63 // CHECK-DYN-NEXT: %add = add
64 // CHECK-DYN-NEXT: store i32 %add, i32* @c,
65 int c = y + (__builtin_is_constant_evaluated() ? 2 : y); // dynamic initialization to y+y
66 
67 // This is dynamic initialization that we can convert to static initialization
68 // during lowering. When doing so, the dynamic initializer value is preserved.
69 // CHECK-STATIC-DAG: @_ZL1a = internal constant i32 1
70 const int a = __builtin_is_constant_evaluated() ? y : 1; // dynamic initialization to 1
71 const int *a_sink = &a;
72 
73 // CHECK-ARR-LABEL: define{{.*}} void @_Z13test_arr_exprv
test_arr_expr()74 void test_arr_expr() {
75   // CHECK-ARR: %x1 = alloca [101 x i8],
76   char x1[std::is_constant_evaluated() && __builtin_is_constant_evaluated() ? 101 : 1];
77 
78   // CHECK-ARR: %x2 = alloca [42 x i8],
79   char x2[std::is_constant_evaluated() && __builtin_is_constant_evaluated() ? 42 : RANDU()];
80 
81   // CHECK-ARR: call i8* @llvm.stacksave()
82   // CHECK-ARR: %vla = alloca i8, i64 13,
83   char x3[std::is_constant_evaluated() || __builtin_is_constant_evaluated() ? RANDU() : 13];
84 }
85 
86 // CHECK-ARR-LABEL: define{{.*}} void @_Z17test_new_arr_exprv
test_new_arr_expr()87 void test_new_arr_expr() {
88   // CHECK-ARR: call noalias nonnull i8* @_Znam(i64 17)
89   new char[std::is_constant_evaluated() || __builtin_is_constant_evaluated() ? 1 : 17];
90 }
91 
92 // CHECK-FOLD-LABEL: @_Z31test_constant_initialized_locali(
test_constant_initialized_local(int k)93 bool test_constant_initialized_local(int k) {
94   // CHECK-FOLD: store i8 1, i8* %n,
95   // CHECK-FOLD: store volatile i8* %n, i8** %p,
96   const bool n = __builtin_is_constant_evaluated() && std::is_constant_evaluated();
97   const bool *volatile p = &n;
98   return *p;
99 }
100 
101 // CHECK-FOLD-LABEL: define{{.*}} void @_Z21test_ir_constant_foldv()
test_ir_constant_fold()102 void test_ir_constant_fold() {
103   // CHECK-FOLD-NEXT: entry:
104   // CHECK-FOLD-NEXT: call void @OK()
105   // CHECK-FOLD-NEXT: call void @OK()
106   // CHECK-FOLD-NEXT: ret void
107   if (std::is_constant_evaluated()) {
108     BOOM();
109   } else {
110     OK();
111   }
112   std::is_constant_evaluated() ? BOOM() : OK();
113 }
114 
115 // CHECK-STATIC-DAG: @ir ={{.*}} constant i32* @i_constant,
116 int i_constant;
117 int i_not_constant;
118 int &ir = __builtin_is_constant_evaluated() ? i_constant : i_not_constant;
119 
120 // CHECK-FOLD-LABEL: @_Z35test_ref_initialization_local_scopev()
test_ref_initialization_local_scope()121 void test_ref_initialization_local_scope() {
122   const int i_constant = 42;
123   const int i_non_constant = 101;
124   // CHECK-FOLD: store i32* %i_non_constant, i32** %r,
125   const int &r = __builtin_is_constant_evaluated() ? i_constant : i_non_constant;
126 }
127 
128 // CHECK-FOLD-LABEL: @_Z22test_ref_to_static_varv()
test_ref_to_static_var()129 void test_ref_to_static_var() {
130   static int i_constant = 42;
131   static int i_non_constant = 101;
132   // CHECK-FOLD: store i32* @_ZZ22test_ref_to_static_varvE10i_constant, i32** %r,
133   int &r = __builtin_is_constant_evaluated() ? i_constant : i_non_constant;
134 }
135 
136 int not_constexpr;
137 
138 // __builtin_is_constant_evaluated() should never evaluate to true during
139 // destruction if it would not have done so during construction.
140 //
141 // FIXME: The standard doesn't say that it should ever return true when
142 // evaluating a destructor call, even for a constexpr variable. That seems
143 // obviously wrong.
144 struct DestructorBCE {
145   int n;
DestructorBCEDestructorBCE146   constexpr DestructorBCE(int n) : n(n) {}
~DestructorBCEDestructorBCE147   constexpr ~DestructorBCE() {
148     if (!__builtin_is_constant_evaluated())
149       not_constexpr = 1;
150   }
151 };
152 
153 // CHECK-DTOR-NOT: @_ZN13DestructorBCED{{.*}}@global_dtor_bce_1
154 DestructorBCE global_dtor_bce_1(101);
155 
156 // CHECK-DTOR: load i32, i32* @not_constexpr
157 // CHECK-DTOR: call {{.*}} @_ZN13DestructorBCEC1Ei({{.*}} @global_dtor_bce_2, i32
158 // CHECK-DTOR: atexit{{.*}} @_ZN13DestructorBCED{{.*}} @global_dtor_bce_2
159 // CHECK-DTOR: }
160 DestructorBCE global_dtor_bce_2(not_constexpr);
161 
162 // CHECK-DTOR-NOT: @_ZN13DestructorBCED{{.*}}@global_dtor_bce_3
163 constexpr DestructorBCE global_dtor_bce_3(103);
164 
165 // CHECK-DTOR-LABEL: define {{.*}} @_Z15test_dtor_bce_1v(
test_dtor_bce_1()166 void test_dtor_bce_1() {
167   // Variable is neither constant initialized (because it has automatic storage
168   // duration) nor usable in constant expressions, so BCE should not return
169   // true during destruction. It would be OK if we replaced the constructor
170   // call with a direct store, but we should emit the destructor call.
171 
172   // CHECK-DTOR: call {{.*}} @_ZN13DestructorBCEC1Ei({{.*}}, i32 201)
173   DestructorBCE local(201);
174   // CHECK-DTOR: call {{.*}} @_ZN13DestructorBCED
175   // CHECK-DTOR: }
176 }
177 
178 // CHECK-DTOR-LABEL: define {{.*}} @_Z15test_dtor_bce_2v(
test_dtor_bce_2()179 void test_dtor_bce_2() {
180   // Non-constant init => BCE is false in destructor.
181 
182   // CHECK-DTOR: call {{.*}} @_ZN13DestructorBCEC1Ei({{.*}}
183   DestructorBCE local(not_constexpr);
184   // CHECK-DTOR: call {{.*}} @_ZN13DestructorBCED
185   // CHECK-DTOR: }
186 }
187 
188 // CHECK-DTOR-LABEL: define {{.*}} @_Z15test_dtor_bce_3v(
test_dtor_bce_3()189 void test_dtor_bce_3() {
190   // Should never call dtor for a constexpr variable.
191 
192   // CHECK-DTOR-NOT: call {{.*}} @_ZN13DestructorBCEC1Ei(
193   constexpr DestructorBCE local(203);
194   // CHECK-DTOR-NOT: @_ZN13DestructorBCED
195   // CHECK-DTOR: }
196 }
197 
198 // CHECK-DTOR-LABEL: define {{.*}} @_Z22test_dtor_bce_static_1v(
test_dtor_bce_static_1()199 void test_dtor_bce_static_1() {
200   // Variable is constant initialized, so BCE returns true during constant
201   // destruction.
202 
203   // CHECK: store i32 301
204   // CHECK-DTOR-NOT: @_ZN13DestructorBCEC1Ei({{.*}}
205   static DestructorBCE local(301);
206   // CHECK-DTOR-NOT: @_ZN13DestructorBCED
207   // CHECK-DTOR: }
208 }
209 
210 // CHECK-DTOR-LABEL: define {{.*}} @_Z22test_dtor_bce_static_2v(
test_dtor_bce_static_2()211 void test_dtor_bce_static_2() {
212   // CHECK-DTOR: call {{.*}} @_ZN13DestructorBCEC1Ei({{.*}}
213   static DestructorBCE local(not_constexpr);
214   // CHECK-DTOR: call {{.*}}atexit{{.*}} @_ZN13DestructorBCED
215   // CHECK-DTOR: }
216 }
217 
218 // CHECK-DTOR-LABEL: define {{.*}} @_Z22test_dtor_bce_static_3v(
test_dtor_bce_static_3()219 void test_dtor_bce_static_3() {
220   // CHECK: store i32 303
221   // CHECK-DTOR-NOT: @_ZN13DestructorBCEC1Ei({{.*}}
222   static constexpr DestructorBCE local(303);
223   // CHECK-DTOR-NOT: @_ZN13DestructorBCED
224   // CHECK-DTOR: }
225 }
226