[clang] 62f429e - [clang][CodeGen] Skip deleted globals in emitUsed (#210959)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Jul 28 08:12:40 PDT 2026
Author: Emery Conrad
Date: 2026-07-28T18:12:35+03:00
New Revision: 62f429ec6e85224a0f7fa6d4e51595b27e5ac9c1
URL: https://github.com/llvm/llvm-project/commit/62f429ec6e85224a0f7fa6d4e51595b27e5ac9c1
DIFF: https://github.com/llvm/llvm-project/commit/62f429ec6e85224a0f7fa6d4e51595b27e5ac9c1.diff
LOG: [clang][CodeGen] Skip deleted globals in emitUsed (#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)
Co-authored-by: Emery Conrad <emery.conrad at chicagotrading.com>
Added:
clang/test/Interpreter/used-global-after-error.cpp
Modified:
clang/lib/CodeGen/CodeGenModule.cpp
Removed:
################################################################################
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 817b2b5e6a69d..e7c1d182fd20d 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -3703,13 +3703,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
diff erently-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
More information about the cfe-commits
mailing list