]> granicus.if.org Git - clang/commitdiff
When evaluating a VarDecl as a constant or determining whether it is
authorDouglas Gregor <dgregor@apple.com>
Tue, 26 May 2009 18:54:04 +0000 (18:54 +0000)
committerDouglas Gregor <dgregor@apple.com>
Tue, 26 May 2009 18:54:04 +0000 (18:54 +0000)
an integral constant expression, maintain a cache of the value and the
is-an-ICE flag within the VarDecl itself. This eliminates
exponential-time behavior of the Fibonacci template metaprogram.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@72428 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/Decl.h
lib/AST/Decl.cpp
lib/AST/Expr.cpp
lib/AST/ExprConstant.cpp
lib/AST/StmtIterator.cpp
lib/Frontend/PCHReaderDecl.cpp
lib/Sema/SemaDecl.cpp
lib/Sema/SemaDeclCXX.cpp

index 4c4763f48ea64627fff154c03ad2c87103a98ae0..a30cf0fbc50ba287b547223a1ab6737ec1367906 100644 (file)
@@ -14,6 +14,7 @@
 #ifndef LLVM_CLANG_AST_DECL_H
 #define LLVM_CLANG_AST_DECL_H
 
+#include "clang/AST/APValue.h"
 #include "clang/AST/DeclBase.h"
 #include "clang/AST/DeclarationName.h"
 #include "clang/AST/ExternalASTSource.h"
@@ -186,6 +187,27 @@ public:
   static bool classof(const ValueDecl *D) { return true; }
 };
 
