https://github.com/conrade-ctc created 
https://github.com/llvm/llvm-project/pull/210959

A global on the `llvm.used`/`llvm.compiler.used` list can be deleted before the 
module is released: CodeGen erases an unreferenced `GlobalValue` without RAUW 
when the same mangled name is redefined with a different type 
(`GetOrCreateLLVMGlobal`), which nulls the `WeakTrackingVH` on the used list — 
value handles are not uses, so the `use_empty()` guard does not protect them.

In whole-TU compilation this is unobservable because the accompanying 
`err_duplicate_mangled_name` suppresses `Release()`. The incremental 
interpreter, however, clears the diagnostic state after the failed parse and 
keeps building the same module, so the next successful parse runs `Release()` 
and crashes in `emitUsed` dereferencing the dead handle:

```
clang-repl> __attribute__((used)) int a asm("sym") = 1; float b asm("sym") = 
2.0f;
clang-repl> int ok = 0;
clang-repl: .../ValueHandle.h:95: llvm::Value& 
llvm::ValueHandleBase::operator*() const: Assertion `V && "Dereferencing 
deleted ValueHandle"' failed.
```

Out-of-tree incremental clients hit the same crash through other routes that 
delete an emitted used global before module release (e.g. CppInterOp's 
force-emission of reflection queries, see compiler-research/CppInterOp#1068).

Fix: skip null handles when building the used array. The repro above is 
included as a lit test; `clang/test/Interpreter`, `clang/test/CodeGen`, and 
`clang/test/CodeGenCXX` pass locally.

🤖 Done with the help of [Claude Code](https://claude.com/claude-code) (Fable 5, 
human in the loop)

>From df8408acd35a0886f18232bff86f1af37035fba9 Mon Sep 17 00:00:00 2001
From: Emery Conrad <[email protected]>
Date: Tue, 21 Jul 2026 07:13:40 -0500
Subject: [PATCH] [clang][CodeGen] Skip deleted globals in emitUsed

A global on the llvm.used/llvm.compiler.used list can be deleted before
the module is released: CodeGen erases an unreferenced GlobalValue
without RAUW when the same mangled name is redefined with a different
type (GetOrCreateLLVMGlobal), which nulls the WeakTrackingVH on the
used list. In whole-TU compilation the error suppresses Release(), but
the incremental interpreter clears the diagnostic and keeps building
the same module, so the next successful parse crashes in emitUsed
dereferencing the dead handle. Skip null handles when building the
used array.

(clang-repl repro included as a lit test.)

Co-developed-with-the-help-of: Claude Code (Fable 5, human in the loop)
---
 clang/lib/CodeGen/CodeGenModule.cpp             | 17 ++++++++++-------
 .../Interpreter/used-global-after-error.cpp     | 15 +++++++++++++++
 2 files changed, 25 insertions(+), 7 deletions(-)
 create mode 100644 clang/test/Interpreter/used-global-after-error.cpp

diff --git a/clang/lib/CodeGen/CodeGenModule.cpp 
b/clang/lib/CodeGen/CodeGenModule.cpp
index 78627047b19ad..ead29559ced51 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -3701,13 +3701,16 @@ static void emitUsed(CodeGenModule &CGM, StringRef Name,
   if (List.empty())
     return;
 
-  // Convert List to what ConstantArray needs.
-  SmallVector<llvm::Constant*, 8> UsedArray;
-  UsedArray.resize(List.size());
-  for (unsigned i = 0, e = List.size(); i != e; ++i) {
-    UsedArray[i] =
-        llvm::ConstantExpr::getPointerBitCastOrAddrSpaceCast(
-            cast<llvm::Constant>(&*List[i]), CGM.Int8PtrTy);
+  // Convert List to what ConstantArray needs. A used global may have been
+  // deleted after it was added to the list (e.g. when its home module keeps
+  // accumulating declarations after an erroneous incremental parse), leaving
+  // a null value handle behind; skip those entries.
+  SmallVector<llvm::Constant *, 8> UsedArray;
+  UsedArray.reserve(List.size());
+  for (const llvm::WeakTrackingVH &VH : List) {
+    if (llvm::Value *V = VH)
+      UsedArray.push_back(llvm::ConstantExpr::getPointerBitCastOrAddrSpaceCast(
+          cast<llvm::Constant>(V), CGM.Int8PtrTy));
   }
 
   if (UsedArray.empty())
diff --git a/clang/test/Interpreter/used-global-after-error.cpp 
b/clang/test/Interpreter/used-global-after-error.cpp
new file mode 100644
index 0000000000000..fc1043042d5cc
--- /dev/null
+++ b/clang/test/Interpreter/used-global-after-error.cpp
@@ -0,0 +1,15 @@
+// REQUIRES: host-supports-jit
+// UNSUPPORTED: system-aix
+// RUN: cat %s | clang-repl | FileCheck %s
+
+// A cast<> of a null WeakTrackingVH used to crash emitUsed when a global on
+// the llvm.used list was deleted before the module was released: the
+// duplicate definition below is diagnosed by CodeGen, which then replaces the
+// used global's unreferenced GV with a differently-typed one; the failed
+// parse keeps the module alive, and the next successful parse finalizes it.
+extern "C" int printf(const char *, ...);
+__attribute__((used)) int a asm("sym") = 1; float b asm("sym") = 2.0f;
+auto r1 = printf("ok = %d\n", 42);
+// CHECK: ok = 42
+
+%quit

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to