[PATCH] D82314: [RFC][Coroutines] Optimize the lifespan of temporary co_await object

Xun Li via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Mon Jun 22 10:12:50 PDT 2020


lxfind created this revision.
lxfind added reviewers: lewissbaker, modocache, junparser.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

If we ever assign co_await to a temporary variable, such as foo(co_await expr),
we generate AST that looks like this: MaterializedTemporaryExpr(CoawaitExpr(...)).
MaterializedTemporaryExpr would emit an intrinsics that marks the lifetime start of the
temporary storage. However such temporary storage will not be used until co_await is ready
to write the result. Marking the lifetime start way too early causes extra storage to be
put in the coroutine frame instead of the stack.
As you can see from https://godbolt.org/z/XsLUiY, the frame generated for get_big_object2 is 12K, which contains a big_object object unnecessarily.
After this patch, the frame size for get_big_object2 is now only 8K. There are still room for improvements, in particular, GCC has a 4K frame for this function. But that's a separate problem and not addressed in this patch.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D82314

Files:
  clang/lib/CodeGen/CGCoroutine.cpp


Index: clang/lib/CodeGen/CGCoroutine.cpp
===================================================================
--- clang/lib/CodeGen/CGCoroutine.cpp
+++ clang/lib/CodeGen/CGCoroutine.cpp
@@ -245,10 +245,39 @@
   }
 
   LValueOrRValue Res;
-  if (forLValue)
+  if (forLValue) {
     Res.LV = CGF.EmitLValue(S.getResumeExpr());
-  else
+  } else {
+    // If the result of co_await is stored into a memory address, we want to
+    // make sure the lifetime of that memory address does not start too early,
+    // causing more space to be allocated in the frame rather than on stack.
+    // The lifetime of the return value of co_await should start here right
+    // before we attempt to assign it.
+    auto adjustLifetimeStart = [&]() {
+      // aggSlot points to the instruction that allocated the object. Latter
+      // instructions use it in this pattern:
+      //   %tmpX = alloca %.., align 4
+      //   %0 = bitcast %...* %tmpX to i8*
+      //   call void @llvm.lifetime.start.p0i8(i64 ..., i8* nonnull %0) #2
+      // Hence we trace back through uses to eventually locate the lifetime
+      // start intrinsics marker, and move it down to the current insertion
+      // point.
+      auto *AllocaInst =
+          dyn_cast_or_null<llvm::AllocaInst>(aggSlot.getPointer());
+      if (!AllocaInst || AllocaInst->getNumUses() != 1)
+        return;
+      auto *CastInst = dyn_cast<llvm::CastInst>(*AllocaInst->users().begin());
+      if (!CastInst || CastInst->getNumUses() != 1)
+        return;
+      if (auto *LifetimeInst =
+              dyn_cast<llvm::IntrinsicInst>(*CastInst->users().begin())) {
+        LifetimeInst->removeFromParent();
+        CGF.Builder.Insert(LifetimeInst);
+      }
+    };
+    adjustLifetimeStart();
     Res.RV = CGF.EmitAnyExpr(S.getResumeExpr(), aggSlot, ignoreResult);
+  }
 
   if (TryStmt) {
     Builder.CreateFlagStore(false, Coro.ResumeEHVar);


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D82314.272476.patch
Type: text/x-patch
Size: 1904 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20200622/1560519a/attachment-0001.bin>


More information about the cfe-commits mailing list