llvmglue::GlobalStateGuard
Holds LLVM's process-global state for one job, shared or exclusive.
| Declared in | src/libs/libllvm/llvmglue/GlobalStateGuard.hpp |
| Inherited By | llvmglue::CompileGuard and llvmglue::LinkGuard |
Detailed description
LLVM keeps two kinds of state for the whole process that an in-process user cannot make private:
- The value of every `cl::opt`. lld's ELF driver calls `cl::ResetAllOptionOccurrences()` on each link (lld/ELF/Driver.cpp:1798), and lld parses `-plugin-opt` and `-mllvm` into the same options. Some backends write an option while they compile: `AVRTargetLowering` sets "min-jump-table-entries" to UINT_MAX (AVRISelLowering.cpp:202, TargetLoweringBase.cpp:2459).
- lld's linker context, one global pointer (lld/Common/CommonLinkerContext.cpp:23).
A compile on one thread reads options that a link or an AVR compile on another thread writes. The result was an AVR compile that got a jump table its backend cannot select (testFixoHarness, 2026-10-08), and an x86 compile without jump tables after any AVR compile in the process.
A job that only reads takes the guard shared. A job that writes takes it exclusive: nothing else runs while it holds the guard, and when it releases the guard every option goes back to its default. So a shared holder always sees the defaults, and nothing that a link or an AVR compile wrote stays behind.
A waiting exclusive job goes before new shared jobs, so a steady stream of compiles cannot keep a link waiting. A thread that already holds the guard can take it again; a nested take does not wait. A thread that holds it shared must not ask for it exclusive, because that waits for itself: the guard stops the process with a message instead of a deadlock.
Take the guard outside `CrashRecoveryContext::RunSafely()`. A recovered crash leaves the lambda without unwinding, so a guard inside it is never released.