Specify constant context in constant emitter

The constant emitter may need to evaluate the expression in a constant context.
For exasmple, global initializer lists.

llvm-svn: 348070
This commit is contained in:
Bill Wendling 2018-12-01 08:29:36 +00:00
parent 2d6324c3cb
commit 2a81f6670d
5 changed files with 177 additions and 4 deletions

View File

@ -8199,6 +8199,7 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
// We can delay calculation of __builtin_constant_p until after
// inlining. Note: This diagnostic won't be shown to the user.
Info.FFDiag(E, diag::note_invalid_subexpr_in_const_expr);
return false;
}
return Success(false, E);
}

View File

@ -1455,6 +1455,7 @@ llvm::Constant *ConstantEmitter::tryEmitPrivateForVarInit(const VarDecl &D) {
if (CD->isTrivial() && CD->isDefaultConstructor())
return CGM.EmitNullConstant(D.getType());
}
InConstantContext = true;
}
QualType destType = D.getType();
@ -1552,7 +1553,7 @@ llvm::Constant *ConstantEmitter::tryEmitPrivate(const Expr *E,
if (destType->isReferenceType())
Success = E->EvaluateAsLValue(Result, CGM.getContext());
else
Success = E->EvaluateAsRValue(Result, CGM.getContext());
Success = E->EvaluateAsRValue(Result, CGM.getContext(), InConstantContext);
llvm::Constant *C;
if (Success && !Result.HasSideEffects)

View File

@ -38,6 +38,9 @@ private:
/// Whether the constant-emission failed.
bool Failed = false;
/// Whether we're in a constant context.
bool InConstantContext = false;
/// The AST address space where this (non-abstract) initializer is going.
/// Used for generating appropriate placeholders.
LangAS DestAddressSpace;

View File

@ -70,14 +70,14 @@ void test_constant_p() {
const int j = 2;
constexpr int k = 3;
clang_analyzer_eval(__builtin_constant_p(42) == 1); // expected-warning {{TRUE}}
clang_analyzer_eval(__builtin_constant_p(i) == 0); // expected-warning {{TRUE}}
clang_analyzer_eval(__builtin_constant_p(i) == 0); // expected-warning {{UNKNOWN}}
clang_analyzer_eval(__builtin_constant_p(j) == 1); // expected-warning {{TRUE}}
clang_analyzer_eval(__builtin_constant_p(k) == 1); // expected-warning {{TRUE}}
clang_analyzer_eval(__builtin_constant_p(i + 42) == 0); // expected-warning {{TRUE}}
clang_analyzer_eval(__builtin_constant_p(i + 42) == 0); // expected-warning {{UNKNOWN}}
clang_analyzer_eval(__builtin_constant_p(j + 42) == 1); // expected-warning {{TRUE}}
clang_analyzer_eval(__builtin_constant_p(k + 42) == 1); // expected-warning {{TRUE}}
clang_analyzer_eval(__builtin_constant_p(" ") == 1); // expected-warning {{TRUE}}
clang_analyzer_eval(__builtin_constant_p(test_constant_p) == 0); // expected-warning {{TRUE}}
clang_analyzer_eval(__builtin_constant_p(test_constant_p) == 0); // expected-warning {{UNKNOWN}}
clang_analyzer_eval(__builtin_constant_p(k - 3) == 0); // expected-warning {{FALSE}}
clang_analyzer_eval(__builtin_constant_p(k - 3) == 1); // expected-warning {{TRUE}}
}

View File

@ -0,0 +1,168 @@
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -o - %s -O2 | FileCheck %s
int a = 42;
inline int bcp(int x) {
return __builtin_constant_p(x);
}
/* --- Compound literals */
struct foo { int x, y; };
int y;
struct foo f = (struct foo){ __builtin_constant_p(y), 42 };
struct foo test0(int expr) {
// CHECK: define i64 @test0(i32 %expr)
// CHECK: call i1 @llvm.is.constant.i32(i32 %expr)
struct foo f = (struct foo){ __builtin_constant_p(expr), 42 };
return f;
}
/* --- Pointer types */
inline int test1_i(int *x) {
return *x;
}
int test1() {
// CHECK: define i32 @test1
// CHECK: add nsw i32 %0, -13
// CHECK-NEXT: call i1 @llvm.is.constant.i32(i32 %sub)
return bcp(test1_i(&a) - 13);
}
int test2() {
// CHECK: define i32 @test2
// CHECK: ret i32 0
return __builtin_constant_p(&a - 13);
}
inline int test3_i(int *x) {
return 42;
}
int test3() {
// CHECK: define i32 @test3
// CHECK: ret i32 1
return bcp(test3_i(&a) - 13);
}
/* --- Aggregate types */
int b[] = {1, 2, 3};
int test4() {
// CHECK: define i32 @test4
// CHECK: ret i32 0
return __builtin_constant_p(b);
}
const char test5_c[] = {1, 2, 3, 0};
int test5() {
// CHECK: define i32 @test5
// CHECK: ret i32 0
return __builtin_constant_p(test5_c);
}
inline char test6_i(const char *x) {
return x[1];
}
int test6() {
// CHECK: define i32 @test6
// CHECK: ret i32 0
return __builtin_constant_p(test6_i(test5_c));
}
/* --- Non-constant global variables */
int test7() {
// CHECK: define i32 @test7
// CHECK: call i1 @llvm.is.constant.i32(i32 %0)
return bcp(a);
}
/* --- Constant global variables */
const int c = 42;
int test8() {
// CHECK: define i32 @test8
// CHECK: ret i32 1
return bcp(c);
}
/* --- Array types */
int arr[] = { 1, 2, 3 };
const int c_arr[] = { 1, 2, 3 };
int test9() {
// CHECK: define i32 @test9
// CHECK: call i1 @llvm.is.constant.i32(i32 %0)
return __builtin_constant_p(arr[2]);
}
int test10() {
// CHECK: define i32 @test10
// CHECK: ret i32 1
return __builtin_constant_p(c_arr[2]);
}
int test11() {
// CHECK: define i32 @test11
// CHECK: ret i32 0
return __builtin_constant_p(c_arr);
}
/* --- Function pointers */
int test12() {
// CHECK: define i32 @test12
// CHECK: ret i32 0
return __builtin_constant_p(&test10);
}
int test13() {
// CHECK: define i32 @test13
// CHECK: ret i32 1
return __builtin_constant_p(&test10 != 0);
}
typedef unsigned long uintptr_t;
#define assign(p, v) ({ \
uintptr_t _r_a_p__v = (uintptr_t)(v); \
if (__builtin_constant_p(v) && _r_a_p__v == (uintptr_t)0) { \
union { \
uintptr_t __val; \
char __c[1]; \
} __u = { \
.__val = (uintptr_t)_r_a_p__v \
}; \
*(volatile unsigned int*)&p = *(unsigned int*)(__u.__c); \
__u.__val; \
} \
_r_a_p__v; \
})
typedef void fn_p(void);
extern fn_p *dest_p;
static void src_fn(void) {
}
void test14() {
assign(dest_p, src_fn);
}
extern int test15_v;
struct { const char *t; int a; } test15[] = {
{ "tag", __builtin_constant_p(test15_v) && !test15_v ? 1 : 0 }
};
extern char test16_v;
struct { int a; } test16 = { __builtin_constant_p(test16_v) };