[IR] Skip removeDeadConstantUsers use-list walks to save ~0.25% wall time (#240626)
GlobalOpt's `deleteIfDead` calls `removeDeadConstantUsers()` on every
global before checking whether it is dead. For a function, that walks
the whole use list, which is mostly call sites, and loads each user only
to find that it is an instruction. Those loads usually miss in cache. In
AMD IBS profiles of `opt -O2` on four large LLVM TUs (X86ISelLowering,
AArch64ISelLowering, DAGCombiner, ScalarEvolution),
`removeDeadConstantUsers` accounts for 10-19% of the loads served from
L3 or DRAM, but only about 0.5% of cycles.
Only constants that are not globals can be dead users, and with opaque
pointers most functions have none. This patch adds a `GlobalValue` bit,
`MayHaveConstantUsers`. The bit is set when such a constant takes the
global as an operand: in `ConstantUniqueMap::create` and
`replaceOperandsInPlace`, and for `DSOLocalEquivalent` and `NoCFIValue`.
`removeDeadConstantUsers` returns immediately when the bit is clear. It
clears the bit after a walk that leaves no live non-global constant
users. Only those constant-creation paths set the bit, and they are
cold, so `Use::set` and `setOperand` are unchanged. The bit comes from
`GlobalValue`'s subclass data, which shrinks from 15 to 14 bits
(subclasses use at most 11), so `GlobalValue` does not grow. The
Verifier checks that a global without the bit has no non-global constant
users.
Measured on Zen 4 with release builds, over 10 rounds of alternating
base and patched runs:
- `removeDeadConstantUsers` drops from 0.52% to 0.01% of `opt -O2`
cycles on X86ISelLowering.
- Wall time improves by 0.25% ± 0.07% (standard error), pooled across
`opt -O2` and `clang -c -O2` on the four TUs.
- Peak RSS is unchanged, and the `opt -O2 -S` output and `clang -c -O2`
objects are byte-identical to the base.
Compile-time tracker:
https://llvm-compile-time-tracker.com/compare.php?from=db0a0e8065824941ab9ad0de1a36d7ade39348b4&to=5fb2de287bdfec622fdd31fa52e74ea482726e43&stat=instructions:u
LLM-aided