timshen created this revision.
timshen added a reviewer: rsmith.
timshen added a subscriber: cfe-commits.

Use pushCleanupAfterFullExpr to add lifetime.end markers for 
lifetime-extended temporaries, similar to the way destructor calls get 
generated.

It does not change the AST representation. It doesn't generate normal temporary
lifetime markers, since normal temporaries appear in substantially different
places, which could be done in a separate patch.

http://reviews.llvm.org/D19930

Files:
  lib/CodeGen/CGCleanup.cpp
  lib/CodeGen/CGDecl.cpp
  lib/CodeGen/CGExpr.cpp
  lib/CodeGen/CodeGenFunction.h
  lib/CodeGen/EHScopeStack.h
  test/CodeGen/lifetime-extended-temporaries.cpp

Index: test/CodeGen/lifetime-extended-temporaries.cpp
===================================================================
--- /dev/null
+++ test/CodeGen/lifetime-extended-temporaries.cpp
@@ -0,0 +1,131 @@
+// RUN: %clang_cc1 %s -std=c++11 -O1 -DWITH_DTOR -triple x86_64 -emit-llvm -o - | FileCheck -check-prefix=CHECK-DTOR %s
+// RUN: %clang_cc1 %s -std=c++11 -O1 -triple x86_64 -emit-llvm -o - | FileCheck -check-prefix=CHECK-NO-DTOR %s
+
+struct A {
+  A();
+#ifdef WITH_DTOR
+  ~A();
+#endif
+  char a[1024];
+  operator bool() const;
+};
+
+template <typename T>
+void Foo(T &&);
+
+template <typename T>
+void Bar(T &&);
+
+template <typename T>
+T Baz();
+
+void Test1() {
+  // CHECK-DTOR-LABEL: Test1
+  // CHECK-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR:[0-9]+]])
+  // CHECK-DTOR: call void @_ZN1AC1Ev(%struct.A* nonnull %[[VAR:[^ ]+]])
+  // CHECK-DTOR: call void @_Z3FooIRK1AEvOT_
+  // CHECK-DTOR: call void @_ZN1AD1Ev(%struct.A* nonnull %[[VAR]])
+  // CHECK-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR]])
+  // CHECK-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR:[0-9]+]])
+  // CHECK-DTOR: call void @_ZN1AC1Ev(%struct.A* nonnull %[[VAR:[^ ]+]])
+  // CHECK-DTOR: call void @_Z3FooIRK1AEvOT_
+  // CHECK-DTOR: call void @_ZN1AD1Ev(%struct.A* nonnull %[[VAR]])
+  // CHECK-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR]])
+  // CHECK-DTOR: }
+
+  // CHECK-NO-DTOR-LABEL: Test1
+  // CHECK-NO-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR:[0-9]+]])
+  // CHECK-NO-DTOR: call void @_ZN1AC1Ev(%struct.A* nonnull %[[VAR:[^ ]+]])
+  // CHECK-NO-DTOR: call void @_Z3FooIRK1AEvOT_
+  // CHECK-NO-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR]])
+  // CHECK-NO-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR:[0-9]+]])
+  // CHECK-NO-DTOR: call void @_ZN1AC1Ev(%struct.A* nonnull %[[VAR:[^ ]+]])
+  // CHECK-NO-DTOR: call void @_Z3FooIRK1AEvOT_
+  // CHECK-NO-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR]])
+  // CHECK-NO-DTOR: }
+  {
+    const A &a = A{};
+    Foo(a);
+  }
+  {
+    const A &a = A{};
+    Foo(a);
+  }
+}
+
+void Test2() {
+  // CHECK-DTOR-LABEL: Test2
+  // CHECK-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR1:[0-9]+]])
+  // CHECK-DTOR: call void @_ZN1AC1Ev(%struct.A* nonnull %[[VAR1:[^ ]+]])
+  // CHECK-DTOR: call void @_Z3FooIRK1AEvOT_
+  // CHECK-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR2:[0-9]+]])
+  // CHECK-DTOR: call void @_ZN1AC1Ev(%struct.A* nonnull %[[VAR2:[^ ]+]])
+  // CHECK-DTOR: call void @_Z3FooIRK1AEvOT_
+  // CHECK-DTOR: call void @_ZN1AD1Ev(%struct.A* nonnull %[[VAR2]])
+  // CHECK-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR2]])
+  // CHECK-DTOR: call void @_ZN1AD1Ev(%struct.A* nonnull %[[VAR1]])
+  // CHECK-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR1]])
+  // CHECK-DTOR: }
+
+  // CHECK-NO-DTOR-LABEL: Test2
+  // CHECK-NO-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR1:[0-9]+]])
+  // CHECK-NO-DTOR: call void @_ZN1AC1Ev(%struct.A* nonnull %[[VAR1:[^ ]+]])
+  // CHECK-NO-DTOR: call void @_Z3FooIRK1AEvOT_
+  // CHECK-NO-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR2:[0-9]+]])
+  // CHECK-NO-DTOR: call void @_ZN1AC1Ev(%struct.A* nonnull %[[VAR2:[^ ]+]])
+  // CHECK-NO-DTOR: call void @_Z3FooIRK1AEvOT_
+  // CHECK-NO-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR2]])
+  // CHECK-NO-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR1]])
+  // CHECK-NO-DTOR: }
+  const A &a = A{};
+  Foo(a);
+  const A &b = A{};
+  Foo(b);
+}
+
+void Test3() {
+  // CHECK-DTOR-LABEL: Test3
+  // CHECK-DTOR: entry:
+  // CHECK-DTOR: call void @llvm.lifetime.start
+  // CHECK-DTOR: call void @llvm.lifetime.start
+  // CHECK-DTOR: if.then:
+  // CHECK-DTOR: call void @llvm.lifetime.end
+  // CHECK-DTOR: cleanup{{.*}}:
+  // CHECK-DTOR: call void @llvm.lifetime.end
+  // CHECK-DTOR: cleanup{{.*}}:
+  // CHECK-DTOR: call void @llvm.lifetime.end
+  // CHECK-DTOR: }
+  const A &a = A{};
+  if (const A &b = A(a)) {
+    Foo(b);
+    return;
+  }
+  Bar(a);
+}
+
+void Test4() {
+  // CHECK-DTOR-LABEL: Test4
+  // CHECK-DTOR: entry:
+  // CHECK-DTOR: call void @llvm.lifetime.start
+  // CHECK-DTOR: for.cond.cleanup:
+  // CHECK-DTOR: call void @llvm.lifetime.end
+  // CHECK-DTOR: for.body:
+  // CHECK-DTOR: }
+  for (const A &a = A{}; a;) {
+    Foo(a);
+  }
+}
+
+int Test5() {
+  // CHECK-DTOR-LABEL: Test5
+  // CHECK-DTOR: call void @llvm.lifetime.start
+  // CHECK-DTOR: call i32 @_Z3BazIiET_v()
+  // CHECK-DTOR: store
+  // CHECK-DTOR: call void @_Z3FooIRKiEvOT_
+  // CHECK-DTOR: load
+  // CHECK-DTOR: call void @llvm.lifetime.end
+  // CHECK-DTOR: }
+  const int &a = Baz<int>();
+  Foo(a);
+  return a;
+}
Index: lib/CodeGen/EHScopeStack.h
===================================================================
--- lib/CodeGen/EHScopeStack.h
+++ lib/CodeGen/EHScopeStack.h
@@ -89,7 +89,10 @@
   InactiveCleanup = 0x4,
   InactiveEHCleanup = EHCleanup | InactiveCleanup,
   InactiveNormalCleanup = NormalCleanup | InactiveCleanup,
