Skip to content

Update to the latest release/23.x branch - #199

Merged
nikic merged 224 commits into
rust-lang:rustc/23.1-2026-07-22from
alexcrichton:update-llvm
Aug 21, 2026
Merged

Update to the latest release/23.x branch#199
nikic merged 224 commits into
rust-lang:rustc/23.1-2026-07-22from
alexcrichton:update-llvm

Conversation

@alexcrichton

Copy link
Copy Markdown
Member

This PR is the result of a git merge origin/release/23.x which updates this branch to the latest version of the upstream release/23.x branch.

For rationale of this, this notably includes llvm#217096 which lays some groundwork for the upcoming wasip3 target.

aokblast and others added 30 commits July 22, 2026 13:42
…m#208356)

762b77a moved the definition of "xindex" out of the header, and in the
process dropped the _LIBCPP_HAS_THREADS check. Re-add the check to
maintain the status quo.

The discussion on llvm#198994
indicates it's not clear whether LIBCXX_ENABLE_THREADS=OFF is actually
supposed to mean single-threaded. But it clearly does in practice:
atomic_support.h uses non-atomic ops when threads are disabled, and a
few other APIs have explicit non-atomic fallback paths.

My team ran into this trying to run libc++ tests for a RISC-V core
without the "a" extension.

