Index: clang/include/clang/AST/ASTContext.h =================================================================== --- clang/include/clang/AST/ASTContext.h +++ clang/include/clang/AST/ASTContext.h @@ -249,6 +249,8 @@ ASTContext&> SubstTemplateTemplateParmPacks; + mutable llvm::DenseMap ExprEvalResultCache; + /// The set of nested name specifiers. /// /// This set is managed by the NestedNameSpecifier class. @@ -2527,6 +2529,8 @@ /// Determine whether the given expressions \p X and \p Y are equivalent. bool hasSameExpr(const Expr *X, const Expr *Y) const; + bool getCachedEvaluated(const Expr *X, APValue &Result) const; + void addExprResultToCache(const Expr *X, APValue &Result) const; /// Return this type as a completely-unqualified array type, /// capturing the qualifiers in \p Quals. Index: clang/lib/AST/ASTContext.cpp =================================================================== --- clang/lib/AST/ASTContext.cpp +++ clang/lib/AST/ASTContext.cpp @@ -40,6 +40,7 @@ #include "clang/AST/ParentMapContext.h" #include "clang/AST/RawCommentList.h" #include "clang/AST/RecordLayout.h" +#include "clang/AST/ODRHash.h" #include "clang/AST/Stmt.h" #include "clang/AST/TemplateBase.h" #include "clang/AST/TemplateName.h" @@ -12385,6 +12386,52 @@ return IDX == IDY; } +bool ASTContext::getCachedEvaluated(const Expr *X, APValue &Result) const { + assert (X && "We shouldn't evaluate on null expr.\n"); + + auto *CE = dyn_cast(X); + if (!CE) + return false; + + auto *FD = CE->getDirectCallee(); + if (!FD || !FD->isConstexpr()) + return false; + + llvm::FoldingSetNodeID ID; + X->Profile(ID, *this, /*Canonical=*/true); + unsigned HashValue = llvm::hash_combine(ID.ComputeHash(), const_cast(FD)->getODRHash()); + + auto Iter = ExprEvalResultCache.find(HashValue); + if (Iter == ExprEvalResultCache.end()) + return false; + + Result = Iter->second; + assert(Result.hasValue()); + return true; +} + +void ASTContext::addExprResultToCache(const Expr *X, APValue &Result) const { + if (!Result.hasValue()) + return; + + auto *CE = dyn_cast(X); + if (!CE) + return; + + auto *FD = CE->getDirectCallee(); + if (!FD || !FD->isConstexpr()) + return; + + assert (X && "We shouldn't evaluate on null expr.\n"); + llvm::FoldingSetNodeID ID; + X->Profile(ID, *this, /*Canonical=*/true); + unsigned HashValue = llvm::hash_combine(ID.ComputeHash(), const_cast(FD)->getODRHash()); + + auto Iter = ExprEvalResultCache.find(HashValue); + assert(Iter == ExprEvalResultCache.end()); + ExprEvalResultCache.insert({HashValue, Result}); +} + // The getCommon* helpers return, for given 'same' X and Y entities given as // inputs, another entity which is also the 'same' as the inputs, but which // is closer to the canonical form of the inputs, each according to a given Index: clang/lib/AST/ExprConstant.cpp =================================================================== --- clang/lib/AST/ExprConstant.cpp +++ clang/lib/AST/ExprConstant.cpp @@ -15412,6 +15412,11 @@ SourceLocation DeclLoc = VD->getLocation(); QualType DeclTy = VD->getType(); + if (Ctx.getCachedEvaluated(this, Value)) + return CheckConstantExpression(Info, DeclLoc, DeclTy, Value, + ConstantExprKind::Normal) && + CheckMemoryLeaks(Info); + if (Info.EnableNewConstInterp) { auto &InterpCtx = const_cast(Ctx).getInterpContext(); if (!InterpCtx.evaluateAsInitializer(Info, VD, Value)) @@ -15432,9 +15437,14 @@ if (!Info.discardCleanups()) llvm_unreachable("Unhandled cleanup; missing full expression marker?"); } - return CheckConstantExpression(Info, DeclLoc, DeclTy, Value, + auto Ret = CheckConstantExpression(Info, DeclLoc, DeclTy, Value, ConstantExprKind::Normal) && CheckMemoryLeaks(Info); + + if (Ret && Value.hasValue()) + Ctx.addExprResultToCache(this, Value); + + return Ret; } bool VarDecl::evaluateDestruction(