diff options
-rw-r--r-- | include/clang/AST/Stmt.h | 16 | ||||
-rw-r--r-- | lib/CodeGen/CGStmt.cpp | 54 | ||||
-rw-r--r-- | lib/CodeGen/CodeGenFunction.h | 21 | ||||
-rw-r--r-- | lib/Frontend/PCHReaderStmt.cpp | 1 | ||||
-rw-r--r-- | lib/Frontend/PCHWriterStmt.cpp | 1 | ||||
-rw-r--r-- | lib/Sema/SemaStmt.cpp | 12 | ||||
-rw-r--r-- | lib/Sema/TreeTransform.h | 13 | ||||
-rw-r--r-- | test/CodeGenCXX/condition.cpp | 20 |
8 files changed, 126 insertions, 12 deletions
diff --git a/include/clang/AST/Stmt.h b/include/clang/AST/Stmt.h index 103b373017..b7505e1bc7 100644 --- a/include/clang/AST/Stmt.h +++ b/include/clang/AST/Stmt.h @@ -750,10 +750,13 @@ public: /// class WhileStmt : public Stmt { enum { COND, BODY, END_EXPR }; + VarDecl *Var; Stmt* SubExprs[END_EXPR]; SourceLocation WhileLoc; public: - WhileStmt(Expr *cond, Stmt *body, SourceLocation WL) : Stmt(WhileStmtClass) { + WhileStmt(VarDecl *Var, Expr *cond, Stmt *body, SourceLocation WL) + : Stmt(WhileStmtClass), Var(Var) + { SubExprs[COND] = reinterpret_cast<Stmt*>(cond); SubExprs[BODY] = body; WhileLoc = WL; @@ -762,6 +765,17 @@ public: /// \brief Build an empty while statement. explicit WhileStmt(EmptyShell Empty) : Stmt(WhileStmtClass, Empty) { } + /// \brief Retrieve the variable declared in this "while" statement, if any. + /// + /// In the following example, "x" is the condition variable. + /// \code + /// while (int x = random()) { + /// // ... + /// } + /// \endcode + VarDecl *getConditionVariable() const { return Var; } + void setConditionVariable(VarDecl *V) { Var = V; } + Expr *getCond() { return reinterpret_cast<Expr*>(SubExprs[COND]); } const Expr *getCond() const { return reinterpret_cast<Expr*>(SubExprs[COND]);} void setCond(Expr *E) { SubExprs[COND] = reinterpret_cast<Stmt*>(E); } diff --git a/lib/CodeGen/CGStmt.cpp b/lib/CodeGen/CGStmt.cpp index 34e364967d..6fddcf6b04 100644 --- a/lib/CodeGen/CGStmt.cpp +++ b/lib/CodeGen/CGStmt.cpp @@ -353,15 +353,37 @@ void CodeGenFunction::EmitWhileStmt(const WhileStmt &S) { // body of the loop. llvm::BasicBlock *ExitBlock = createBasicBlock("while.end"); llvm::BasicBlock *LoopBody = createBasicBlock("while.body"); + llvm::BasicBlock *CleanupBlock = 0; + llvm::BasicBlock *EffectiveExitBlock = ExitBlock; // Store the blocks to use for break and continue. BreakContinueStack.push_back(BreakContinue(ExitBlock, LoopHeader)); + // C++ [stmt.while]p2: + // When the condition of a while statement is a declaration, the + // scope of the variable that is declared extends from its point + // of declaration (3.3.2) to the end of the while statement. + // [...] + // The object created in a condition is destroyed and created + // with each iteration of the loop. + CleanupScope ConditionScope(*this); + + if (S.getConditionVariable()) { + EmitLocalBlockVarDecl(*S.getConditionVariable()); + + // If this condition variable requires cleanups, create a basic + // block to handle those cleanups. + if (ConditionScope.requiresCleanups()) { + CleanupBlock = createBasicBlock("while.cleanup"); + EffectiveExitBlock = CleanupBlock; + } + } + // Evaluate the conditional in the while header. C99 6.8.5.1: The // evaluation of the controlling expression takes place before each // execution of the loop body. llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond()); - + // while(1) is common, avoid extra exit blocks. Be sure // to correctly handle break/continue though. bool EmitBoolCondBranch = true; @@ -371,23 +393,39 @@ void CodeGenFunction::EmitWhileStmt(const WhileStmt &S) { // As long as the condition is true, go to the loop body. if (EmitBoolCondBranch) - Builder.CreateCondBr(BoolCondVal, LoopBody, ExitBlock); - + Builder.CreateCondBr(BoolCondVal, LoopBody, EffectiveExitBlock); + // Emit the loop body. - EmitBlock(LoopBody); - EmitStmt(S.getBody()); + { + CleanupScope BodyScope(*this); + EmitBlock(LoopBody); + EmitStmt(S.getBody()); + } BreakContinueStack.pop_back(); - // Cycle to the condition. - EmitBranch(LoopHeader); + if (CleanupBlock) { + // If we have a cleanup block, jump there to perform cleanups + // before looping. + EmitBranch(CleanupBlock); + + // Emit the cleanup block, performing cleanups for the condition + // and then jumping to either the loop header or the exit block. + EmitBlock(CleanupBlock); + ConditionScope.ForceCleanup(); + Builder.CreateCondBr(BoolCondVal, LoopHeader, ExitBlock); + } else { + // Cycle to the condition. + EmitBranch(LoopHeader); + } // Emit the exit block. EmitBlock(ExitBlock, true); + // The LoopHeader typically is just a branch if we skipped emitting // a branch, try to erase it. - if (!EmitBoolCondBranch) + if (!EmitBoolCondBranch && !CleanupBlock) SimplifyForwardingBlocks(LoopHeader); } diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index a0dd7c08bf..a42fa51265 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -171,13 +171,16 @@ public: CodeGenFunction& CGF; size_t CleanupStackDepth; bool OldDidCallStackSave; + bool PerformCleanup; CleanupScope(const CleanupScope &); // DO NOT IMPLEMENT CleanupScope &operator=(const CleanupScope &); // DO NOT IMPLEMENT public: /// \brief Enter a new cleanup scope. - explicit CleanupScope(CodeGenFunction &CGF) : CGF(CGF) { + explicit CleanupScope(CodeGenFunction &CGF) + : CGF(CGF), PerformCleanup(true) + { CleanupStackDepth = CGF.CleanupEntries.size(); OldDidCallStackSave = CGF.DidCallStackSave; } @@ -185,8 +188,24 @@ public: /// \brief Exit this cleanup scope, emitting any accumulated /// cleanups. ~CleanupScope() { + if (PerformCleanup) { + CGF.DidCallStackSave = OldDidCallStackSave; + CGF.EmitCleanupBlocks(CleanupStackDepth); + } + } + + /// \brief Determine whether this scope requires any cleanups. + bool requiresCleanups() const { + return CGF.CleanupEntries.size() > CleanupStackDepth; + } + + /// \brief Force the emission of cleanups now, instead of waiting + /// until this object is destroyed. + void ForceCleanup() { + assert(PerformCleanup && "Already forced cleanup"); CGF.DidCallStackSave = OldDidCallStackSave; CGF.EmitCleanupBlocks(CleanupStackDepth); + PerformCleanup = false; } }; diff --git a/lib/Frontend/PCHReaderStmt.cpp b/lib/Frontend/PCHReaderStmt.cpp index 1e9f72bd6e..ea2c979c52 100644 --- a/lib/Frontend/PCHReaderStmt.cpp +++ b/lib/Frontend/PCHReaderStmt.cpp @@ -210,6 +210,7 @@ unsigned PCHStmtReader::VisitSwitchStmt(SwitchStmt *S) { unsigned PCHStmtReader::VisitWhileStmt(WhileStmt *S) { VisitStmt(S); + S->setConditionVariable(cast_or_null<VarDecl>(Reader.GetDecl(Record[Idx++]))); S->setCond(cast_or_null<Expr>(StmtStack[StmtStack.size() - 2])); S->setBody(StmtStack.back()); S->setWhileLoc(SourceLocation::getFromRawEncoding(Record[Idx++])); diff --git a/lib/Frontend/PCHWriterStmt.cpp b/lib/Frontend/PCHWriterStmt.cpp index 064c258edc..5138776a90 100644 --- a/lib/Frontend/PCHWriterStmt.cpp +++ b/lib/Frontend/PCHWriterStmt.cpp @@ -193,6 +193,7 @@ void PCHStmtWriter::VisitSwitchStmt(SwitchStmt *S) { void PCHStmtWriter::VisitWhileStmt(WhileStmt *S) { VisitStmt(S); + Writer.AddDeclRef(S->getConditionVariable(), Record); Writer.WriteSubStmt(S->getCond()); Writer.WriteSubStmt(S->getBody()); Writer.AddSourceLocation(S->getWhileLoc(), Record); diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index 6ff3d638fe..61d48cb63e 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -720,6 +720,15 @@ Sema::ActOnWhileStmt(SourceLocation WhileLoc, FullExprArg Cond, StmtArg Body) { Expr *condExpr = CondArg.takeAs<Expr>(); assert(condExpr && "ActOnWhileStmt(): missing expression"); + VarDecl *ConditionVar = 0; + if (CXXConditionDeclExpr *Cond = dyn_cast<CXXConditionDeclExpr>(condExpr)) { + ConditionVar = Cond->getVarDecl(); + condExpr = DeclRefExpr::Create(Context, 0, SourceRange(), ConditionVar, + ConditionVar->getLocation(), + ConditionVar->getType().getNonReferenceType()); + // FIXME: Leaks the old condExpr + } + if (CheckBooleanCondition(condExpr, WhileLoc)) { CondArg = condExpr; return StmtError(); @@ -729,7 +738,8 @@ Sema::ActOnWhileStmt(SourceLocation WhileLoc, FullExprArg Cond, StmtArg Body) { DiagnoseUnusedExprResult(bodyStmt); CondArg.release(); - return Owned(new (Context) WhileStmt(condExpr, bodyStmt, WhileLoc)); + return Owned(new (Context) WhileStmt(ConditionVar, condExpr, bodyStmt, + WhileLoc)); } Action::OwningStmtResult diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index e1d0bba4a4..202e16e31e 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -3139,7 +3139,18 @@ template<typename Derived> Sema::OwningStmtResult TreeTransform<Derived>::TransformWhileStmt(WhileStmt *S) { // Transform the condition - OwningExprResult Cond = getDerived().TransformExpr(S->getCond()); + OwningExprResult Cond(SemaRef); + VarDecl *ConditionVar = 0; + if (S->getConditionVariable()) { + ConditionVar + = cast_or_null<VarDecl>( + getDerived().TransformDefinition(S->getConditionVariable())); + if (!ConditionVar) + return SemaRef.StmtError(); + + Cond = getSema().CheckConditionVariable(ConditionVar); + } else + Cond = getDerived().TransformExpr(S->getCond()); if (Cond.isInvalid()) return SemaRef.StmtError(); diff --git a/test/CodeGenCXX/condition.cpp b/test/CodeGenCXX/condition.cpp index f3c8a9b769..0bb9121f7f 100644 --- a/test/CodeGenCXX/condition.cpp +++ b/test/CodeGenCXX/condition.cpp @@ -69,3 +69,23 @@ void switch_destruct(int z) { // CHECK: store i32 20 z = 20; } + +int foo(); + +void while_destruct(int z) { + // CHECK: define void @_Z14while_destructi + // CHECK: while.cond: + while (X x = X()) { + // CHECK: call void @_ZN1XC1Ev + + // CHECK: while.body: + // CHECK: store i32 21 + z = 21; + + // CHECK: while.cleanup: + // CHECK: call void @_ZN1XD1Ev + } + // CHECK: while.end + // CHECK: store i32 22 + z = 22; +} |