Index: clang/lib/AST/Interp/ByteCodeExprGen.h =================================================================== --- clang/lib/AST/Interp/ByteCodeExprGen.h +++ clang/lib/AST/Interp/ByteCodeExprGen.h @@ -152,6 +152,8 @@ bool visitArrayInitializer(const Expr *Initializer); /// Compiles a record initializer. bool visitRecordInitializer(const Expr *Initializer); + /// Creates and initializes a variable from the given decl. + bool visitVarDecl(const VarDecl *VD); /// Visits an expression and converts it to a boolean. bool visitBool(const Expr *E); @@ -255,6 +257,12 @@ return T->getAsCXXRecordDecl(); } + /// Returns whether we should create a global variable for the + /// given VarDecl. + bool shouldBeGloballyIndexed(const VarDecl *VD) const { + return VD->hasGlobalStorage() || VD->isConstexpr(); + } + protected: /// Variable to storage mapping. llvm::DenseMap Locals; Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp =================================================================== --- clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -854,6 +854,13 @@ PrimType Ty, bool IsConst, bool IsExtended) { + // Make sure we don't accidentally register the same decl twice. + if (const auto *VD = + dyn_cast_if_present(Src.dyn_cast())) { + assert(!P.getGlobal(VD)); + assert(Locals.find(VD) == Locals.end()); + } + // FIXME: There are cases where Src.is() is wrong, e.g. // (int){12} in C. Consider using Expr::isTemporaryObject() instead // or isa(). @@ -869,8 +876,14 @@ template llvm::Optional ByteCodeExprGen::allocateLocal(DeclTy &&Src, bool IsExtended) { - QualType Ty; + // Make sure we don't accidentally register the same decl twice. + if (const auto *VD = + dyn_cast_if_present(Src.dyn_cast())) { + assert(!P.getGlobal(VD)); + assert(Locals.find(VD) == Locals.end()); + } + QualType Ty; const ValueDecl *Key = nullptr; const Expr *Init = nullptr; bool IsTemporary = false; @@ -1188,41 +1201,87 @@ return this->emitRetValue(Exp); } +/// Toplevel visitDecl(). +/// We get here from evaluateAsInitializer(). +/// We need to evaluate the initializer and return its value. template bool ByteCodeExprGen::visitDecl(const VarDecl *VD) { - const Expr *Init = VD->getInit(); + Optional VarT = classify(VD->getType()); - if (Optional I = P.createGlobal(VD, Init)) { - if (Optional T = classify(VD->getType())) { - { - // Primitive declarations - compute the value and set it. - DeclScope LocalScope(this, VD); - if (!visit(Init)) - return false; - } + // Create and initialize the variable. + if (!this->visitVarDecl(VD)) + return false; - // If the declaration is global, save the value for later use. - if (!this->emitDup(*T, VD)) + // Get a pointer to the variable + if (shouldBeGloballyIndexed(VD)) { + if (auto GlobalIndex = P.getGlobal(VD)) { + if (!this->emitGetPtrGlobal(*GlobalIndex, VD)) return false; - if (!this->emitInitGlobal(*T, *I, VD)) + } + } else { + if (auto It = Locals.find(VD); It != Locals.end()) { + if (!this->emitGetPtrLocal(It->second.Offset, VD)) return false; - return this->emitRet(*T, VD); - } else { - { - // Composite declarations - allocate storage and initialize it. - DeclScope LocalScope(this, VD); - if (!visitGlobalInitializer(Init, *I)) + } + } + + // Return the value + if (VarT) { + if (!this->emitLoadPop(*VarT, VD)) + return false; + + return this->emitRet(*VarT, VD); + } + + return this->emitRetValue(VD); +} + +template +bool ByteCodeExprGen::visitVarDecl(const VarDecl *VD) { + const Expr *Init = VD->getInit(); + Optional VarT = classify(VD->getType()); + + if (shouldBeGloballyIndexed(VD)) { + Optional GlobalIndex = P.getOrCreateGlobal(VD, Init); + + if (!GlobalIndex) + return this->bail(VD); + + assert(Init); + { + DeclScope LocalScope(this, VD); + + if (VarT) { + if (!this->visit(Init)) return false; + return this->emitInitGlobal(*VarT, *GlobalIndex, VD); } + return this->visitGlobalInitializer(Init, *GlobalIndex); + } + } else { + DeclScope LocalScope(this, VD); + + if (VarT) { + unsigned Offset = this->allocateLocalPrimitive( + VD, *VarT, VD->getType().isConstQualified()); + // Compile the initializer in its own scope. + if (Init) { + ExprScope Scope(this); + if (!this->visit(Init)) + return false; - // Return a pointer to the global. - if (!this->emitGetPtrGlobal(*I, VD)) - return false; - return this->emitRetValue(VD); + return this->emitSetLocal(*VarT, Offset, VD); + } + } else { + if (Optional Offset = this->allocateLocal(VD)) { + if (Init) + return this->visitLocalInitializer(Init, *Offset); + } } + return true; } - return this->bail(VD); + return false; } template Index: clang/lib/AST/Interp/ByteCodeStmtGen.h =================================================================== --- clang/lib/AST/Interp/ByteCodeStmtGen.h +++ clang/lib/AST/Interp/ByteCodeStmtGen.h @@ -65,7 +65,6 @@ bool visitContinueStmt(const ContinueStmt *S); /// Compiles a variable declaration. - bool visitVarDecl(const VarDecl *VD); private: /// Type of the expression returned by the function. Index: clang/lib/AST/Interp/ByteCodeStmtGen.cpp =================================================================== --- clang/lib/AST/Interp/ByteCodeStmtGen.cpp +++ clang/lib/AST/Interp/ByteCodeStmtGen.cpp @@ -207,7 +207,7 @@ for (auto *D : DS->decls()) { // Variable declarator. if (auto *VD = dyn_cast(D)) { - if (!visitVarDecl(VD)) + if (!this->visitVarDecl(VD)) return false; continue; } @@ -391,37 +391,6 @@ return this->jump(*ContinueLabel); } -template -bool ByteCodeStmtGen::visitVarDecl(const VarDecl *VD) { - if (!VD->hasLocalStorage()) { - // No code generation required. - return true; - } - - // Integers, pointers, primitives. - if (Optional T = this->classify(VD->getType())) { - const Expr *Init = VD->getInit(); - - unsigned Offset = - this->allocateLocalPrimitive(VD, *T, VD->getType().isConstQualified()); - // Compile the initializer in its own scope. - if (Init) { - ExprScope Scope(this); - if (!this->visit(Init)) - return false; - - return this->emitSetLocal(*T, Offset, VD); - } - return true; - } - - // Composite types - allocate storage and initialize it. - if (Optional Offset = this->allocateLocal(VD)) - return this->visitLocalInitializer(VD->getInit(), *Offset); - - return this->bail(VD); -} - namespace clang { namespace interp { Index: clang/lib/AST/Interp/Program.h =================================================================== --- clang/lib/AST/Interp/Program.h +++ clang/lib/AST/Interp/Program.h @@ -79,7 +79,8 @@ llvm::Optional getGlobal(const ValueDecl *VD); /// Returns or creates a global an creates an index to it. - llvm::Optional getOrCreateGlobal(const ValueDecl *VD); + llvm::Optional getOrCreateGlobal(const ValueDecl *VD, + const Expr *Init = nullptr); /// Returns or creates a dummy value for parameters. llvm::Optional getOrCreateDummy(const ParmVarDecl *PD); Index: clang/lib/AST/Interp/Program.cpp =================================================================== --- clang/lib/AST/Interp/Program.cpp +++ clang/lib/AST/Interp/Program.cpp @@ -124,11 +124,12 @@ return Index; } -llvm::Optional Program::getOrCreateGlobal(const ValueDecl *VD) { +llvm::Optional Program::getOrCreateGlobal(const ValueDecl *VD, + const Expr *Init) { if (auto Idx = getGlobal(VD)) return Idx; - if (auto Idx = createGlobal(VD, nullptr)) { + if (auto Idx = createGlobal(VD, Init)) { GlobalIndices[VD] = *Idx; return Idx; } @@ -156,6 +157,7 @@ llvm::Optional Program::createGlobal(const ValueDecl *VD, const Expr *Init) { + assert(!getGlobal(VD)); bool IsStatic, IsExtern; if (auto *Var = dyn_cast(VD)) { IsStatic = !Var->hasLocalStorage();