Attributor: Reuse string attributes when manifesting them
Manifesting a string attribute looked it up by kind and then created
it again from its kind and value, which hashes both strings to find
the uniqued attribute that was already passed in. Add the given
attribute instead. Also don't replace a string attribute with an equal
one, and don't report that as a change.
AMDGPUAttributor manifests about 20 string attributes per function.
Instructions spent in amdgpu-attributor
(opt, parse cost subtracted, median of 3 runs):
rocPRIM test_device_scan, gfx942: 1963M -> 1903M (-3.1%)
Blender Cycles HIP kernel, gfx1100: 636M -> 632M (within noise)
rocPRIM 16 tests, full LTO, closed world: 7415M -> 7198M (-2.9%)
Co-authored-by: Claude Opus 5.5 <noreply at anthropic.com>
[OpenMP][clang] Properly handle nested "teams loop"
Treat a nested "teams loop" directive like a "teams distribute" since
it's lowered as such.
Claude assisted with this patch.
[bolt] Don't compile installed binary with --emit-relocs (#231819)
Bolt has a smoke-test that uses `merge-fdata` as a non-trivial binary.
This requires it to be compiled with `--emit-relocs`. However, this is
also an installed binary, so it ends up installed with relocations.
This always ends up being flagged by our downstream binary analysis
tooling. It would be easy enough to strip relocations and symbols on our
side, but I don't think that the build system should be producing any
"weird" installed binaries. Either we should automatically strip during
installation, or we should not emit relocations for a binary that gets
installed.
I'm using the second approach here, compiling a separate
merge-fdata-for-test binary that does not get installed. I assume that
we're not particularly concerned about the cost of building an
additional tiny binary here.
IR: Speed up AttrBuilder merge
AttrBuilder::merge added the attributes of the other builder one at a
time, each with a binary search and a vector insert into the sorted
list. Both lists are sorted, so merge them in one pass, as the TODO
suggested. This speeds up Attributor manifesting attributes.
Instructions spent in amdgpu-attributor
(opt, parse cost subtracted, median of 3 runs):
rocPRIM test_device_scan, gfx942: 1963M -> 1923M (-2.0%)
Blender Cycles HIP kernel, gfx1100: 636M -> 632M (within noise)
rocPRIM 16 tests, full LTO, closed world: 7415M -> 7273M (-1.9%)
Co-authored-by: Claude Opus 5.5 <noreply at anthropic.com>
[SanitizerCoverage][NFC] Add funclet-bundle.ll and regenerate wineh.ll (#231751)
Precommit tests for #226762, as asked in its review.
`funclet-bundle.ll` is the catch handler from #212404, instrumented with
the options `-fsanitize=fuzzer` uses (level 4, inline 8-bit counters,
trace-compares) and with gated callbacks, where the calls sit in blocks
split off inside the funclet. `wineh.ll` keeps its source and its FIXME
and gets checks regenerated.
Assisted-by: Claude Code (Claude Opus 5.5)
[AMDGPU] Don't apply the memory cluster budget to hard clause formation (#229445)
SIInsertHardClauses reused shouldClusterMemOps() to check whether two
memory operations share a base, and tried to neutralise its
register-pressure budget (MaxMemoryClusterDWords) by passing a fake
2-dword cluster. With "amdgpu-max-memory-cluster-dwords" set below 2 no
clause could form at all. That budget only bounds pre-RA register
pressure and is irrelevant after register allocation.
Add SIInstrInfo::memOpsHaveSameBase() and use it from both places. No
change with the default budget.
[Option] Make OptionsStruct per-context by default (#232170)
Make perContext the default, and opt out the ported libraries that still
read Global with perContext = false. X86Options, X86MCOptions, and
RISCVOptions become per-context.
A TargetMachine and the MC layer have no LLVMContext. Add
MCTargetOptions::LibraryOptions, which llc, opt, and clang set to the
compiled module's LLVMContext. X86TargetMachine, RISCVTargetMachine, and
X86's MCAsmInfo, MCAsmBackend, and MCTargetAsmParser read their options
through LibraryOptionsContext::get, which returns the global values when
it is unset, as in llvm-mc and clang -cc1as.
Aided by Opus 5.5
[CIR] Fix cir.try successor regions
`TryOp::getSuccessorRegions` listed the try region and every handler as
entries from the parent, so a handler could run without the try body. Now
the parent enters the try region, a `cir.resume` ending the try region
unwinds into the handlers, and every other exit leaves the op. This matches
`CIRTryOpFlattening`, which branches such resumes to the handler dispatch
block. A `cir.try` with no handlers has nowhere to unwind to, so a
`cir.resume` in its body leaves the op.
The interface only describes exits through a region terminator. A throw from
a call inside the try body is not one, so it gets no edge, and a handler
reached only that way has no predecessor. The catch-all unit test therefore
runs `verifyControlFlowInterfaceConsistency` with `requireAllReachable` set
to false, which replaces its TODO.
Co-Authored-By: Claude Opus 5.5 <noreply at anthropic.com>
[EarlyIfConversion] Limit critical-path growth for strongly biased branches (#228709)
Early if-conversion now gives a branch no critical-path extension budget
when its colder edge is below a configurable threshold (1% by default),
while other branches keep their existing budget. This prevents a
conversion of heavily biased branches which are likely to be predictable.
On a 3.2 MB bundle of `zstd` source files, this improves decompression
throughput on Apple M1 Pro by 11.6% at level 1 and 14.4% at level 3 when
the decoder uses likelihood hints.
Assisted-by: OpenAI Codex
[CIR] Fix cir.try successor regions
`TryOp::getSuccessorRegions` listed the try region and every handler as
entries from the parent, so a handler could run without the try body. Now
the parent enters the try region, a `cir.resume` ending the try region
unwinds into the handlers, and every other exit leaves the op. This matches
`CIRTryOpFlattening`, which branches such resumes to the handler dispatch
block. A `cir.try` with no handlers has nowhere to unwind to, so a
`cir.resume` in its body leaves the op.
The interface only describes exits through a region terminator. A throw from
a call inside the try body is not one, so it gets no edge, and a handler
reached only that way has no predecessor. The catch-all unit test therefore
runs `verifyControlFlowInterfaceConsistency` with `requireAllReachable` set
to false, which replaces its TODO.
Co-Authored-By: Claude Opus 5.5 <noreply at anthropic.com>
TableGen: Store classes and defs in a StringMap
Classes and defs were kept in a std::map ordered by name, which makes
every lookup a series of string comparisons over names that often share
long prefixes. Store them in a StringMap instead, and have getClasses()
and getDefs() return a list sorted by name, built when first needed
after records are added.
Gigainstructions executed by llvm-tblgen --null-backend
before after
AMDGPU.td 18.17 16.98 -6.6%
X86.td 6.15 5.84 -5.1%
AArch64.td 3.32 3.00 -9.7%
RISCV.td 8.26 7.44 -9.9%
Including the backends, all tablegen runs in a build of all targets and
clang execute 3.1% fewer instructions in total, and none are slower.
Peak memory parsing AMDGPU.td drops from 835 MB to 830 MB.
[2 lines not shown]
TableGen: Cache resolved class instantiations (#231091)
The same class instantiation is often referenced many times in one
record, and each reference was resolved from scratch along with
everything nested in its arguments. Cache the results in MapResolver and
RecordResolver, and share them with any TrackUnresolvedResolver wrapping
those. The cache records whether a result has unresolved references, so
a cache hit is reported to the enclosing trackers.
Gigainstructions executed by llvm-tblgen --null-backend
before after
AMDGPU.td 71.46 17.78 -75.1%
X86.td 6.04 6.09 +0.9%
AArch64.td 3.27 3.31 +1.4%
RISCV.td 8.12 8.21 +1.1%
Co-authored-by: Claude Opus 5.5 <noreply at anthropic.com>
[CIR] Lower undef and poison initializers of any type in CXXABILowering (#230901)
CUDA host shadows of device arrays, pointers and member pointers crashed
the pass
Targets: Use none_of instead of not in AssemblerPredicate conditions (#232033)
Negate features in AssemblerPredicate conditions using the new none_of
instead of not.
Co-authored-by: Claude Opus 5.5 <noreply at anthropic.com>
---
<sub>Stack created with <a
href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a
href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>
---------
Co-authored-by: Claude Opus 5.5 <noreply at anthropic.com>
TableGen: Add none_of for negating features in AssemblerPredicate (#232021)
AssemblerPredicates are currently specified with "all_of", "any_of",
and then "not" as the odd one out. The use of these operators relies
on the "forward declaration" of not, which is not a language feature
tablegen has. This "not" operator isn't actually the same empty record
as all_of/any_of, and is in fact usually supplied by the PatFrag to
match xor -1 in from TargetSelectionDAG.td. The predicate parser just
special cases the name.
Introduce none_of to go with the other predicate operators, which is
more consistent and expressive. This will help cleanup some tablegen
include dependencies, like avoiding redeclaring not in every target's
TargetParser generation.
Co-authored-by: Claude Opus 5.5 <noreply at anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply at anthropic.com>
[CIR][NFC] Use CleanupKindAttr predicates in FlattenCFG (#221234)
`flattenCleanup` still checks the cleanup kind by comparing the raw enum
against `Normal`, `EH` and `All`. `CleanupKindAttr::isNormal()` and
`isEH()` mean exactly that, and the rest of `FlattenCFG` already calls
`isEH()` since #224829.
Assisted-by: Claude Code (Claude Opus 5.5).
[LoongArch] Legalize BUILD_VECTOR into a broadcast when all non-undef elements are identical
When a BUILD_VECTOR consists of the same element (ignoring undefs),
it is better emitting a broadcast instead of multiple insertions.
Some floating-point cases suffer performance regressions, those
specific cases are excluded in this commit. Including when:
- only one element is non-undef,
- only two elements are non-undef, and one of them must at index 0,
- for v8f32 vector type, specially exclude the cases when the only
two non-undefs are at index (1,2)/(1,3)/(2,3).
[CIR] Fix cir.cleanup.scope successor regions (#221222)
`CleanupScopeOp::getSuccessorRegions` listed the body and the cleanup
region as entries from the parent, so the cleanup could run without the
body and no edge led from the body to the cleanup. Now the parent enters
the body, a body exit enters the cleanup region if the cleanup kind
covers that exit, and the cleanup region exits to the parent.
`cir.resume` runs EH cleanups, and `cir.yield` and `cir.co_return` run
normal ones. Take this scope, where `<kind>` is `normal`, `eh` or `all`:
```mlir
cir.cleanup.scope {
cir.brcond %cond ^normal, ^unwind
^normal:
cir.yield
^unwind:
%tok = cir.eh.initiate cleanup : !cir.eh_token
cir.resume %tok : !cir.eh_token
} cleanup <kind> {
[31 lines not shown]
[Option] Add AliasOf for library options (#232174)
An AliasOf keeps an old spelling of a renamed library option, e.g. for
the CodeGenPrepare options that #228838 renamed:
```
def : AliasOf<"cgpp-huge-func", "cgp-huge-func">;
def : AliasOf<"disable-cgp-branch-opts", "cgp-branch-opts=false">;
```
It names its target by spelling, so adding or removing an alias leaves
the target's row as is. Both boolean and non-boolean targets are
supported.
This feature replaces Alias<>, whose only user was
-aarch64-stack-hazard-size.
LLM-aided
TableGen: Avoid linear search for register class names (#232108)
Inferring register classes checked whether a name was already used by
walking the list of all register classes. AMDGPU has thousands of
register classes, so this was a large part of building the register
bank. Keep a set of the names instead.
The 8 AMDGPU tablegen runs that build the register bank each execute
8% to 12% fewer cycles.
Co-authored-by: Claude Opus 5.5 <noreply at anthropic.com>