diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -4351,13 +4351,15 @@ SourceLocation LParenLoc, RParenLoc; public: StmtExpr(CompoundStmt *SubStmt, QualType T, SourceLocation LParenLoc, - SourceLocation RParenLoc, unsigned TemplateDepth) + SourceLocation RParenLoc, unsigned TemplateDepth, + bool NeedsCleanup = false) : Expr(StmtExprClass, T, VK_PRValue, OK_Ordinary), SubStmt(SubStmt), LParenLoc(LParenLoc), RParenLoc(RParenLoc) { setDependence(computeDependence(this, TemplateDepth)); // FIXME: A templated statement expression should have an associated // DeclContext so that nested declarations always have a dependent context. StmtExprBits.TemplateDepth = TemplateDepth; + StmtExprBits.NeedsCleanup = NeedsCleanup; } /// Build an empty statement expression. @@ -4377,6 +4379,8 @@ unsigned getTemplateDepth() const { return StmtExprBits.TemplateDepth; } + bool needsCleanup() const { return StmtExprBits.NeedsCleanup; } + static bool classof(const Stmt *T) { return T->getStmtClass() == StmtExprClass; } diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -604,7 +604,11 @@ /// The number of levels of template parameters enclosing this statement /// expression. Used to determine if a statement expression remains /// dependent after instantiation. - unsigned TemplateDepth; + unsigned TemplateDepth : 7; + + /// Whether this StmtExpr needs cleanup. This is always false when it is + /// created for a GNU statement expression. + unsigned NeedsCleanup : 1; }; //===--- C++ Expression bitfields classes ---===// diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -5681,7 +5681,8 @@ ExprResult ActOnStmtExpr(Scope *S, SourceLocation LPLoc, Stmt *SubStmt, SourceLocation RPLoc); ExprResult BuildStmtExpr(SourceLocation LPLoc, Stmt *SubStmt, - SourceLocation RPLoc, unsigned TemplateDepth); + SourceLocation RPLoc, unsigned TemplateDepth, + bool NeedsCleanup = false); // Handle the final expression in a statement expression. ExprResult ActOnStmtExprResult(ExprResult E); void ActOnStmtExprError(); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -15769,13 +15769,15 @@ } ExprResult Sema::BuildStmtExpr(SourceLocation LPLoc, Stmt *SubStmt, - SourceLocation RPLoc, unsigned TemplateDepth) { + SourceLocation RPLoc, unsigned TemplateDepth, + bool NeedsCleanup) { assert(SubStmt && isa(SubStmt) && "Invalid action invocation!"); CompoundStmt *Compound = cast(SubStmt); if (hasAnyUnrecoverableErrorsInThisFunction()) DiscardCleanupsInEvaluationContext(); - assert(!Cleanup.exprNeedsCleanups() && + // Cleanups may be needed if temporaries are created in an AsmStmt. + assert((!Cleanup.exprNeedsCleanups() || NeedsCleanup) && "cleanups within StmtExpr not correctly bound!"); PopExpressionEvaluationContext(); @@ -15800,8 +15802,8 @@ // FIXME: Check that expression type is complete/non-abstract; statement // expressions are not lvalues. - Expr *ResStmtExpr = - new (Context) StmtExpr(Compound, Ty, LPLoc, RPLoc, TemplateDepth); + Expr *ResStmtExpr = new (Context) + StmtExpr(Compound, Ty, LPLoc, RPLoc, TemplateDepth, NeedsCleanup); if (StmtExprMayBindToTemp) return MaybeBindToTemporary(ResStmtExpr); return ResStmtExpr; diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -7299,7 +7299,7 @@ Context, SubStmt, SourceLocation(), SourceLocation()); Expr *E = new (Context) StmtExpr(CompStmt, Context.VoidTy, SourceLocation(), SourceLocation(), - /*FIXME TemplateDepth=*/0); + /*FIXME TemplateDepth=*/0, /*NeedsCleanup=*/true); return MaybeCreateExprWithCleanups(E); } diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -2873,9 +2873,10 @@ /// By default, performs semantic analysis to build the new expression. /// Subclasses may override this routine to provide different behavior. ExprResult RebuildStmtExpr(SourceLocation LParenLoc, Stmt *SubStmt, - SourceLocation RParenLoc, unsigned TemplateDepth) { - return getSema().BuildStmtExpr(LParenLoc, SubStmt, RParenLoc, - TemplateDepth); + SourceLocation RParenLoc, unsigned TemplateDepth, + bool NeedsCleanup) { + return getSema().BuildStmtExpr(LParenLoc, SubStmt, RParenLoc, TemplateDepth, + NeedsCleanup); } /// Build a new __builtin_choose_expr expression. @@ -11565,7 +11566,8 @@ } return getDerived().RebuildStmtExpr(E->getLParenLoc(), SubStmt.get(), - E->getRParenLoc(), NewDepth); + E->getRParenLoc(), NewDepth, + E->needsCleanup()); } template diff --git a/clang/test/CodeGenCXX/asm.cpp b/clang/test/CodeGenCXX/asm.cpp --- a/clang/test/CodeGenCXX/asm.cpp +++ b/clang/test/CodeGenCXX/asm.cpp @@ -1,5 +1,7 @@ // RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm %s -o - | FileCheck %s +// CHECK: %[[STRUCT_A:.*]] = type { i8 } + struct A { ~A(); @@ -12,3 +14,20 @@ asm("" : : "r"(foo(a)) ); // rdar://8540491 // CHECK: call void @_ZN1AD1Ev } + +namespace TestTemplate { +template +void bar(A &a) { + asm("" : : "r"(foo(a)) ); + asm(""); +} + +// CHECK: define {{.*}}void @_ZN12TestTemplate3barIvEEvR1A( +// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_A]], +// CHECK: %[[CALL:.*]] = call noundef i32 @_Z3foo1A({{.*}}%[[AGG_TMP]]) +// CHECK: call void asm sideeffect "", "r,~{dirflag},~{fpsr},~{flags}"(i32 %[[CALL]]) +// CHECK: call void @_ZN1AD1Ev({{.*}}%[[AGG_TMP]]) +// CHECK: call void asm sideeffect "", + +void test(A &a) { bar(a); } +} // namespace TestTemplate diff --git a/clang/test/SemaTemplate/instantiate-expr-1.cpp b/clang/test/SemaTemplate/instantiate-expr-1.cpp --- a/clang/test/SemaTemplate/instantiate-expr-1.cpp +++ b/clang/test/SemaTemplate/instantiate-expr-1.cpp @@ -190,3 +190,19 @@ test_asm_tied(1.f); // expected-note {{instantiation of}} } } + +namespace TestAsmCleanup { +struct S { + operator int() const { return 1; } + ~S(); +}; + +template +void foo() { + __asm__ __volatile__("%[i]" + : + : [i] "r"(-S())); +} + +void test() { foo(); } +} // namespace TestAsmCleanup