From 2a81f6670db3cb78d65e0a05ff6a7f0b114fd359 Mon Sep 17 00:00:00 2001 From: Bill Wendling Date: Sat, 1 Dec 2018 08:29:36 +0000 Subject: [PATCH] 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 --- clang/lib/AST/ExprConstant.cpp | 1 + clang/lib/CodeGen/CGExprConstant.cpp | 3 +- clang/lib/CodeGen/ConstantEmitter.h | 3 + clang/test/Analysis/builtin-functions.cpp | 6 +- clang/test/CodeGen/builtin-constant-p.c | 168 ++++++++++++++++++++++ 5 files changed, 177 insertions(+), 4 deletions(-) create mode 100644 clang/test/CodeGen/builtin-constant-p.c diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 91b3fbfa4663..5eb2f2e7b84b 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -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); } diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp index eb5e01096843..519b206ad379 100644 --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -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) diff --git a/clang/lib/CodeGen/ConstantEmitter.h b/clang/lib/CodeGen/ConstantEmitter.h index b4d1b65743c7..7ad8e5d37cd1 100644 --- a/clang/lib/CodeGen/ConstantEmitter.h +++ b/clang/lib/CodeGen/ConstantEmitter.h @@ -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; diff --git a/clang/test/Analysis/builtin-functions.cpp b/clang/test/Analysis/builtin-functions.cpp index 19984963b4fa..da2fcf915d31 100644 --- a/clang/test/Analysis/builtin-functions.cpp +++ b/clang/test/Analysis/builtin-functions.cpp @@ -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}} } diff --git a/clang/test/CodeGen/builtin-constant-p.c b/clang/test/CodeGen/builtin-constant-p.c new file mode 100644 index 000000000000..7f4e7d07cc66 --- /dev/null +++ b/clang/test/CodeGen/builtin-constant-p.c @@ -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) };