+/// \brief Structure used to store a statement, the constant value to
+/// which it was evaluated (if any), and whether or not the statement
+/// is an integral constant expression (if known).
+struct EvaluatedStmt {
+  EvaluatedStmt() : WasEvaluated(false), CheckedICE(false), IsICE(false) { }
+
+  /// \brief Whether this statement was already evaluated.
+  bool WasEvaluated : 1;
+
+  /// \brief Whether we already checked whether this statement was an
+  /// integral constant expression.
+  bool CheckedICE : 1;
+
+  /// \brief Whether this statement is an integral constant
+  /// expression. Only valid if CheckedICE is true.
+  bool IsICE : 1;
+
+  Stmt *Value;
+  APValue Evaluated;
+};
+
 /// VarDecl - An instance of this class is created to represent a variable
 /// declaration or definition.
 class VarDecl : public ValueDecl {
@@ -201,7 +223,7 @@ public:
   static const char *getStorageClassSpecifierString(StorageClass SC);
 
 private:
-  Stmt *Init;
+  mutable llvm::PointerUnion<Stmt *, EvaluatedStmt *> Init;
   // FIXME: This can be packed into the bitfields in Decl.
   unsigned SClass : 3;
   bool ThreadSpecified : 1;
@@ -220,7 +242,7 @@ private:
 protected:
   VarDecl(Kind DK, DeclContext *DC, SourceLocation L, IdentifierInfo *Id,
           QualType T, StorageClass SC, SourceLocation TSSL = SourceLocation())
-    : ValueDecl(DK, DC, L, Id, T), Init(0),
+    : ValueDecl(DK, DC, L, Id, T), Init(),
       ThreadSpecified(false), HasCXXDirectInit(false),
       DeclaredInCondition(false), PreviousDeclaration(0), 
       TypeSpecStartLoc(TSSL) { 
@@ -243,10 +265,95 @@ public:
     TypeSpecStartLoc = SL;
   }
 
-  const Expr *getInit() const { return (const Expr*) Init; }
-  Expr *getInit() { return (Expr*) Init; }
-  void setInit(Expr *I) { Init = (Stmt*) I; }
-      
+  const Expr *getInit() const { 
+    if (Init.isNull())
+      return 0;
+
+    const Stmt *S = Init.dyn_cast<Stmt *>();
+    if (!S)
+      S = Init.get<EvaluatedStmt *>()->Value;
+
+    return (const Expr*) S; 
+  }
+  Expr *getInit() { 
+    if (Init.isNull())
+      return 0;
+
+    Stmt *S = Init.dyn_cast<Stmt *>();
+    if (!S)
+      S = Init.get<EvaluatedStmt *>()->Value;
+
+    return (Expr*) S; 
+  }
+
+  /// \brief Retrieve the address of the initializer expression.
+  Stmt **getInitAddress() {
+    if (Init.is<Stmt *>())
+      return reinterpret_cast<Stmt **>(&Init); // FIXME: ugly hack
+    return &Init.get<EvaluatedStmt *>()->Value;
+  }
+
+  void setInit(ASTContext &C, Expr *I);
+   
+  /// \brief Note that constant evaluation has computed the given
+  /// value for this variable's initializer.
+  void setEvaluatedValue(ASTContext &C, const APValue &Value) const {
+    EvaluatedStmt *Eval = Init.dyn_cast<EvaluatedStmt *>();
+    if (!Eval) {
+      Stmt *S = Init.get<Stmt *>();
+      Eval = new (C) EvaluatedStmt;
+      Eval->Value = S;
+      Init = Eval;
+    }
+
+    Eval->WasEvaluated = true;
+    Eval->Evaluated = Value;
+  }
+   
+  /// \brief Return the already-evaluated value of this variable's
+  /// initializer, or NULL if the value is not yet known.
+  APValue *getEvaluatedValue() const {
+    if (EvaluatedStmt *Eval = Init.dyn_cast<EvaluatedStmt *>())
+      if (Eval->WasEvaluated)
+        return &Eval->Evaluated;
+
+    return 0;
+  }
+
+  /// \brief Determines whether it is already known whether the
+  /// initializer is an integral constant expression or not.
+  bool isInitKnownICE() const {
+    if (EvaluatedStmt *Eval = Init.dyn_cast<EvaluatedStmt *>())
+      return Eval->CheckedICE;
+
+    return false;
+  }
+
+  /// \brief Determines whether the initializer is an integral
+  /// constant expression.
+  ///
+  /// \pre isInitKnownICE()
+  bool isInitICE() const {
+    assert(isInitKnownICE() && 
+           "Check whether we already know that the initializer is an ICE");
+    return Init.get<EvaluatedStmt *>()->IsICE;
+  }
+
+  /// \brief Note that we now know whether the initializer is an
+  /// integral constant expression.
+  void setInitKnownICE(ASTContext &C, bool IsICE) const {
+    EvaluatedStmt *Eval = Init.dyn_cast<EvaluatedStmt *>();
+    if (!Eval) {
+      Stmt *S = Init.get<Stmt *>();
+      Eval = new (C) EvaluatedStmt;
+      Eval->Value = S;
+      Init = Eval;
+    }
+
+    Eval->CheckedICE = true;
+    Eval->IsICE = IsICE;
+  }
+
   /// \brief Retrieve the definition of this variable, which may come
   /// from a previous declaration. Def will be set to the VarDecl that
   /// contains the initializer, and the result will be that
index 20fe39d0af5ab4f8c43785952ee196cbb8589c9f..6c620713f763d4162d8e9e4d40e20d18cc3bba0d 100644 (file)
@@ -89,6 +89,15 @@ QualType ParmVarDecl::getOriginalType() const {
   return getType();
 }
 
+void VarDecl::setInit(ASTContext &C, Expr *I) { 
+    if (EvaluatedStmt *Eval = Init.dyn_cast<EvaluatedStmt *>()) {
+      Eval->~EvaluatedStmt();
+      C.Deallocate(Eval);
+    }
+
+    Init = I;
+  }
+
 bool VarDecl::isExternC(ASTContext &Context) const {
   if (!Context.getLangOptions().CPlusPlus)
     return (getDeclContext()->isTranslationUnit() && 
@@ -287,8 +296,13 @@ VarDecl *VarDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L,
 
 void VarDecl::Destroy(ASTContext& C) {
   Expr *Init = getInit();
-  if (Init)
+  if (Init) {
     Init->Destroy(C);
+    if (EvaluatedStmt *Eval = this->Init.dyn_cast<EvaluatedStmt *>()) {
+      Eval->~EvaluatedStmt();
+      C.Deallocate(Eval);
+    }
+  }
   this->~VarDecl();
   C.Deallocate((void *)this);
 }
index 6711faffe7476790e5712c7f42f55c939654bcb5..aca5efeb16370077ecb44ae327310d07c8d0d92f 100644 (file)
@@ -1210,8 +1210,21 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) {
       //   type initialized by an ICE can be used in ICEs.
       if (const VarDecl *Dcl =
               dyn_cast<VarDecl>(cast<DeclRefExpr>(E)->getDecl())) {
-        if (const Expr *Init = Dcl->getInit())
-          return CheckICE(Init, Ctx);
+        if (Dcl->isInitKnownICE()) {
+          // We have already checked whether this subexpression is an
+          // integral constant expression.
+          if (Dcl->isInitICE())
+            return NoDiag();
+          else
+            return ICEDiag(2, E->getLocStart());
+        }
+
+        if (const Expr *Init = Dcl->getInit()) {
+          ICEDiag Result = CheckICE(Init, Ctx);
+          // Cache the result of the ICE test.
+          Dcl->setInitKnownICE(Ctx, Result.Val == 0);
+          return Result;
+        }
       }
     }
     return ICEDiag(2, E->getLocStart());
index 34b018797086006e3fc7036debe0ae03e866e0a0..7651884aa60974395ff04efcd2f247a499d07f2b 100644 (file)
@@ -700,8 +700,17 @@ bool IntExprEvaluator::VisitDeclRefExpr(const DeclRefExpr *E) {
   // In C, they can also be folded, although they are not ICEs.
   if (E->getType().getCVRQualifiers() == QualType::Const) {
     if (const VarDecl *D = dyn_cast<VarDecl>(E->getDecl())) {
-      if (const Expr *Init = D->getInit())
-        return Visit(const_cast<Expr*>(Init));
+      if (APValue *V = D->getEvaluatedValue())
+        return Success(V->getInt(), E);
+      if (const Expr *Init = D->getInit()) {
+        if (Visit(const_cast<Expr*>(Init))) {
+          // Cache the evaluated value in the variable declaration.
+          D->setEvaluatedValue(Info.Ctx, Result);
+          return true;
+        }
+
+        return false;
+      }
     }
   }
 
index 20024f513ffcea6228c745231b8d71149ae9db8f..5c22e28894f9ea849f5f54c5f5db564a7a9c9491 100644 (file)
@@ -140,14 +140,14 @@ Stmt*& StmtIteratorBase::GetDeclExpr() const {
   
   if (inDeclGroup()) {
     VarDecl* VD = cast<VarDecl>(*DGI);
-    return VD->Init;
+    return *VD->getInitAddress();
   }
   
   assert (inDecl());
 
   if (VarDecl* VD = dyn_cast<VarDecl>(decl)) {
     assert (VD->Init);
-    return VD->Init;
+    return *VD->getInitAddress();
   }
 
   EnumConstantDecl* ECD = cast<EnumConstantDecl>(decl);
index 7d4c63419326e71682b59f89a50ebf7d882e11aa..adf0d1155e23899b399c5dd97a5bdf930398b2f9 100644 (file)
@@ -342,7 +342,7 @@ void PCHDeclReader::VisitVarDecl(VarDecl *VD) {
                          cast_or_null<VarDecl>(Reader.GetDecl(Record[Idx++])));
   VD->setTypeSpecStartLoc(SourceLocation::getFromRawEncoding(Record[Idx++]));
   if (Record[Idx++])
-    VD->setInit(Reader.ReadDeclExpr());
+    VD->setInit(*Reader.getContext(), Reader.ReadDeclExpr());
 }
 
 void PCHDeclReader::VisitImplicitParamDecl(ImplicitParamDecl *PD) {
index 22bdc7999bbf1cb5df8161a217b64625c3778f9e..4149fa4c9a35ac3d90c5588371afdec65aa3f34b 100644 (file)
@@ -2581,7 +2581,7 @@ void Sema::AddInitializerToDecl(DeclPtrTy dcl, ExprArg init, bool DirectInit) {
     // };
 
     // Attach the initializer
-    VDecl->setInit(Init);
+    VDecl->setInit(Context, Init);
 
     // C++ [class.mem]p4:
     //   A member-declarator can contain a constant-initializer only
@@ -2644,7 +2644,7 @@ void Sema::AddInitializerToDecl(DeclPtrTy dcl, ExprArg init, bool DirectInit) {
   }
     
   // Attach the initializer to the decl.
-  VDecl->setInit(Init);
+  VDecl->setInit(Context, Init);
 
   // If the previous declaration of VDecl was a tentative definition,
   // remove it from the set of tentative definitions.
index feb94569ed2b3566940fd97b67629a0c8c9b6a05..ebe34064d6d1f70611b9321b466fe7e047398a5c 100644 (file)
@@ -1776,7 +1776,7 @@ void Sema::InitializeVarWithConstructor(VarDecl *VD,
                                         Expr **Exprs, unsigned NumExprs) {
   Expr *Temp = CXXConstructExpr::Create(Context, VD, DeclInitType, Constructor, 
                                         false, Exprs, NumExprs);
-  VD->setInit(Temp);
+  VD->setInit(Context, Temp);
 }
 
 /// AddCXXDirectInitializerToDecl - This action is called immediately after