-  InactiveNormalAndEHCleanup = NormalAndEHCleanup | InactiveCleanup
+  InactiveNormalAndEHCleanup = NormalAndEHCleanup | InactiveCleanup,
+
+  LifetimeMarker = 0x8,
+  NormalEHLifetimeMarker = LifetimeMarker | NormalAndEHCleanup,
 };
 
 /// A stack of scopes which respond to exceptions, including cleanups
Index: lib/CodeGen/CodeGenFunction.h
===================================================================
--- lib/CodeGen/CodeGenFunction.h
+++ lib/CodeGen/CodeGenFunction.h
@@ -300,6 +300,19 @@
 
   llvm::Instruction *CurrentFuncletPad = nullptr;
 
+  class CallLifetimeEnd final : public EHScopeStack::Cleanup {
+    llvm::Value *Addr;
+    llvm::Value *Size;
+
+  public:
+    CallLifetimeEnd(Address addr, llvm::Value *size)
+        : Addr(addr.getPointer()), Size(size) {}
+
+    void Emit(CodeGenFunction &CGF, Flags flags) override {
+      CGF.EmitLifetimeEnd(Size, Addr);
+    }
+  };
+
   /// Header for data within LifetimeExtendedCleanupStack.
   struct LifetimeExtendedCleanupHeader {
     /// The size of the following cleanup object.
Index: lib/CodeGen/CGExpr.cpp
===================================================================
--- lib/CodeGen/CGExpr.cpp
+++ lib/CodeGen/CGExpr.cpp
@@ -11,13 +11,14 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "CodeGenFunction.h"
 #include "CGCXXABI.h"
 #include "CGCall.h"
+#include "CGCleanup.h"
 #include "CGDebugInfo.h"
 #include "CGObjCRuntime.h"
 #include "CGOpenMPRuntime.h"
 #include "CGRecordLayout.h"
+#include "CodeGenFunction.h"
 #include "CodeGenModule.h"
 #include "TargetInfo.h"
 #include "clang/AST/ASTContext.h"
@@ -415,7 +416,19 @@
       EmitAnyExprToMem(E, Object, Qualifiers(), /*IsInit*/true);
     }
   } else {
+    llvm::Value *Size = nullptr;
+    // Only handle lifetime extended objects.
+    // TODO: Handle normal temporaries.
+    if (M->getStorageDuration() == SD_Automatic)
+      Size = EmitLifetimeStart(
+          CGM.getDataLayout().getTypeAllocSize(Object.getElementType()),
+          Object.getPointer());
+
     EmitAnyExprToMem(E, Object, Qualifiers(), /*IsInit*/true);
+
+    if (Size)
+      pushCleanupAfterFullExpr<CallLifetimeEnd>(NormalEHLifetimeMarker, Object,
+                                                Size);
   }
   pushTemporaryCleanup(*this, M, E, Object);
 