(cherry picked from commit 3eb929b)
…ates (llvm#209693)

This fixes another case of member functions where we overlooked template
depths when only abbreviated template parameters are involved.

This mirrors previous fix cfb2520, but I don't intend to put it in
ParseTrailingRequiresClause because we might want the similar fix for
e.g. noexcept expressions, so let's keep it inline for future refactor.

The example comes from llvm#205557.

(cherry picked from commit 3485d85)
…delete (llvm#209585)

PR llvm#188372 made Clang's MSVC-ABI deleting-destructor path route global
deletes
through compiler-generated __global_delete / __global_array_delete
wrappers
instead of referencing ::operator delete directly. This lets a deleting
destructor be emitted in environments (e.g. kernel mode) where no global
::operator delete exists: each wrapper defaults to a trapping
__empty_global_delete fallback, and a real forwarding body that calls
::operator delete is materialized only when the program actually uses
::delete.

This change refines how those wrappers and their fallbacks are emitted,
fixing
two problems that made the llvm#188372 mechanism fail in practice.

New behavior:

- The trapping fallback is now emitted as a weak GlobalAlias to
__empty_global_delete rather than via an /alternatename directive. This
lowers
to a COFF weak-external-with-default -- exactly what MSVC emits -- so a
real
__global_delete defined in any TU overrides the fallback in every other
TU at
  link time.

- A ::delete expression now registers the wrapper for its resolved
global
operator delete directly (in EmitCXXDeleteExpr), so the strong
forwarding body
is emitted in that TU regardless of where the class's deleting
destructor is
defined. This matches MSVC, which emits the forwarder at every ::delete
site.

Bugs fixed:

1. Arm64EC (miscompile -> LNK2019). /alternatename only names the plain
symbol,
not the backend-generated "$$h" hybrid EC symbol referenced by the exit
thunk, so Arm64EC images left __global_delete$exit_thunk unresolved. The
weak
GlobalAlias correctly produces the "$$h" symbol, exit thunk, and plain
alias
   (verified on both x64 and Arm64EC).

2. Cross-TU ::delete (runtime crash). Previously the forwarding body was
only
emitted in a TU that *both* emitted a matching vector deleting
destructor and
contained a ::delete. A TU that only performed `::delete p` -- with the
class
defined elsewhere -- emitted no forwarder, so nothing overrode the
weak-alias
trap and the program executed a trapping __empty_global_delete at
runtime
(STATUS_ILLEGAL_INSTRUCTION). Registering the wrapper at the ::delete
site
   fixes this.

To share wrapper-creation logic between the deleting-destructor path and
the
delete-expression path, getOrCreateMSVCGlobalDeleteWrapper is promoted
from a
static helper in CGClass.cpp to a CodeGenModule method.

Verified end-to-end: a weak alias in one TU and a strong forwarder in
another
resolve to the forwarder under both lld-link and MSVC link.exe, and the
previously-crashing delete-only TU now runs cleanly.
Similar to llvm#209194, this was previously hitting an error with an illegal
FP_ROUND_MERGE_PASSTHRU. This time we lower the fptrunc to a scalable
fptrunc and let it legalize naturally. On SVE systems a f64->bf16
fptrunc will fail to legalize, but lowers successfully with sve2 with
fcvtn. A store(fptrunc) -> truncstore combine was disabled too as it
fails with bf16.

(cherry picked from commit 50f2b94)
…g exit thunks (llvm#209581)

ARM64EC vararg functions receive all types of the first four arguments
in x0-x3. Because x86_64 expects floating-point arguments in FP
registers, always copy x0-x3 to d0-d3 in the exit thunks, matching
MSVC's behavior.

(cherry picked from commit 9c6a9f5)
We found out that the new mechanism for detecting overriden functions
does not support Arm Pointer Authentication (PAuth). Addressing this
limitation is going to require changes to Clang. In the meantime, we
switched back to the old mechanism when PAuth is enabled.

(cherry picked from commit db7356d)
…t library (llvm#210311)

In mingw mode, when linking against a DLL, the user can either provide a
regular import library, or provide the actual DLL. When linking against
an ARM64X image, add both native and EC views to the symbol table on EC
targets. Also getMachine() on such images returns ARM64X, treat it as
ARM64 instead.

(cherry picked from commit 30f270e)
…#210347)

The PR llvm#208552 has introduced a regression where brace initialization
was not taken into account `auto foo{12}`. It was also breaking
`dcl.type.general` p2 rules, which is also now tested.

(cherry picked from commit d592aa5)
…vm#210151)

ActOnUninitializedDecl dereferenced the std::optional<Token> from
Lexer::findNextToken() unconditionally when diagnosing a structured
binding with no initializer. Guard the optional and fall back to the
declaration's location.

(cherry picked from commit 0e3852f)
Add DAG pattern to recognize the optimized IR form of BLSIC: (xor (and
x, -x), -1)

Fixes llvm#209718 - matches the IR form produced after InstCombine
optimizations

Added tests for both 32-bit and 64-bit BLSIC patterns

Assisted by: Claude Code, helped me understand
pattern-matching/intrinsics, and some file structure. As well as how
testing for LLVM works.

(cherry picked from commit 466503d)
Addressing regression introduced by llvm#205844 in which a significantly
slower SVE tail loop is generated.

The cost model for the case where the interleave factor is larger than
the VF has been adjusted to more accurately reflect the cost of the uzp
instructions generated by the deinterleave tree, and the cost of
legalizing the type of each subvector.

(cherry picked from commit 79e05f4)
…lignment

A destination alloca's alignment could have been unconditionally
overwritten, letting a lower-aligned source incorrectly undo a
previous alignment increase. This issue has been addressed by
considering the maximum alignment between the new source target
and current destination, adhering to what the existing comment
already promises.
…MOVMSK signbit reduction (llvm#210281)

VectorCombine may have folded:
  icmp_eq(vecreduce_or(splatsign(x)),0) --> icmp_sgt(vecreduce_umax(x),-1)

which DAG folds to:
  srl(vecreduce_umax(x),bw-1).

This match attempts to lower:
  srl(vecreduce_umax(x),bw-1) --> icmp_ne(movmsk(x),0) "any_of negative"
  srl(not(vecreduce_umax(x)),bw-1) --> icmp_eq(movmsk(x),0) "none_of negative"

The correct fix would be to improve vecreduce_or costs to prevent
VectorCombine doing this, but that change is far too big to be merged
into 23.x - so I've created the narrow backend fix.

Fixes llvm#209714

(cherry picked from commit 8abc269)
As in title. This adds support for using apxf in attribute target.
Individual features are not supported for FMV but are supported for
enabling a feature for the function

Patch done with usage of Claude Code.

(cherry picked from commit 1d55a81)
…210570)

This was introduced as part of llvm#94977. Rather than suppressing the
warning with `#pragma clang diagnostic ignored`, we use
`reinterpret_cast` to avoid the implicit conversion.

(cherry picked from commit 16ffd27)
)

CompileUnit::getDirAndFilenameFromLineTable reads and mutates the unit's
FileNames DenseMap without synchronization. During the parallel
type-name assignment phase a unit's cache is touched both by its own
worker and, through cross-unit type-name references
(addReferencedODRDies calling addDieNameFromDeclFileAndDeclLine) by
other units' workers.

The concurrent find/insert/grow corrupts the map and trips the
assertion:

```
Assertion failed: (TheBucket), function findBucketForInsertion, DenseMap.h
```

Guard the cache with a mutex. Store each entry in a heap-allocated pair
so the StringRefs handed back to callers stay valid across a concurrent
rehash. Otherwise a short (small-string-optimized) file name would move
when another insertion grows the map, dangling a StringRef already
returned.

(cherry picked from commit 499b69c)
…mpound statement (llvm#209229)

<https://eel.is/c++draft/stmt.expand#nt:expansion-statement>:

_expansion-statement_:
template for ( _init-statement<sub>opt</sub>_ _for-range-declaration_ :
_expansion-initializer_ ) _compound-statement_

(cherry picked from commit d13b862)
This will cause the TokenFactor to be split into nodes of at most
SDNode::getMaxNumOperands() size.

This fixes llvm#189161 but I have not added a test case as the output is
in excess of 160000 lines long.

(cherry picked from commit ba83659)
`Process::GetState()` calls `PolicyStack::Get().Current()` on every
prompt redraw, so the previous unconditional LLDB_LOG at the read site
fired on every keypress once `log enable lldb process` was on, drowning
out any other process log output. Keep the dump for when it's actually
wanted, but only fire it if the log is set to verbose.

Signed-off-by: Med Ismail Bennani <ismail@bennani.ma>
(cherry picked from commit c7a0afe)
…r array-to-pointer decay (llvm#210649)

ProcessInitializer() strips implicit casts from a CXXCtorInitializer's
init expression via IgnoreImplicit(), then decides whether to treat the
initializer as a direct array-to-array member copy by checking
Init->getType()->isArrayType(). For a pointer member initialized via
array-to-pointer decay of a reference-to-array constructor parameter
(e.g. `Foo(T (&arr)[N]) : ptr_(arr) {}`), IgnoreImplicit() strips the
ArrayToPointerDecay cast, exposing the underlying array-typed
expression, so this check misfires even though the field itself is a
pointer, not an array. That branch fetches the raw region address of the
whole array, bypassing the normal decay logic (which produces an
ElementRegion), so the pointer member ends up holding the address of the
whole array typed as the array itself, instead of an ElementRegion at
index 0.

Later, dereferencing and storing through that mistyped pointer routes
into RegionStoreManager::bindArray() (instead of bindScalar()), which
unconditionally casts its Init value to nonloc::CompoundVal, asserting
in a debug build and segfaulting in a release build when Init is
anything else, e.g. a nonloc::LocAsInteger produced by round-tripping a
pointer through an integer type.

Fix the actual bug by checking the field's type instead of the
initializer expression's type. Also generalize bindArray()'s existing
guard (added by llvm#178923 for issue llvm#178797) from an enumeration of
specific SVal kinds to the same exhaustive
`!isa<nonloc::CompoundVal>()` check already used by its siblings
bindStruct() and bindVector(), so it doesn't need to be extended again
every time a new SVal kind reaches this path -- this is what actually
catches our case (nonloc::LocAsInteger), which the prior enumeration
didn't cover.

This is the same underlying bug behind llvm#147686 (fixed by llvm#153177, which
its own author noted was "more of a workaround") and llvm#178797 (fixed by
llvm#178923); both those fixes patched symptoms at bindArray() without
addressing the ProcessInitializer() root cause. Fixing the root cause
also resolves two FIXME-annotated precision gaps in
clang/test/Analysis/initializer.cpp's gh147686 regression test.

Fixes llvm#210183

AI tool use disclosure: Claude Code (Anthropic) assisted in reducing the
original crash to a minimal, dependency-free reproducer (via creduce
plus manual bisection, verifying each reduction step against the actual
crash), which informed root-causing this bug in
RegionStoreManager::bindArray and ExprEngine::ProcessInitializer. The
commits made here were drafted with Claude's assistance and reviewed by
me before being pushed. I've reviewed all AI-assisted contributions here
and take full responsibility for the correctness of this change.

---------

Co-authored-by: Andy Ames <andy.ames@joby.aero>
Co-authored-by: Balázs Benics <benicsbalazs@gmail.com>
(cherry picked from commit 5b1fa37)
…llvm#210167)

When `std::get` is called on a dereferenced integer-to-pointer cast, the
checker `alpha.core.StdVariant` crashes. Minimal reproducer:

```
std::get<int>(*(std::variant<int, char> *)11);
```

Godbolt reproducer - https://godbolt.org/z/4EKe1PrKb

The root cause is that `StdVariantChecker::handleStdGetCall()` calls
`SVal::getType()` on any non-unknown argument, then calls
`getPointeeType()` on the result while assuming it is a pointer type. In
the case of a concrete integer cast to a pointer and then dereferenced,
it is modeled as `loc::ConcreteInt`, whose recovered type is an integer.
`getPointeeType()` on such an input returns a null QualType, on which
`getTypePtr()` crashes.

Fix the crash by using the argument's static type rather than recovering
its SVal, eliminating the need to guard and call `getPointeeType()`.

(cherry picked from commit accd1ed)
Ensure that `llvm-readobj` / `llvm-readelf` processes and dumps all
SHT_LLVM_CALL_GRAPH sections present in an object file, rather
than just the first one.

**Motivation:**
Consider this `foo.s` file
```
.pushsection .text, "axG", %progbits, foo
  .globl foo
  foo:
  ret
  .pushsection .llvm.callgraph, "?", %llvm_call_graph
    .byte 0, 0
    .dc.a foo
    .quad 0
  .popsection
.popsection

.pushsection .text, "axG", %progbits, bar
  .globl foo
  bar:
  ret
  .pushsection .llvm.callgraph, "?", %llvm_call_graph
    .byte 0, 0
    .dc.a bar
    .quad 0
  .popsection
.popsection
```
`clang -c foo.s && llvm-readelf --elf-output-style=JSON --pretty-print
--call-graph-info foo.o` on that shows that there is only one record
reported because the current implementation just looked at the first
SHT_LLVM_CALL_GRAPH section and then ignored all others. This patch will
address this gap by iterating over all the SHT_LLVM_CALL_GRAPH sections.

(cherry picked from commit eb76774)
…nsts (llvm#210955)

`expandCCOpToCMov` was only accepting signed 5-bit immediates before
forming `Xqcicm` conditional-move pseudos. Valid `uimm5` operands for
unsigned compare forms such as `QC_MVGEUI` and `QC_MVLTUI` were not
being handled leading to a crash.

Track whether the selected conditional-move opcode expects a signed or
unsigned immediate, and validate the RHS against the matching range.

(cherry picked from commit 9bcb851)
…llvm#211019)

The pre-RA Zilsd optimizer allowed a pair when both stored values came
from the same virtual register if that virtual register was defined by a
copy from X0. This is only valid when the virtual register class can
actually contain X0.

Check the virtual register class before treating the value as `x0_pair`.
This prevents forming an invalid paired store for register classes such
as `GPRNoX0`.

Without this change we were hitting the following assertion in
`RISCVLoadStoreOptimizer`:

```
assert(
    FirstReg != SecondReg &&
    "First register and second register is impossible to be same register");
```

(cherry picked from commit 6b408bf)
…wering (llvm#209701)

**Summary**

This regression was introduced by llvm#196094, which added a special
lowering path for reductions on a single array element, such as `a(2)`.

The problem is that Flang also treated an array section like `a(2:96)`
as if it were a single element. Because of this, the section was sent to
a code path that only supports scalar elements.

That path produced an array type that the reduction initialization code
could not handle, so Flang reached a `TODO` and aborted with a “not yet
implemented” error.

**Fix**

The fix is to use the special element path only when the expression has
rank 0, which means it represents one single value. Array sections have
rank greater than 0, so they should continue through the existing
boxed-array path.

Fixes : [209462](llvm#209462)

---------

Co-authored-by: Jay Satish Kumar Patel <kumarpat@pe31.hpc.amslabs.hpecorp.net>
(cherry picked from commit 6c560cd)
)

This was previously not required, but the patch to introduce a new
size-based vector layout unintentionally added this new requirement. We
almost certainly not want to promise this guarantee going forward, but
we should actually land this change explicitly and consider the
transition story, not do it as a fallout of another refactoring.

Fixes llvm#210732

(cherry picked from commit 17ac8fd)
We need to checkout the upload-release-artifact composite action before
using it.

(cherry picked from commit 8ad500f)
rorth and others added 24 commits August 18, 2026 08:22
Several sanitizer tests hang indefinitely on NetBSD:

```
MemorySanitizer-Unit :: ./Msan-x86_64-Test
MemorySanitizer-Unit :: ./Msan-x86_64-with-call-Test
MemorySanitizer-X86_64 :: zero_alloc.cpp
ThreadSanitizer-x86_64 :: signal_cond.cpp
libFuzzer-x86_64-default-NetBSD :: reload.test
```

All of them loop and don't time out, so they need to be terminated
manually for `ninja check-all` to complete. To avoid this, this patch
skips the affected tests or subtests. Unfortunately, the Msan unit tests
still hang on exit in `__cxa_finalize` even if all subtests are skipped
with `llvm-lit -gtest_filter=-*`.

Tested on `x86_64-pc-netbsd11.0`, `x86_64-pc-freebsd15.1`, and
`x86_64-pc-linux-gnu`.

(cherry picked from commit 782f113)
When fix-its are emitted into a .dia, we previously stored the fix-it
text in a blob of arbitrary size, but the size of the text was stored in
a separate 16 bit field. For really large fix-its, the size won't fit it
those 16 bits, and the compiler crashes.

Switch to a variable length encoding of the fit-it size. This is similar
to this issue:
llvm@e26aea5

We also remove a hard coded check in the clang library used to load .dia
files that failed for fix-its whose size doesn't fit in 16 bits.

Generated with codex

Co-authored-by: Nuri Amari <nuriamari@fb.com>
(cherry picked from commit bb123b2)
Revert llvm#196094 and its follow-up llvm#209701. The expression override
mechanism does not preserve reduction-object identity across all data
environments, leading to incorrect lowering for procedure-local and
nested private arrays.

Restore the pre-llvm#196094 lowering. Keep coverage showing that
array-element constructs compile through the boxed-array reduction
path.

This was in response to this comment:
llvm#196094 (comment)

Fixing exactly the bug in the comment wasn't hard but AI code review
found a large number of follow on bugs so I think the design needs a
rethink, and definitely shouldn't be included in the LLVM release.

Assisted-by: Codex
Task and taskloop array-element reductions can introduce both a
reduction block argument and an implicit firstprivate block argument for
the base array. Sequential symbol binding can then select the wrong
argument for references in the construct body.

Reject these cases until lowering can distinguish the reduction element
from other uses of the base array. Keep supported array-element reduction
coverage in the existing test and move the unsupported task forms to
focused TODO tests.

Assisted-by: Codex
…eadsInfo (llvm#212706)

Add `ReadFrameZeroStackMemory`, which expedites the innermost frame's stack
memory so a variables view on a stop is served from lldb's memory cache.  When
frame 0's `$fp` looks usable, two windows are expedited:

  * `[$fp + 2*ptr_size, $fp + 2*ptr_size + k_expedite_stack_arg_size)` for stack-passed
     parameters, starting above the saved `{fp, lr}` pair the backchain already covers.
  * `[$fp - below, $fp)`, `below = min($fp - $sp, k_expedite_stack_window - k_expedite_stack_arg_size)`,
     for locals and spilled register arguments.  A small frame gets all of `[$sp, $fp)`; a large one keeps the part nearest `$fp`, so the cost stays bounded.

If `$fp` fails validation (frameless leaf, or `$fp` used as a scratch GPR),
a single `[$sp, $sp + k_expedite_stack_window)` window is expedited instead.

Each window is a separate chunk, because lldb's L1 cache only serves reads
fully contained in one expedited chunk.  Only the thread that stopped gets
these windows, so the stop reply does not grow with thread count.

`GetJSONThreadsInfo` now builds the `"memory"` array from both sources and
emits it whenever either produced an entry.  Add `JSONGenerator::Array::empty`
for that check.

(cherry picked from commit b631e0c)
Hexagon implements KCFI operand-bundle lowering in the back end
HexagonTargetLowering::EmitKCFICheck emits a KCFI_CHECK pseudo, which
HexagonAsmPrinter::LowerKCFI_CHECK expands into a type-hash check and a
trap - like PS_crash.

Hexagon was never added to the list in addKCFIPass() of targets whose
back end lowers the bundles, so Clang kept running the middle-end
KCFIPass for it.

Add Hexagon to the addKCFIPass() early-return so the "kcfi" bundles
reach the back end, which then emits the trapping load that actually
blocks the call.

(cherry picked from commit 23a601d)
…ation (gcc 7.5.0) (llvm#211331)

GCC 7.5.0 fails to compile this code. Use explicit upcasts from
std::unique_ptr<Derived> to std::unique_ptr<Base> in deserializeSummary
and deserializeAnalysisResult. This resolves a compilation error where
llvm::Expected<std::unique_ptr<Base>> could not be constructed from
unique_ptr of derived summary/result types.
lldb.SBValue.format should map to GetFormat instead of GetName

(cherry picked from commit 91ba38d)
…llvm#216730)

`visitSelectInst` folds:

    select(mask, masked.load(ptr, mask, PT), FV)

into:

    masked.load(ptr, mask, FV)

The replacement load was previously created at the select, effectively
moving the memory access past any intervening instructions. If one of
them writes the loaded memory, the replacement load reads the updated
value instead of the original one. This was also observed downstream in
[ispc/ispc#3891](ispc/ispc#3891).

The fold was added in `eb8589987267`. The issue is labelled
`regression:22`, so it affects LLVM 22.1 as well as current trunk.

Create the replacement load at the original load's position and require
`FV` to be available there. Otherwise, leave the select unchanged.
Requiring `FV` to be available at the original load means the fold no
longer fires when `FV` is computed between the load and the select. No
existing `llvm/test/Transforms` checks change as a result of this
restriction.

Tests cover an intervening aliasing store and the case where `FV` is
unavailable at the original load. They also guard against carrying over
call-site attributes such as `range` and `noundef` when those attributes
no longer apply.

Fixes llvm#215453

(cherry picked from commit fec2cba)
…lvm#216318)

`MemoryCache::Read` fetches a whole L2 cache line for any read that fits
in one,
so reading a few bytes caches the line around them. `ReadRanges` probed
only L1,
and re-fetched ranges that line already held. Callers hit this whenever
they
read an array's header and then batch the elements that follow it in the
same
line, as `AppleObjCRuntimeV2::SharedCacheImageHeaders` and
`ClassDescriptorV2::method_list_t` both do.  llvm#201166 uses MemoryCache in
`Process::ReadRangesFromMemory`, but I didn't see why is L1 used only.

Add `FindL2CacheEntry`, a lookup that never reads from the inferior, and
consult
it after L1. When it serves every range in a batch, `ReadRanges` returns
without
calling `Process::DoReadMemoryRanges`, so no packet is sent. As in the
L1 lookup
a range spanning two lines is a miss, and a partially read line is used
only up
to what it holds.

Over the region
`TestObjCMethodsNSError.test_runtime_types_efficient_memreads`
brackets, `MultiMemRead` drops from 190 packets to 107 and the ranges
they carry
from 7004 to 6411, with the `m`/`x` count unchanged at 856. That test
now also
requires no read range to be contained in one an earlier packet already
read,
which counted 593 ranges before this change and none after.

`TestReadMemoryRangesUsesL2Cache` covers the lookup directly.

(cherry picked from commit 797a057)
Only alloca or poison is allowed as arguments for lifetime intrinsics.
Before replacing, check if the operand can be replaced with a variable
and skip replacement if it is not valid.

Fixes a verifier crash in the added test case.

PR: llvm#216991
(cherry picked from commit 13034ef)
…lvm#208263)

This commit is a change to `wasm-ld`'s behavior when the
`--cooperative-threading` flag is passed to the linker. The change here
is to by default work as if `--export-table` was passed as well. This is
required conventionally on this target because the table is where
function pointers are read from in the component model
`thread.new-indirect` intrinsic. If the table is not exported then
there's no way to turn the core module into a component so it's
effectively required. This behavior only applies to when the table isn't
otherwise imported, for example in shared libraries.

The other motivation behind this change is that it'll avoid the need to
manually specify `-Wl,--export-table` when compiling for the
`wasm32-wasip3` target. This additionally avoids the need for the Clang
driver to figure out if flags like `--import-table` were otherwise
passed. Basically it seemed best to put this in `wasm-ld` itself to
avoid as little juggling of pieces as necessary.

cc WebAssembly/wasi-libc#808
…llvm#206831)

With `--gc-sections` (the default), `wasm-ld` garbage-collects functions
that are only reachable through `__wasm_get_tls_base` /
`__wasm_set_tls_base` in the cooperative-threading (libcall
thread-context) configuration. This produces a linked module that is
invalid or behaves incorrectly: the relocation inside
`__wasm_set_tls_base` is left dangling / mis-resolved, so callers trap
at runtime (e.g. `validation error: ... values remaining on stack at end
of block`, or a call to an unrelated function).

In cooperative-threading mode (`--cooperative-threading`, added in
llvm#200855), per-task thread context is accessed through libcalls rather
than wasm globals. `wasm-ld` synthesizes `__wasm_init_tls` /
`__wasm_init_memory`, which invoke `__wasm_get_tls_base` and
`__wasm_set_tls_base` via **raw `call` instructions that carry no
relocations**. To keep those accessors in the output, the linker marks
them live with `Symbol::markLive()`.

## Error and Repro

```bash
Error: failed to compile: wasm[0]::function[16]::__wasm_set_tls_base

Caused by:
    0: WebAssembly translation error
    1: Invalid input WebAssembly code at offset 778: type mismatch: values remaining on stack at end of block
```

import was dropped and __wasm_set_tls_base was rewritten from

```wat
(func $__wasm_set_tls_base (param i32)
  local.get 0
  call $__wasm_component_model_builtin_context_set_1)   ;; correct
```

to

```wat
(func $__wasm_set_tls_base (param i32)
  local.get 0
  call $__wasm_component_model_builtin_context_get_0)   ;; wrong: get_0 is () -> i32
```

Minimal linker-only repro (no runtime needed) is:
```bash
wasm-ld --cooperative-threading tls.o libc.a   # context-set-1 dropped, set_tls_base calls get_0
wasm-ld --cooperative-threading --no-gc-sections tls.o libc.a   # correct
```

## Root cause

`Symbol::markLive()` sets the live flag (and the chunk's live bit) but
does not push the defining chunk onto the mark queue. The mark phase
only follows relocations of chunks that were enqueued via
`MarkLive::enqueue()`. As a result the accessors' own relocations are
never traversed.

This is the same situation already handled for constructors reached
through the relocation-less `__wasm_call_ctors`, which `MarkLive::run()`
enqueues explicitly.

## Fix

In `MarkLive::run()`, enqueue the defining chunks of
`__wasm_get_tls_base` and `__wasm_set_tls_base` before `mark()`, so
their relocations are followed.

```cpp
for (Symbol *sym : {static_cast<Symbol *>(ctx.sym.getTLSBase),
                    static_cast<Symbol *>(ctx.sym.setTLSBase)})
  if (sym)
    if (InputChunk *c = sym->getChunk())
      enqueue(c);
```

The symbols are only set in the libcall-thread-context configuration and
are null otherwise, so the loop is a no-op for all other builds.

## Testing

Adds `lld/test/wasm/cooperative-threading-gc.s`, which links with
`--cooperative-threading --gc-sections` and checks (via `obj2yaml`) that
the import called by `__wasm_set_tls_base` survives GC. Without this
change the test drops the import and rewrites `__wasm_set_tls_base` to
call an unrelated function; with it the import is retained and the call
is correct.

```sh
ninja check-lld-wasm
```

## Note

Human-in-the-loop with claude opus 4.8. I iterated with this patch and
got to a working component running with a wasi-sdk fork.
This commit fixes a few issues that have surfaced in `wasm-ld`'s
handling of `--cooperative-threading` with `-shared`. Two primary issues
fixed are:

* The `__wasm_init_memory` function was not valid as it referenced
nonexistent locals. This was fixed by adjusting how locals are declared
to make this a bit more flexible.
* Combining data segments for PIC without extended-const is generalized
to only operate on active data segments and is now orthogonal to
threading. With coop threads there's a mixture of passive/active
segments (TLS is passive, other data is active) and the active segments
still need combining while TLS stays passive.

The latter fix ended up touching a few more areas. The first is that
`.tdata` sections are now sorted just before `.bss`, the end section,
rather than first. This is done to ensure that active segments when
combined can indeed start at a relative address of 0 (as required
without extended-const). This change resulted in a number of adjustments
for tests as the data is now sorted differently. Additionally
relocations in TLS segments needed adjusting since the previous
implementation implicitly only worked if `.tdata` is first.
This commit updates how `wasm-ld` initializes the `__init_tls_base`
global during module instantiation. Previously this global was left
entirely unmodified meaning that it was always 0. This change updates
the `__wasm_init_memory` function to set this global dynamically in PIC
mode based on the TLS address calculation, or in non-PIC mode the
generation of `__wasm_init_memory` correctly sets it to the desired TLS
address.

cc WebAssembly/wasi-libc#819
…#215548)

The fold here for (abs (sub x y)) -> (abdu x y) was proven in Alive,
assuming that both operands had a sign bit of zero. However, the code
was checking if x had a sign bit of zero and y had a sign bit of 1

Fixes llvm#214942

Original Alive proof from
llvm#186659 :
https://alive2.llvm.org/ce/z/HfPF5q
A variant that's explicitly (abs (sub x y)):
https://alive2.llvm.org/ce/z/QEgDaa
And changing the range to 32770 or higher there will break the
transformation

(cherry picked from commit 93030c3)
a118910 added B.W to v8-M Baseline, relaxing t2B, tCBZ and tCBNZ
from Requires<[IsThumb2]> to Requires<[IsThumb, HasV8MBaseline]>, and
enabled tail calls for it. It missed tTAILJMPd, which expands to t2B but
still required IsThumb2, so emitting a tail call for a Thumb1 MachO
target failed:
> LLVM ERROR: Attempting to emit tTAILJMPd instruction but the
Feature_IsThumb2 predicate(s) are not met

(cherry picked from commit 0ef7e0c)
Repro:

```llvm
target triple = "s390x-linux-gnu"

define <2 x half> @fabs_v2f16(<2 x half> %arg) "target-cpu"="z16" {
  %bits = bitcast <2 x half> %arg to <2 x i16>
  %is_neg = icmp slt <2 x i16> %bits, zeroinitializer
  %neg = fneg <2 x half> %arg
  %res = select <2 x i1> %is_neg, <2 x half> %neg, <2 x half> %arg
  ret <2 x half> %res
}
```
```console
$ llc fp-half-vector-abs.ll
llc: llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp:7343: SDValue llvm::SelectionDAG::getNode(unsigned int, const SDLoc &, EVT, SDValue, const SDNodeFlags): Assertion `N1.getValueType().bitsGT(VT) && "Invalid truncate node, src < dst!"' failed.
```

Previously this code would generate:

    i16 = bitcast f16
    i32 = truncate i16

Which would immediately assert due to the invalid truncate, but even
with that fixed it would assert later due to the illegal i16. Instead,
use the same lowering as `LowerOperationWrapper` for the bitcast.

(cherry picked from commit 1c51891)
…or operand scalarization (llvm#216136)

PowerPC registers v1i128 as a legal type when P8Altivec is available.
When lowering <4 x fp128> comparisons, the type legalizer hits a v1i1
only assert. Generalize the assert to accept any single-element vector
result type.

(cherry picked from commit 9f4703c)
…de (llvm#215929)

When extended-const is enabled, non-TLS global relocations are handled
via extended constant expressions. However, TLS GOT entries still
require runtime relocation code in `__wasm_apply_global_tls_relocs`
because TLS global addresses depend on `__tls_base`.

Allow `GlobalSection::generateRelocationCode` to be called when `TLS` is
true even if `ctx.arg.extendedConst` is set.

(cherry picked from commit 82ea231)
…dependently (llvm#217404)

We were assuming that both conversions were for the same integer
signedness

Fixes llvm#217355

(cherry picked from commit fc70e3d)
…#216665)

MIPS branch/jump instructions (B, BEQ, JALR64Pseudo, PseudoReturn64,
etc.) have a delay slot. The actual encoded size is 8 bytes (instr +
NOP). This fixes "out of range PC16 fixup" errors on large functions.

This issue was exposed in llvm 23 by commit pr llvm#191460 which changed
MipsBranchExpansion to use MBB::iterator instead of instr_iterator,
making the MBB size calculation more accurate and revealing the
pre-existing bug.

Thanks for the pr llvm#187703 `AllowOverEstimate` to help find instr which
actual size mismatch expected size .

Fix llvm#112010.

(cherry picked from commit a97f512)
@alexcrichton
alexcrichton requested a review from nikic as a code owner August 20, 2026 21:25
@nikic
nikic merged commit 16696ad into rust-lang:rustc/23.1-2026-07-22 Aug 21, 2026
66 checks passed
alexcrichton added a commit to alexcrichton/rust that referenced this pull request Aug 21, 2026
This pulls in rust-lang/llvm-project#199 which updates the revision of
LLVM 23 to the latest copy of the release branch, just past the rc3 release.
alexcrichton added a commit to alexcrichton/rust that referenced this pull request Aug 21, 2026
This pulls in rust-lang/llvm-project#199 which updates the revision of
LLVM 23 to the latest copy of the release branch, just past the rc3 release.
Kobzol pushed a commit to Kobzol/rust that referenced this pull request Aug 21, 2026
This pulls in rust-lang/llvm-project#199 which updates the revision of
LLVM 23 to the latest copy of the release branch, just past the rc3 release.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.