mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-02-03 07:38:57 +00:00
Properly track whether a variable is constant-initialized.
This fixes miscomputation of __builtin_constant_evaluated in the initializer of a variable that's not usable in constant expressions, but is readable when constant-folding. If evaluation of a constant initializer fails, we throw away the evaluated result instead of keeping it as a non-constant-initializer value for the variable, because it might not be a correct value. To avoid regressions for initializers that are foldable but not formally constant initializers, we now try constant-evaluating some globals in C++ twice: once to check for a constant initializer (in an mode where is_constannt_evaluated returns true) and again to determine the runtime value if the initializer is not a constant initializer.
This commit is contained in:
parent
1329944c22
commit
08c8d5bc51
@ -1273,12 +1273,15 @@ public:
|
||||
EvaluatedStmt *getEvaluatedStmt() const;
|
||||
|
||||
/// Attempt to evaluate the value of the initializer attached to this
|
||||
/// declaration, and produce notes explaining why it cannot be evaluated or is
|
||||
/// not a constant expression. Returns a pointer to the value if evaluation
|
||||
/// succeeded, 0 otherwise.
|
||||
/// declaration, and produce notes explaining why it cannot be evaluated.
|
||||
/// Returns a pointer to the value if evaluation succeeded, 0 otherwise.
|
||||
APValue *evaluateValue() const;
|
||||
APValue *evaluateValue(SmallVectorImpl<PartialDiagnosticAt> &Notes) const;
|
||||
|
||||
private:
|
||||
APValue *evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes,
|
||||
bool IsConstantInitialization) const;
|
||||
|
||||
public:
|
||||
/// Return the already-evaluated value of this variable's
|
||||
/// initializer, or NULL if the value is not yet known. Returns pointer
|
||||
/// to untyped APValue if the value could not be evaluated.
|
||||
|
@ -699,7 +699,8 @@ public:
|
||||
/// notes will be produced if the expression is not a constant expression.
|
||||
bool EvaluateAsInitializer(APValue &Result, const ASTContext &Ctx,
|
||||
const VarDecl *VD,
|
||||
SmallVectorImpl<PartialDiagnosticAt> &Notes) const;
|
||||
SmallVectorImpl<PartialDiagnosticAt> &Notes,
|
||||
bool IsConstantInitializer) const;
|
||||
|
||||
/// EvaluateWithSubstitution - Evaluate an expression as if from the context
|
||||
/// of a call to the given function with the given arguments, inside an
|
||||
|
@ -2360,11 +2360,11 @@ EvaluatedStmt *VarDecl::getEvaluatedStmt() const {
|
||||
|
||||
APValue *VarDecl::evaluateValue() const {
|
||||
SmallVector<PartialDiagnosticAt, 8> Notes;
|
||||
return evaluateValue(Notes);
|
||||
return evaluateValueImpl(Notes, hasConstantInitialization());
|
||||
}
|
||||
|
||||
APValue *VarDecl::evaluateValue(
|
||||
SmallVectorImpl<PartialDiagnosticAt> &Notes) const {
|
||||
APValue *VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes,
|
||||
bool IsConstantInitialization) const {
|
||||
EvaluatedStmt *Eval = ensureEvaluatedStmt();
|
||||
|
||||
const auto *Init = cast<Expr>(Eval->Value);
|
||||
@ -2383,8 +2383,16 @@ APValue *VarDecl::evaluateValue(
|
||||
|
||||
Eval->IsEvaluating = true;
|
||||
|
||||
bool Result = Init->EvaluateAsInitializer(Eval->Evaluated, getASTContext(),
|
||||
this, Notes);
|
||||
ASTContext &Ctx = getASTContext();
|
||||
bool Result = Init->EvaluateAsInitializer(Eval->Evaluated, Ctx, this, Notes,
|
||||
IsConstantInitialization);
|
||||
|
||||
// In C++11, this isn't a constant initializer if we produced notes. In that
|
||||
// case, we can't keep the result, because it may only be correct under the
|
||||
// assumption that the initializer is a constant context.
|
||||
if (IsConstantInitialization && Ctx.getLangOpts().CPlusPlus11 &&
|
||||
!Notes.empty())
|
||||
Result = false;
|
||||
|
||||
// Ensure the computed APValue is cleaned up later if evaluation succeeded,
|
||||
// or that it's empty (so that there's nothing to clean up) if evaluation
|
||||
@ -2392,7 +2400,7 @@ APValue *VarDecl::evaluateValue(
|
||||
if (!Result)
|
||||
Eval->Evaluated = APValue();
|
||||
else if (Eval->Evaluated.needsCleanup())
|
||||
getASTContext().addDestruction(&Eval->Evaluated);
|
||||
Ctx.addDestruction(&Eval->Evaluated);
|
||||
|
||||
Eval->IsEvaluating = false;
|
||||
Eval->WasEvaluated = true;
|
||||
@ -2447,7 +2455,14 @@ bool VarDecl::checkForConstantInitialization(
|
||||
assert(!Init->isValueDependent());
|
||||
|
||||
// Evaluate the initializer to check whether it's a constant expression.
|
||||
Eval->HasConstantInitialization = evaluateValue(Notes) && Notes.empty();
|
||||
Eval->HasConstantInitialization =
|
||||
evaluateValueImpl(Notes, true) && Notes.empty();
|
||||
|
||||
// If evaluation as a constant initializer failed, allow re-evaluation as a
|
||||
// non-constant initializer if we later find we want the value.
|
||||
if (!Eval->HasConstantInitialization)
|
||||
Eval->WasEvaluated = false;
|
||||
|
||||
return Eval->HasConstantInitialization;
|
||||
}
|
||||
|
||||
|
@ -3269,12 +3269,9 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E,
|
||||
|
||||
// Check that we can fold the initializer. In C++, we will have already done
|
||||
// this in the cases where it matters for conformance.
|
||||
SmallVector<PartialDiagnosticAt, 8> Notes;
|
||||
if (!VD->evaluateValue(Notes)) {
|
||||
Info.FFDiag(E, diag::note_constexpr_var_init_non_constant,
|
||||
Notes.size() + 1) << VD;
|
||||
if (!VD->evaluateValue()) {
|
||||
Info.FFDiag(E, diag::note_constexpr_var_init_non_constant, 1) << VD;
|
||||
NoteLValueLocation(Info, Base);
|
||||
Info.addNotes(Notes);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -14687,7 +14684,8 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, ConstExprUsage Usage,
|
||||
|
||||
bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
|
||||
const VarDecl *VD,
|
||||
SmallVectorImpl<PartialDiagnosticAt> &Notes) const {
|
||||
SmallVectorImpl<PartialDiagnosticAt> &Notes,
|
||||
bool IsConstantInitialization) const {
|
||||
assert(!isValueDependent() &&
|
||||
"Expression evaluator can't be called on a dependent expression.");
|
||||
|
||||
@ -14700,11 +14698,12 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
|
||||
Expr::EvalStatus EStatus;
|
||||
EStatus.Diag = &Notes;
|
||||
|
||||
EvalInfo Info(Ctx, EStatus, VD->isConstexpr()
|
||||
? EvalInfo::EM_ConstantExpression
|
||||
: EvalInfo::EM_ConstantFold);
|
||||
EvalInfo Info(Ctx, EStatus,
|
||||
(IsConstantInitialization && Ctx.getLangOpts().CPlusPlus11)
|
||||
? EvalInfo::EM_ConstantExpression
|
||||
: EvalInfo::EM_ConstantFold);
|
||||
Info.setEvaluatingDecl(VD, Value);
|
||||
Info.InConstantContext = true;
|
||||
Info.InConstantContext = IsConstantInitialization;
|
||||
|
||||
SourceLocation DeclLoc = VD->getLocation();
|
||||
QualType DeclTy = VD->getType();
|
||||
|
@ -1622,8 +1622,8 @@ llvm::Constant *ConstantEmitter::tryEmitPrivateForVarInit(const VarDecl &D) {
|
||||
if (CD->isTrivial() && CD->isDefaultConstructor())
|
||||
return CGM.EmitNullConstant(D.getType());
|
||||
}
|
||||
InConstantContext = true;
|
||||
}
|
||||
InConstantContext = D.hasConstantInitialization();
|
||||
|
||||
QualType destType = D.getType();
|
||||
|
||||
|
@ -63,9 +63,9 @@ CONSTINIT int b = __builtin_is_constant_evaluated() ? 2 : y; // static initializ
|
||||
// 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
|
||||
// This is dynamic initialization that we can convert to static initialization
|
||||
// during lowering. When doing so, the dynamic initializer value is preserved.
|
||||
// CHECK-STATIC-DAG: @_ZL1a = internal constant i32 1
|
||||
const int a = __builtin_is_constant_evaluated() ? y : 1; // dynamic initialization to 1
|
||||
const int *a_sink = &a;
|
||||
|
||||
|
67
clang/test/PCH/builtin-is-constant-evaluated.cpp
Normal file
67
clang/test/PCH/builtin-is-constant-evaluated.cpp
Normal file
@ -0,0 +1,67 @@
|
||||
// RUN: %clang_cc1 -std=c++98 -Wno-constant-evaluated -triple x86_64-linux -include %s -verify %s -emit-llvm -o - | FileCheck %s
|
||||
// RUN: %clang_cc1 -std=c++98 -Wno-constant-evaluated -triple x86_64-linux -emit-pch %s -o %t
|
||||
// RUN: %clang_cc1 -std=c++98 -Wno-constant-evaluated -triple x86_64-linux -include-pch %t -verify %s -emit-llvm -o - | FileCheck %s
|
||||
|
||||
// RUN: %clang_cc1 -std=c++11 -Wno-constant-evaluated -triple x86_64-linux -include %s -verify %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,CXX11
|
||||
// RUN: %clang_cc1 -std=c++11 -Wno-constant-evaluated -triple x86_64-linux -emit-pch %s -o %t-cxx11
|
||||
// RUN: %clang_cc1 -std=c++11 -Wno-constant-evaluated -triple x86_64-linux -include-pch %t-cxx11 -verify %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,CXX11
|
||||
|
||||
// RUN: %clang_cc1 -std=c++20 -Wno-constant-evaluated -triple x86_64-linux -include %s -verify %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,CXX11
|
||||
// RUN: %clang_cc1 -std=c++20 -Wno-constant-evaluated -triple x86_64-linux -emit-pch %s -o %t-cxx11
|
||||
// RUN: %clang_cc1 -std=c++20 -Wno-constant-evaluated -triple x86_64-linux -include-pch %t-cxx11 -verify %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,CXX11
|
||||
|
||||
// expected-no-diagnostics
|
||||
|
||||
#ifndef HEADER_INCLUDED
|
||||
#define HEADER_INCLUDED
|
||||
|
||||
// CHECK-DAG: @a = global i8 1,
|
||||
// CHECK-DAG: @b = constant i8 1,
|
||||
// CXX11-DAG: @c = constant i8 1,
|
||||
// CHECK-DAG: @d = global float 1.000000e+00
|
||||
// CHECK-DAG: @e = constant float 1.000000e+00
|
||||
|
||||
bool a = __builtin_is_constant_evaluated();
|
||||
extern const bool b = __builtin_is_constant_evaluated();
|
||||
#if __cplusplus >= 201103L
|
||||
extern constexpr bool c = __builtin_is_constant_evaluated();
|
||||
#endif
|
||||
float d = __builtin_is_constant_evaluated();
|
||||
extern const float e = __builtin_is_constant_evaluated();
|
||||
|
||||
void g(...);
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @_Z1fv(
|
||||
// CHECK: store i8 0, i8* %[[A:.*]],
|
||||
// CHECK: store i8 1, i8* %[[B:.*]],
|
||||
// CXX11: store i8 1, i8* %[[C:.*]],
|
||||
// CHECK: store float 0.000000e+00, float* %[[D:.*]],
|
||||
// CHECK: store float 0.000000e+00, float* %[[E:.*]],
|
||||
// CHECK: load i8, i8* %[[A]],
|
||||
// CHECK: call {{.*}} @_Z1gz(i32 %{{[^,]+}}, i32 1
|
||||
// CXX11-SAME: , i32 1
|
||||
// CHECK-SAME: , double %{{[^,]+}}, double 0.000000e+00)
|
||||
void f() {
|
||||
bool a = __builtin_is_constant_evaluated();
|
||||
const bool b = __builtin_is_constant_evaluated();
|
||||
#if __cplusplus >= 201103L
|
||||
constexpr bool c = __builtin_is_constant_evaluated();
|
||||
#endif
|
||||
float d = __builtin_is_constant_evaluated();
|
||||
const float e = __builtin_is_constant_evaluated();
|
||||
g(a, b
|
||||
#if __cplusplus >= 201103L
|
||||
, c
|
||||
#endif
|
||||
, d, e);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
_Static_assert(b, "");
|
||||
#if __cplusplus >= 201103L
|
||||
static_assert(c, "");
|
||||
#endif
|
||||
_Static_assert(__builtin_constant_p(1) ? e == 1.0f : false, "");
|
||||
|
||||
#endif
|
@ -152,7 +152,7 @@ void test_basic_start_static_2_2() {
|
||||
#else
|
||||
ATTR static PODType pod; // expected-error {{variable does not have a constant initializer}}
|
||||
// expected-note@-1 {{required by 'require_constant_initialization' attribute here}}
|
||||
// expected-note@-2 {{subobject of type 'int' is not initialized}}
|
||||
// expected-note-re@-2 {{{{non-constexpr constructor|subobject of type 'int' is not initialized}}}}
|
||||
#endif
|
||||
ATTR static PODType pot2 = {ReturnInt()}; // expected-error {{variable does not have a constant initializer}}
|
||||
// expected-note@-1 {{required by 'require_constant_initialization' attribute here}}
|
||||
@ -191,7 +191,7 @@ struct TT2 {
|
||||
PODType TT2::pod_noinit; // expected-note 0+ {{declared here}}
|
||||
#if __cplusplus >= 201103L
|
||||
// expected-error@-2 {{variable does not have a constant initializer}}
|
||||
// expected-note@-3 {{subobject of type 'int' is not initialized}}
|
||||
// expected-note-re@-3 {{{{non-constexpr constructor|subobject of type 'int' is not initialized}}}}
|
||||
#endif
|
||||
PODType TT2::pod_copy_init(TT2::pod_noinit); // expected-error {{variable does not have a constant initializer}}
|
||||
#if __cplusplus >= 201103L
|
||||
|
@ -1,5 +1,7 @@
|
||||
// RUN: %clang_cc1 -std=c++2a -verify %s -fcxx-exceptions -Wno-constant-evaluated -triple=x86_64-linux-gnu
|
||||
|
||||
#define fold(x) (__builtin_constant_p(x) ? (x) : (x))
|
||||
|
||||
using size_t = decltype(sizeof(int));
|
||||
|
||||
namespace std {
|
||||
@ -119,3 +121,25 @@ struct TestConditionalExplicit {
|
||||
};
|
||||
TestConditionalExplicit e = 42;
|
||||
#endif
|
||||
|
||||
namespace fold_initializer {
|
||||
// Global 'f' has a constant initializer.
|
||||
const float f = __builtin_is_constant_evaluated();
|
||||
static_assert(fold(f == 1.0f));
|
||||
|
||||
void g() {
|
||||
// Local static 'sf' has a constant initializer.
|
||||
static const float sf = __builtin_is_constant_evaluated();
|
||||
static_assert(fold(sf == 1.0f));
|
||||
|
||||
// Local non-static 'f' has a non-constant initializer.
|
||||
const float f = __builtin_is_constant_evaluated();
|
||||
static_assert(fold(f == 0.0f));
|
||||
}
|
||||
|
||||
struct A {
|
||||
static const float f;
|
||||
};
|
||||
const float A::f = __builtin_is_constant_evaluated();
|
||||
static_assert(fold(A::f == 1.0f));
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user