[llvm-branch-commits] [compiler-rt] [NFC][TSan] Allocate ScopedReport as a stack variable (PR #228637)

Vitaly Buka via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Sat Oct 3 23:45:06 PDT 2026


https://github.com/vitalybuka updated https://github.com/llvm/llvm-project/pull/228637

>From e03bdae605f3b20a915a509af92233a1a254b13c Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Sat, 3 Oct 2026 19:00:58 -0700
Subject: [PATCH] [NFC][TSan] Allocate ScopedReport as a stack variable

Now that ScopedReport is constructed before acquiring ThreadRegistryLock
or slot locks across all reporting functions, it no longer needs to be
constructed inside the lock scope via placement new on __builtin_alloca
storage.

Declare ScopedReport as a normal stack variable before the lock scope
and remove the manual destructor calls.

Assisted-by: Gemini

Pull Request: https://github.com/llvm/llvm-project/pull/228637
---
 .../lib/tsan/rtl/tsan_interceptors_posix.cpp  | 13 ++---
 .../lib/tsan/rtl/tsan_interface_ann.cpp       | 13 ++---
 compiler-rt/lib/tsan/rtl/tsan_mman.cpp        | 11 ++--
 compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp   | 50 +++++++------------
 compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp  | 21 +++-----
 compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp  | 13 ++---
 6 files changed, 40 insertions(+), 81 deletions(-)

diff --git a/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp b/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
index a8d502966bcca..233985c419be1 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
@@ -2176,24 +2176,19 @@ static void ReportErrnoSpoiling(ThreadState *thr, uptr pc, int sig) {
   // StackTrace::GetNestInstructionPc(pc) is used because return address is
   // expected, OutputReport() will undo this.
   ObtainCurrentStack(thr, StackTrace::GetNextInstructionPc(pc), &stack);
-  // Use alloca, because malloc during signal handling deadlocks
-  ScopedReport *rep = (ScopedReport *)__builtin_alloca(sizeof(ScopedReport));
+  ScopedReport rep(ReportTypeErrnoInSignal);
   bool suppressed;
   // Release locks before symbolizing and outputting the report to avoid
   // deadlocks.
   {
-    new (rep) ScopedReport(ReportTypeErrnoInSignal);
     ThreadRegistryLock l(&ctx->thread_registry);
-    rep->SetSigNum(sig);
+    rep.SetSigNum(sig);
     suppressed = IsFiredSuppression(ctx, ReportTypeErrnoInSignal, stack);
     if (!suppressed)
-      rep->AddStack(stack, true);
+      rep.AddStack(stack, true);
   }
   if (!suppressed)
-    OutputReport(thr, *rep);
-
-  // Need to manually destroy this because we used placement new to allocate
-  rep->~ScopedReport();
+    OutputReport(thr, rep);
 }
 
 static void CallUserSignalHandler(ThreadState *thr, bool sync, bool acquire,
diff --git a/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp b/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp
index acd5066dee07b..11311ce8e901f 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp
@@ -443,23 +443,18 @@ void __tsan_mutex_post_divert(void *addr, unsigned flagz) {
 static void ReportMutexHeldWrongContext(ThreadState *thr, uptr pc) {
   VarSizeStackTrace trace;
   ObtainCurrentStack(thr, pc, &trace);
-  // Use alloca, because malloc during signal handling deadlocks
-  ScopedReport *rep = (ScopedReport *)__builtin_alloca(sizeof(ScopedReport));
+  ScopedReport rep(ReportTypeMutexHeldWrongContext);
   // Release locks before symbolizing and outputting the report to avoid
   // deadlocks.
   {
-    new (rep) ScopedReport(ReportTypeMutexHeldWrongContext);
     ThreadRegistryLock l(&ctx->thread_registry);
     for (uptr i = 0; i < thr->mset.Size(); ++i) {
       MutexSet::Desc desc = thr->mset.Get(i);
-      rep->AddMutex(desc.addr, desc.stack_id);
+      rep.AddMutex(desc.addr, desc.stack_id);
     }
-    rep->AddStack(trace, true);
+    rep.AddStack(trace, true);
   }
-  OutputReport(thr, *rep);
-
-  // Need to manually destroy this because we used placement new to allocate
-  rep->~ScopedReport();
+  OutputReport(thr, rep);
 }
 
 INTERFACE_ATTRIBUTE
diff --git a/compiler-rt/lib/tsan/rtl/tsan_mman.cpp b/compiler-rt/lib/tsan/rtl/tsan_mman.cpp
index 88cdf58af8d58..7a39b8a71a0a7 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_mman.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_mman.cpp
@@ -182,19 +182,14 @@ static void SignalUnsafeCall(ThreadState *thr, uptr pc) {
   ObtainCurrentStack(thr, pc, &stack);
   if (IsFiredSuppression(ctx, ReportTypeSignalUnsafe, stack))
     return;
-  // Use alloca, because malloc during signal handling deadlocks
-  ScopedReport *rep = (ScopedReport *)__builtin_alloca(sizeof(ScopedReport));
+  ScopedReport rep(ReportTypeSignalUnsafe);
   // Release locks before symbolizing and outputting the report to avoid
   // deadlocks.
   {
-    new (rep) ScopedReport(ReportTypeSignalUnsafe);
     ThreadRegistryLock l(&ctx->thread_registry);
-    rep->AddStack(stack, true);
+    rep.AddStack(stack, true);
   }
-  OutputReport(thr, *rep);
-
-  // Need to manually destroy this because we used placement new to allocate
-  rep->~ScopedReport();
+  OutputReport(thr, rep);
 }
 
 
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp
index f0261b71f7009..8ae9c1f4b20e9 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp
@@ -11,7 +11,6 @@
 //===----------------------------------------------------------------------===//
 
 #include <sanitizer_common/sanitizer_deadlock_detector_interface.h>
-#include <sanitizer_common/sanitizer_placement_new.h>
 #include <sanitizer_common/sanitizer_stackdepot.h>
 
 #include "tsan_flags.h"
@@ -58,21 +57,16 @@ static void ReportMutexMisuse(ThreadState *thr, uptr pc, ReportType typ,
     return;
   VarSizeStackTrace trace;
   ObtainCurrentStack(thr, pc, &trace);
-  // Use alloca, because malloc during signal handling deadlocks
-  ScopedReport *rep = (ScopedReport *)__builtin_alloca(sizeof(ScopedReport));
+  ScopedReport rep(typ);
   // Release locks before symbolizing and outputting the report to avoid
   // deadlocks.
   {
-    new (rep) ScopedReport(typ);
     ThreadRegistryLock l(&ctx->thread_registry);
-    rep->AddMutex(addr, creation_stack_id);
-    rep->AddStack(trace, true);
-    rep->AddLocation(addr, 1);
+    rep.AddMutex(addr, creation_stack_id);
+    rep.AddStack(trace, true);
+    rep.AddLocation(addr, 1);
   }
-  OutputReport(thr, *rep);
-
-  // Need to manually destroy this because we used placement new to allocate
-  rep->~ScopedReport();
+  OutputReport(thr, rep);
 }
 
 static void RecordMutexLock(ThreadState *thr, uptr pc, uptr addr,
@@ -539,17 +533,15 @@ void ReportDeadlock(ThreadState *thr, uptr pc, DDReport *r) {
   if (r == 0 || !ShouldReport(thr, ReportTypeDeadlock))
     return;
   uptr dummy_pc = 0x42;
-  // Use alloca, because malloc during signal handling deadlocks
-  ScopedReport *rep = (ScopedReport *)__builtin_alloca(sizeof(ScopedReport));
+  ScopedReport rep(ReportTypeDeadlock);
   // Release locks before symbolizing and outputting the report to avoid
   // deadlocks.
   {
-    new (rep) ScopedReport(ReportTypeDeadlock);
     ThreadRegistryLock l(&ctx->thread_registry);
     for (int i = 0; i < r->n; i++) {
-      rep->AddMutex(r->loop[i].mtx_ctx0, r->loop[i].stk[0]);
-      rep->AddUniqueTid((int)r->loop[i].thr_ctx);
-      rep->AddThread((int)r->loop[i].thr_ctx);
+      rep.AddMutex(r->loop[i].mtx_ctx0, r->loop[i].stk[0]);
+      rep.AddUniqueTid((int)r->loop[i].thr_ctx);
+      rep.AddThread((int)r->loop[i].thr_ctx);
     }
     for (int i = 0; i < r->n; i++) {
       for (int j = 0; j < (flags()->second_deadlock_stack ? 2 : 1); j++) {
@@ -562,14 +554,11 @@ void ReportDeadlock(ThreadState *thr, uptr pc, DDReport *r) {
           // but we should still produce some stack trace in the report.
           stack = StackTrace(&dummy_pc, 1);
         }
-        rep->AddStack(stack, true);
+        rep.AddStack(stack, true);
       }
     }
   }
-  OutputReport(thr, *rep);
-
-  // Need to manually destroy this because we used placement new to allocate
-  rep->~ScopedReport();
+  OutputReport(thr, rep);
 }
 
 void ReportDestroyLocked(ThreadState *thr, uptr pc, uptr addr,
@@ -593,22 +582,17 @@ void ReportDestroyLocked(ThreadState *thr, uptr pc, uptr addr,
   VarSizeStackTrace trace;
   ObtainCurrentStack(thr, pc, &trace);
 
-  // Use alloca, because malloc during signal handling deadlocks
-  ScopedReport* rep = (ScopedReport*)__builtin_alloca(sizeof(ScopedReport));
+  ScopedReport rep(ReportTypeMutexDestroyLocked);
   // Release locks before symbolizing and outputting the report to avoid
   // deadlocks.
   {
-    new (rep) ScopedReport(ReportTypeMutexDestroyLocked);
     ThreadRegistryLock l0(&ctx->thread_registry);
-    rep->AddMutex(addr, creation_stack_id);
-    rep->AddStack(trace, true);
-    rep->AddStack(last_lock_stack, true);
-    rep->AddLocation(addr, 1);
+    rep.AddMutex(addr, creation_stack_id);
+    rep.AddStack(trace, true);
+    rep.AddStack(last_lock_stack, true);
+    rep.AddLocation(addr, 1);
   }
-  OutputReport(thr, *rep);
-
-  // Need to manually destroy this because we used placement new to allocate
-  rep->~ScopedReport();
+  OutputReport(thr, rep);
 }
 
 }  // namespace __tsan
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp
index 7b86e955300de..52a462617ecb4 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp
@@ -837,46 +837,41 @@ void ReportRace(ThreadState *thr, RawShadow *shadow_mem, Shadow cur, Shadow old,
     }
   }
 
-  // Use alloca, because malloc during signal handling deadlocks
-  ScopedReport* rep = (ScopedReport*)__builtin_alloca(sizeof(ScopedReport));
+  ScopedReport rep(rep_typ, tag);
   // Release locks before symbolizing and outputting the report to avoid
   // deadlocks.
   {
-    new (rep) ScopedReport(rep_typ, tag);
     ThreadRegistryLock l0(&ctx->thread_registry);
     for (uptr i = 0; i < kMop; i++)
-      rep->AddMemoryAccess(addr, tags[i], s[i], tids[i], traces[i], mset[i]);
+      rep.AddMemoryAccess(addr, tags[i], s[i], tids[i], traces[i], mset[i]);
 
     for (uptr i = 0; i < kMop; i++) {
       ThreadContext *tctx = static_cast<ThreadContext *>(
           ctx->thread_registry.GetThreadLocked(tids[i]));
-      rep->AddThread(tctx);
+      rep.AddThread(tctx);
     }
 
-    rep->AddLocation(addr_min, addr_max - addr_min);
+    rep.AddLocation(addr_min, addr_max - addr_min);
 
     if (flags()->print_full_thread_history) {
-      const ReportDesc *rep_desc = rep->GetReport();
+      const ReportDesc* rep_desc = rep.GetReport();
       for (uptr i = 0; i < rep_desc->threads.Size(); i++) {
         Tid parent_tid = rep_desc->threads[i]->parent_tid;
         if (parent_tid == kMainTid || parent_tid == kInvalidTid)
           continue;
         ThreadContext *parent_tctx = static_cast<ThreadContext *>(
             ctx->thread_registry.GetThreadLocked(parent_tid));
-        rep->AddThread(parent_tctx);
+        rep.AddThread(parent_tctx);
       }
     }
 
 #if !SANITIZER_GO
     if (!((typ0 | typ1) & kAccessFree) &&
         s[1].epoch() <= thr->last_sleep_clock.Get(s[1].sid()))
-      rep->AddSleep(thr->last_sleep_stack_id);
+      rep.AddSleep(thr->last_sleep_stack_id);
 #endif
   }
-  OutputReport(thr, *rep);
-
-  // Need to manually destroy this because we used placement new to allocate
-  rep->~ScopedReport();
+  OutputReport(thr, rep);
 }
 
 void PrintCurrentStack(ThreadState *thr, uptr pc) {
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp
index c408024e77e60..5edf0c16fdd0d 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp
@@ -96,20 +96,15 @@ void ThreadFinalize(ThreadState *thr) {
   }
 
   for (uptr i = 0; i < leaks.Size(); i++) {
-    // Use alloca, because malloc during signal handling deadlocks
-    ScopedReport *rep = (ScopedReport *)__builtin_alloca(sizeof(ScopedReport));
+    ScopedReport rep(ReportTypeThreadLeak);
     // Release locks before symbolizing and outputting the report to avoid
     // deadlocks.
     {
-      new (rep) ScopedReport(ReportTypeThreadLeak);
       ThreadRegistryLock l(&ctx->thread_registry);
-      rep->AddThread(leaks[i].tctx, true);
-      rep->SetCount(leaks[i].count);
+      rep.AddThread(leaks[i].tctx, true);
+      rep.SetCount(leaks[i].count);
     }
-    OutputReport(thr, *rep);
-
-    // Need to manually destroy this because we used placement new to allocate
-    rep->~ScopedReport();
+    OutputReport(thr, rep);
   }
 #endif
 }



More information about the llvm-branch-commits mailing list