[clang] 1c2e7d2 - [MS] Fix crash involving gnu stmt exprs and inalloca

Reid Kleckner via cfe-commits cfe-commits at lists.llvm.org
Thu Mar 4 13:57:52 PST 2021


Author: Reid Kleckner
Date: 2021-03-04T13:57:46-08:00
New Revision: 1c2e7d200df27e91631ba300965245518bfe252c

URL: https://github.com/llvm/llvm-project/commit/1c2e7d200df27e91631ba300965245518bfe252c
DIFF: https://github.com/llvm/llvm-project/commit/1c2e7d200df27e91631ba300965245518bfe252c.diff

LOG: [MS] Fix crash involving gnu stmt exprs and inalloca

Use a WeakTrackingVH to cope with the stmt emission logic that cleans up
unreachable blocks. This invalidates the reference to the deferred
replacement placeholder. Cope with it.

Fixes PR25102 (from 2015!)

Added: 
    clang/test/CodeGenCXX/inalloca-stmtexpr.cpp

Modified: 
    clang/lib/CodeGen/CGCall.cpp
    clang/lib/CodeGen/CodeGenFunction.cpp
    clang/lib/CodeGen/CodeGenFunction.h

Removed: 
    


################################################################################
diff  --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index f5411daaa677..edcaa528437b 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -4436,7 +4436,8 @@ llvm::CallBase *CodeGenFunction::EmitCallOrInvoke(llvm::FunctionCallee Callee,
 
 void CodeGenFunction::deferPlaceholderReplacement(llvm::Instruction *Old,
                                                   llvm::Value *New) {
-  DeferredReplacements.push_back(std::make_pair(Old, New));
+  DeferredReplacements.push_back(
+      std::make_pair(llvm::WeakTrackingVH(Old), New));
 }
 
 namespace {

diff  --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 6d95adcb6ef0..53bf69f8f86d 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -452,13 +452,13 @@ void CodeGenFunction::FinishFunction(SourceLocation EndLoc) {
   if (CGM.getCodeGenOpts().EmitDeclMetadata)
     EmitDeclMetadata();
 
-  for (SmallVectorImpl<std::pair<llvm::Instruction *, llvm::Value *> >::iterator
-           I = DeferredReplacements.begin(),
-           E = DeferredReplacements.end();
-       I != E; ++I) {
-    I->first->replaceAllUsesWith(I->second);
-    I->first->eraseFromParent();
+  for (const auto &R : DeferredReplacements) {
+    if (llvm::Value *Old = R.first) {
+      Old->replaceAllUsesWith(R.second);
+      cast<llvm::Instruction>(Old)->eraseFromParent();
+    }
   }
+  DeferredReplacements.clear();
 
   // Eliminate CleanupDestSlot alloca by replacing it with SSA values and
   // PHIs if the current function is a coroutine. We don't do it for all

diff  --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 8ef0de018a98..2ce87ac7c8e3 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -4539,8 +4539,8 @@ class CodeGenFunction : public CodeGenTypeCache {
 
   void deferPlaceholderReplacement(llvm::Instruction *Old, llvm::Value *New);
 
-  llvm::SmallVector<std::pair<llvm::Instruction *, llvm::Value *>, 4>
-  DeferredReplacements;
+  llvm::SmallVector<std::pair<llvm::WeakTrackingVH, llvm::Value *>, 4>
+      DeferredReplacements;
 
   /// Set the address of a local variable.
   void setAddrOfLocalVar(const VarDecl *VD, Address Addr) {

diff  --git a/clang/test/CodeGenCXX/inalloca-stmtexpr.cpp b/clang/test/CodeGenCXX/inalloca-stmtexpr.cpp
new file mode 100644
index 000000000000..e7ae2cb4e703
--- /dev/null
+++ b/clang/test/CodeGenCXX/inalloca-stmtexpr.cpp
@@ -0,0 +1,51 @@
+// RUN: %clang_cc1 %s -emit-llvm -triple i686-windows-msvc -o - | FileCheck %s
+
+// Statement allow the user to exit the evaluation scope of a CallExpr without
+// executing the call. Check that clang generates reasonable IR for that case.
+
+// Not trivially copyable, subject to inalloca.
+struct Foo {
+  int x;
+  Foo();
+  ~Foo();
+};
+
+void inalloca(Foo x, Foo y);
+
+// PR25102: In this case, clang attempts to clean up unreachable blocks *during*
+// IR generation. inalloca defers some RAUW operations to the end of codegen,
+// and those references would become stale when the unreachable call to
+// 'inalloca' got deleted.
+extern "C" void pr25102() {
+  inalloca(Foo(), ({
+             goto out;
+             Foo();
+           }));
+out:;
+}
+
+// CHECK-LABEL: define dso_local void @pr25102()
+// CHECK: br label %out
+// CHECK: out:
+// CHECK: ret void
+
+bool cond();
+extern "C" void seqAbort() {
+  inalloca(Foo(), ({
+             if (cond())
+               goto out;
+             Foo();
+           }));
+out:;
+}
+
+// FIXME: This can cause a stack leak. We should really have a "normal" cleanup
+// that goto branches through.
+// CHECK-LABEL: define dso_local void @seqAbort()
+// CHECK: alloca inalloca <{ %struct.Foo, %struct.Foo }>
+// CHECK: call zeroext i1 @"?cond@@YA_NXZ"()
+// CHECK: br i1
+// CHECK: br label %out
+// CHECK: call void @"?inalloca@@YAXUFoo@@0 at Z"(<{ %struct.Foo, %struct.Foo }>* inalloca %{{.*}})
+// CHECK: call void @llvm.stackrestore(i8* %inalloca.save)
+// CHECK: out:


        


More information about the cfe-commits mailing list