Index: lib/CodeGen/CGDecl.cpp
===================================================================
--- lib/CodeGen/CGDecl.cpp
+++ lib/CodeGen/CGDecl.cpp
@@ -521,19 +521,6 @@
       CGF.EmitCall(FnInfo, CleanupFn, ReturnValueSlot(), Args);
     }
   };
-
-  /// A cleanup to call @llvm.lifetime.end.
-  class CallLifetimeEnd final : public EHScopeStack::Cleanup {
-    llvm::Value *Addr;
-    llvm::Value *Size;
-  public:
-    CallLifetimeEnd(Address addr, llvm::Value *size)
-      : Addr(addr.getPointer()), Size(size) {}
-
-    void Emit(CodeGenFunction &CGF, Flags flags) override {
-      CGF.EmitLifetimeEnd(Size, Addr);
-    }
-  };
 } // end anonymous namespace
 
 /// EmitAutoVarWithLifetime - Does the setup required for an automatic
@@ -1283,6 +1270,14 @@
   QualType type = D->getType();
 
   if (type->isReferenceType()) {
+    // It is a lifetime-extension. Ensure that there is a RunCleanupsScope for
+    // the delayed CallLifetimeEnd in EmitMaterializeTemporaryExpr.
+    Optional<RunCleanupsScope> Cleanup;
+    // Create this scope only when there is a MaterializeTemporaryExpr that is
+    // not guarded by an ExprWithCleanups.
+    if (dyn_cast<MaterializeTemporaryExpr>(init))
+      Cleanup.emplace(*this);
+
     RValue rvalue = EmitReferenceBindingToExpr(init);
     if (capturedByInit)
       drillIntoBlockVariable(*this, lvalue, cast<VarDecl>(D));
@@ -1387,13 +1382,10 @@
 
   // Make sure we call @llvm.lifetime.end.  This needs to happen
   // *last*, so the cleanup needs to be pushed *first*.
-  if (emission.useLifetimeMarkers()) {
-    EHStack.pushCleanup<CallLifetimeEnd>(NormalAndEHCleanup,
+  if (emission.useLifetimeMarkers())
+    EHStack.pushCleanup<CallLifetimeEnd>(NormalEHLifetimeMarker,
                                          emission.getAllocatedAddress(),
                                          emission.getSizeForLifetimeMarkers());
-    EHCleanupScope &cleanup = cast<EHCleanupScope>(*EHStack.begin());
-    cleanup.setLifetimeMarker();
-  }
 
   // Check the type for a cleanup.
   if (QualType::DestructionKind dtorKind = D.getType().isDestructedType())
Index: lib/CodeGen/CGCleanup.cpp
===================================================================
--- lib/CodeGen/CGCleanup.cpp
+++ lib/CodeGen/CGCleanup.cpp
@@ -188,6 +188,7 @@
   bool IsNormalCleanup = Kind & NormalCleanup;
   bool IsEHCleanup = Kind & EHCleanup;
   bool IsActive = !(Kind & InactiveCleanup);
+  bool IsLifetimeMarker = Kind & LifetimeMarker;
   EHCleanupScope *Scope =
     new (Buffer) EHCleanupScope(IsNormalCleanup,
                                 IsEHCleanup,
@@ -200,6 +201,8 @@
     InnermostNormalCleanup = stable_begin();
   if (IsEHCleanup)
     InnermostEHScope = stable_begin();
+  if (IsLifetimeMarker)
+    Scope->setLifetimeMarker();
 
   return Scope->getCleanupBuffer();
 }
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to