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