# \`MutableSpan\` comes with performance penalties compared to \`UnsafePointer\`s

**URL:** <https://forums.swift.org/t/mutablespan-comes-with-performance-penalties-compared-to-unsafepointer-s/87385>\
**Category:** Standard Library\
**Created:** [June 15, 2026, 1:44pm UTC](https://forums.swift.org/t/mutablespan-comes-with-performance-penalties-compared-to-unsafepointer-s/87385 "2026-06-15T13:44:15Z")\
**Posts on this page:** 18\
**Page:** 1

<div class="post-metadata">

**Author:** ![MahdiBM](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mahdibm/32/45271_2.png) [@MahdiBM](https://forums.swift.org/u/MahdiBM)\
**Post date:** [June 15, 2026, 1:44pm UTC](https://forums.swift.org/t/mutablespan-comes-with-performance-penalties-compared-to-unsafepointer-s/87385/1 "2026-06-15T13:44:15Z")

</div>

Mostly to redirect some eyes to this issue: [Don't impose a performance penalty when using `MutableSpan` · Issue #89954 · swiftlang/swift · GitHub](https://github.com/swiftlang/swift/issues/89954)

What happened is that I created [this PR](https://github.com/swift-dns/swift-endpoint/pull/20), and there I noticed usage of `MutableSpan` makes the `IPAddressBenchs:IPv4_String_Encoding_Localhost_15M` (encode localhost 15 million times) benchmark go from ~200ms to ~350ms which is a **+75%**.

From what I've noticed, `MutableSpan` is in all cases essentially unusable if you really want to go for maximum performance and compete with C. There are also no escape hatches from what I can tell.  
To be clear `Span` is perfectly fine. I'm talking only about `MutableSpan`.

Optimally we can make sure even Swift 6.4 doesn't come with this penalty when it's released so we don't have to wait another 6-9 months.  
I can work on the changes if the compiler folks agree with me.

---

<div class="post-metadata">

**Author:** ![MahdiBM](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mahdibm/32/45271_2.png) [@MahdiBM](https://forums.swift.org/u/MahdiBM)\
**Post date:** [June 15, 2026, 1:52pm UTC](https://forums.swift.org/t/mutablespan-comes-with-performance-penalties-compared-to-unsafepointer-s/87385/2 "2026-06-15T13:52:46Z")

</div>

Just so the issue is more clear, here's a link to the commit which successfully reverted the perf degradation:

> <https://github.com/swift-dns/swift-endpoint/pull/20/commits/72b70971de65eba5f1d3b8de58179576a461f9db#diff-30f817f4ee934a03f1865de6b8ce2d5a9c41e5be743a26b83293cbde3a13b93aL19>

---

<div class="post-metadata">

**Author:** ![John\_McCall](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/john_mccall/32/1351_2.png) [@John\_McCall](https://forums.swift.org/u/John_McCall)\
**Post date:** [June 15, 2026, 1:55pm UTC](https://forums.swift.org/t/mutablespan-comes-with-performance-penalties-compared-to-unsafepointer-s/87385/3 "2026-06-15T13:55:31Z")

</div>

Neither this post nor the issue say anything about what these expensive unnecessary checks actually are.

---

<div class="post-metadata">

**Author:** ![MahdiBM](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mahdibm/32/45271_2.png) [@MahdiBM](https://forums.swift.org/u/MahdiBM)\
**Post date:** [June 15, 2026, 2:06pm UTC](https://forums.swift.org/t/mutablespan-comes-with-performance-penalties-compared-to-unsafepointer-s/87385/4 "2026-06-15T14:06:13Z")

</div>

Ah right, sorry. For once I was trying to go for a quick issue, but then I managed to get myself into a forums thread.

What I bumped into is when you call `mutableSpan` on Unsafe Pointer types, is when you bump into those checks. But also from a quick look at stdlib, it appears pretty much every place that returns such a `MutableSpan` goes through those (not 100% sure).

Here's a link to what exactly my code was calling:

> <https://github.com/swiftlang/swift/blob/f5a73477a0a095e4623a373e380e522c5cec59ac/stdlib/public/core/UnsafeBufferPointer.swift.gyb#L801>

which calls:

> <https://github.com/swiftlang/swift/blob/f5a73477a0a095e4623a373e380e522c5cec59ac/stdlib/public/core/Span/MutableSpan.swift#L80>

I can already see the checks do not really look that expensive, but well, in practice they're adding 75% to the runtime of the benchmark.  
I'd presume `MutableSpan` is supposed to be the safe alternative to mutable buffer pointers, but then I'd expect it does also perform the same as well. Like `Span` does.

And again, someone else also bumped into the same issue some months ago when working on some crypto package, so I know I'm not the only one that is not happy with this penalty.

---

<div class="post-metadata">

**Author:** ![snej](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/snej/32/365_2.png) [@snej](https://forums.swift.org/u/snej)\
**Post date:** [June 15, 2026, 3:41pm UTC](https://forums.swift.org/t/mutablespan-comes-with-performance-penalties-compared-to-unsafepointer-s/87385/5 "2026-06-15T15:41:49Z")

</div>

The precondition seems important for correctness, though. Otherwise you can create misaligned spans that (on some CPUs) crash later on, in “safe” code, when dereferenced.

I would hope that the check gets optimized out for single-byte types like UInt8, which is by far the most common type of span I use.

---

<div class="post-metadata">

**Author:** ![RandomHashTags](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/randomhashtags/32/24259_2.png) [@RandomHashTags](https://forums.swift.org/u/RandomHashTags)\
**Post date:** [June 15, 2026, 5:09pm UTC](https://forums.swift.org/t/mutablespan-comes-with-performance-penalties-compared-to-unsafepointer-s/87385/6 "2026-06-15T17:09:07Z")

</div>

I expected more detailed explanations and concrete evidence with such a title and proclamation. Using `MutableSpan` instead of unsafe pointers in your project did not affect benchmark performance on my local Linux machine (7.0.11-arch1-1).

I use `MutableSpan` in my high performance libraries extensively, which include a league scheduling engine and networking library, with no runtime backdraws to such an extent, if any, you observe.

---

<div class="post-metadata">

**Author:** ![MahdiBM](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mahdibm/32/45271_2.png) [@MahdiBM](https://forums.swift.org/u/MahdiBM)\
**Post date:** [June 15, 2026, 5:26pm UTC](https://forums.swift.org/t/mutablespan-comes-with-performance-penalties-compared-to-unsafepointer-s/87385/7 "2026-06-15T17:26:58Z")

</div>

> [@RandomHashTags](#):
>
> I expected more detailed explanations and concrete evidence with such a title and proclamation.

I can provide actual benchmark cases if asked to, but I thought (think?) the bottleneck is observable to a compiler engineer already, so hopefully I can save me some time.  
Again, if there is a need, I can provide that. I've already mentioned the benchmark name, and the commit with which the degradation is reversed. So the info is already here, although I understand I should not expect people to go after them themselves and optimally just clearly provide them.

> [@RandomHashTags](#):
>
> with no runtime backdraws to such an extent

The backdraw isn't actually too much, the library is over-magnifying it.

It's only noticeable when you're pixel-peeping into the performance of your library, and that there are no other visible performance bottlenecks already. Specially none of the more important types, such as algorithmic bottlenecks, or not having chosen the correct data structures etc...

If you think about it, the whole performance degradation is a couple light (e.g. no division) integer arithmetic (although there might be more to the performance issue that `MutableSpan` causes, that I haven't noticed).  
The benchmark which it degraded, is also only a bunch of light arithmetic for the most part, to parse IPv4 from the bytes it's been handed.

2 reasons for the fact that I'm noticing this at all are:  
1- The library has benchmarks to compare against native C libraries like Darwin/glibc, and it performs even better than them in most cases. So you know the implementation is very (too?) optimized.  
2- There were already benchmarks with better performance, so it was easy to notice there is a degradation.

So essentially If you're not aiming for very high performance or comparing to a previous baseline, you likely won't notice it.

---

<div class="post-metadata">

**Author:** ![MahdiBM](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mahdibm/32/45271_2.png) [@MahdiBM](https://forums.swift.org/u/MahdiBM)\
**Post date:** [June 15, 2026, 5:33pm UTC](https://forums.swift.org/t/mutablespan-comes-with-performance-penalties-compared-to-unsafepointer-s/87385/8 "2026-06-15T17:33:07Z")

</div>

> [@snej](#):
>
> The precondition seems important for correctness

Right. What I'm hoping is that perhaps due to semantics of specific types or where they're being used, the compiler engineers can already infer that the checks are redundant and they can skip those via an unchecked initializer that simply just puts the type together.

Also in any case, I expected stdlib to provide escape hatches for such a type, even if they are annotated with a hundred "unchecked" "unsafe"s. Sometimes, e.g. when you've just allocated memory for a number of UInt8s, you already know you don't need such checks.

> [@snej](#):
>
> I would hope that the check gets optimized out for single-byte types like UInt8, which is by far the most common type of span I use.

I'd expect that too. Makes me think if there is possibly any other thing going on in here that we haven't noticed.

---

<div class="post-metadata">

**Author:** ![RandomHashTags](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/randomhashtags/32/24259_2.png) [@RandomHashTags](https://forums.swift.org/u/RandomHashTags)\
**Post date:** [June 15, 2026, 8:44pm UTC](https://forums.swift.org/t/mutablespan-comes-with-performance-penalties-compared-to-unsafepointer-s/87385/9 "2026-06-15T20:44:37Z")

</div>

So the exact performance overhead you're observing is mutable span initializers that use `_precondition`, which will impact performance. So you are right, but the title does misrepresent the impact of using `MutableSpan`.

> **side note regarding "very high performance"**
>
> > [@MahdiBM](#):
> >
> > It's only noticeable when you're pixel-peeping into the performance of your library, and that there are no other visible performance bottlenecks already. Specially none of the more important types, such as algorithmic bottlenecks, or not having chosen the correct data structures etc...
> 
> Furthermore, this quote may be generalizing to help the average reader, which is understandable, however, you're assuming I don't know how to properly benchmark or write objectively optimal code.
> 
> Looking at your library again, I've optimized your `writeTextualRepresentation` to perform objectively better (since I won't try rewriting the whole project 😉 ), if you actually want _very high performance_:
> 
> ```swift
> @inlinable
> @inline(__always)
> func writeTextualRepresentation2(into buffer: UnsafeMutableBufferPointer<UInt8>) -> Int {
> var resultIdx = 0
> var addressByte = address.byteSwapped
> UInt8(addressByte & 0xFF).asDecimal(
> writeUTF8Byte: {
> buffer[resultIdx] = $0
> resultIdx &+= 1
> }
> )
> for _ in 1..<4 {
> buffer[resultIdx] = .asciiDot
> resultIdx &+= 1
> 
> addressByte >>= 8
> UInt8(addressByte & 0xFF).asDecimal(
> writeUTF8Byte: {
> buffer[resultIdx] = $0
> resultIdx &+= 1
> }
> )
> }
> return resultIdx
> }
> 
> ```

---

<div class="post-metadata">

**Author:** ![MahdiBM](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mahdibm/32/45271_2.png) [@MahdiBM](https://forums.swift.org/u/MahdiBM)\
**Post date:** [June 16, 2026, 7:10am UTC](https://forums.swift.org/t/mutablespan-comes-with-performance-penalties-compared-to-unsafepointer-s/87385/10 "2026-06-16T07:10:54Z")

</div>

> **Response to "side note regarding "very high performance""**
>
> I appreciate the time, but I don't think the code matters too much here. As a matter of fact, GodBolt shows that the 2 different codes are compiled to almost the same code anyway:
> 
> > **[Compiler Explorer](https://godbolt.org/z/M535ooba6)**
> >
> > Compiler Explorer is an interactive online compiler which shows the assembly output of compiled C++, Rust, Go (and many more) code.
> 
> The code is too simple for the compiler!
> 
> Also IPv4 parsing is around the simpler things that the package does.  
> Performant IPv6 parsing is exponentially more complex due to the compression sign (`::`), and even more complicated than that we have the IDNA-compatibility functions where simple integer arithmetic and stack allocations are no longer enough, and off of memory, I remember `UniqueArray`/`RigidArray`/custom-"TinyArray"/"SmallArray"-implementations/lifetime-annotations had to be used (short of using raw UnsafePointers) to reach the desired performance which was to be able to compete against ICU.

---

<div class="post-metadata">

**Author:** ![MahdiBM](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mahdibm/32/45271_2.png) [@MahdiBM](https://forums.swift.org/u/MahdiBM)\
**Post date:** [June 16, 2026, 7:17am UTC](https://forums.swift.org/t/mutablespan-comes-with-performance-penalties-compared-to-unsafepointer-s/87385/11 "2026-06-16T07:17:16Z")

</div>

> [@RandomHashTags](#):
>
> but the title does misrepresent the impact of using `MutableSpan`.

Hmm I can't decide if it does or it doesn't which means It should be more clear anyway. I'll try to adjust the title. To be fair I am using "relatively" in the title.

In my mind it was/is justified as I had already seen other people struggle with it, and also my expectations of Swift are high, which means off-hand I wouldn't expect `MutableSpan` to come with the aforementioned penalty.

I can see most codes are much more tolerant to such checks compared to `swift-endpoint` or a crypto package, but then I also expect `MutableSpan` to be _the type_ for performance-sensitive code and have no downsides compared to just using UnsafePointers.

**Edit** : title is now "`MutableSpan` comes with performance penalties compared to `UnsafePointer`s".  
Previously (IIRC): "`MutableSpan` comes with relatively big performance penalties"

---

<div class="post-metadata">

**Author:** ![filip-sakel](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/filip-sakel/32/8697_2.png) [@filip-sakel](https://forums.swift.org/u/filip-sakel)\
**Post date:** [June 16, 2026, 10:21am UTC](https://forums.swift.org/t/mutablespan-comes-with-performance-penalties-compared-to-unsafepointer-s/87385/12 "2026-06-16T10:21:50Z")

</div>

> [@MahdiBM](#):
>
> Right. What I'm hoping is that perhaps due to semantics of specific types or where they're being used, the compiler engineers can already infer that the checks are redundant and they can skip those via an unchecked initializer that simply just puts the type together.

I agree that’s a good direction for (Mutable)Span. Specifically, is there a particular code snippet where you’d expect the compiler to use a type’s “semantics” to eliminate a precondition check?

---

<div class="post-metadata">

**Author:** ![MahdiBM](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mahdibm/32/45271_2.png) [@MahdiBM](https://forums.swift.org/u/MahdiBM)\
**Post date:** [June 16, 2026, 1:56pm UTC](https://forums.swift.org/t/mutablespan-comes-with-performance-penalties-compared-to-unsafepointer-s/87385/13 "2026-06-16T13:56:25Z")

</div>

What I meant by "semantics", is that in a type like `Array`, I think `Array` already guarantees the buffer is aligned, so for `Array` the precondition is not necessary and stdlib should be able to just use an "unchecked" initializer of `MutableSpan`.

More generally, it appears all places in stdlib where there is a `MutableSpan` initialization (outside `MutableSpan`'s own implementation), such as in most implementations of `public var mutableSpan`, the stdlib code goes through those preconditions that could be skipped (some might be optimized out already, not sure).

> **Search for \`public var mutableSpan\`**
>
> ```bash
> rg -lF 'public var mutableSpan' ./stdlib | xargs basename | nl
> 1 UnsafeBufferPointer.swift.gyb
> 2 ContiguousArray.swift
> 3 ArraySlice.swift
> 4 Array.swift
> 5 UniqueBox.swift
> 6 InlineArray.swift
> 7 OutputSpan.swift
> 8 CollectionOfOne.swift
> 9 UniqueArray.swift
> 
> ```
> 
> All aside from `UnsafeBufferPointer` should be able to skip the alignment checks I'd assume.

For my specific case I expect the compiler to be able to optimize-out that precondition for 1-byte types like `UInt8`/`Int8`/`Bool`s as there can't be any misalignment issues for them.

`UInt8` itself is the most important one. `Int8` might also help when working with `CChar`s, or maybe someone just prefers to use `Int8` instead of `UInt8`, but I personally haven't bumped into that.  
I could propose a few ways to _possibly/maybe_ achieve this, for example using the `@_specialize` attribute, but then I know you'd already know better than me which way to go and what reasonable ways there are to skip that precondition.

Outside my specific case, I expect the compiler to be smart enough to totally optimize-out that specific precondition anyway. But I'm not a compiler engineer so I'm not sure what is blocking such an optimization and what could be done for that (hopefully without making it take another year to propagate changes through LLVM or such. Again, not a compiler engineer, these are just my vague understandings).

---

<div class="post-metadata">

**Author:** ![RandomHashTags](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/randomhashtags/32/24259_2.png) [@RandomHashTags](https://forums.swift.org/u/RandomHashTags)\
**Post date:** [June 16, 2026, 9:34pm UTC](https://forums.swift.org/t/mutablespan-comes-with-performance-penalties-compared-to-unsafepointer-s/87385/14 "2026-06-16T21:34:48Z")

</div>

I appreciate the title change, however:

> **"very high performance" rebuttal**
>
> > [@MahdiBM](#):
> >
> > I appreciate the time, but I don't think the code matters too much here. As a matter of fact, GodBolt shows that the 2 different codes are compiled to almost the same code anyway:
> 
> Regardless on how the assembly looks, the real-world performance is the only thing that objectively matters. My improvement uses 180 less instructions (~5%) to do the same work (at least on my machine), which improves throughput by 2 samples using the project's throughput benchmark (30 million more operations in the same time-frame on my machine). The same methodology can apply to other parts of the project, but as I previously stated, I won't rewrite it 😉 .
> 
> When I say _very high performance_, I am talking about reducing total instructions executed and improving runtime performance for everything.
> 
> Unfortunately, this is another case of "skill issue". 👋

---

<div class="post-metadata">

**Author:** ![MahdiBM](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mahdibm/32/45271_2.png) [@MahdiBM](https://forums.swift.org/u/MahdiBM)\
**Post date:** [June 17, 2026, 3:51am UTC](https://forums.swift.org/t/mutablespan-comes-with-performance-penalties-compared-to-unsafepointer-s/87385/15 "2026-06-17T03:51:17Z")

</div>

> **Response to ""very high performance" rebuttal"**
>
> > My improvement uses 180 less instructions (~5%)
> 
> _180_? I haven't checked but that sounds suspiciously too high considering godbolt shows essentially the same number of assembly lines.
> 
> > at least on my machine
> 
> Well, that's the keyword. You need to prove the significance of your machine. Does it use a CPU with similar properties compared to a typical server? Or ...? I know swift-endpoint's CI benchmarking machine does.
> 
> > which improves throughput by 2 samples using the project's throughput benchmark (30 million more operations in the same time-frame on my machine).
> 
> I had already tried your changes, yesterday. It didn't make a visible difference in CI: [Try Swift Forums suggestion by MahdiBM · Pull Request #21 · swift-dns/swift-endpoint · GitHub](https://github.com/swift-dns/swift-endpoint/pull/21)
> 
> Some months ago, I tried fully inlining that loop altogether. It didn't make a visible difference either, so that's why the loop is still there. Not because I didn't think it's faster, but because I didn't want such a weird-looking code for such low perf gains (to be clear, weird-looking is not true for your code). So the thing is, it _needs_ to be visible in the benchmarks. Otherwise the change isn't sustainable and the minor improvements can be accidentally reverted in the future.
> 
> I'm assuming the reason for "less instructions" in your code is likely because of the `byteSwapped`, which means you don't need to reverse and index each time. If the inlined-loop was there, the `byteSwapped` would have only been some additional instructions and wouldn't have helped.
> 
> Also so we're clear, swift-endpoint has a custom `UInt128` implementation (backed by 2xUInt64 to be able to dodge Swift's UInt128 macOS 15 requirements for IPv6's storage), so you can't be thinking that I didn't know of existence of `byteSwapped` or such.
> 
> I give it to you that your idea was good. But I'm still not convinced that it's worth a change.  
> Also you already had a pretty decent code, if I may say so myself, to start with. You didn't have to work it out from the beginning, which _could have possibly_ resulted in you going a totally different/worse way. So even if you're proud of your code there, I'm inclined to take part of the credit :)
> 
> After all, there is also a compiler in between the code that we write. At any point it could have taken a decision to inline the whole loop. Or maybe it does in a future version. Then the current code will be looking better since it skips one `byteSwapped`. You're also using bitshifts compared to simply loading the values from the memory which the current code does. Now the compiler is likely smart enough to know what's going on in here, but I doubt the bitshifts have anything to do with a performance improvement, considering what the current code does is to directly load from memory.
> 
> > When I say _very high performance_, I am talking about reducing total instructions executed
> 
> less instructions executed does not mean better performance
> 
> The point here is that we can argue about this all-day long. I think there has been a misunderstanding about what actually I meant by "very high performance". In these kinds of situations, I always take at least part of the blame for not being clear enough. For the most part, it was referring to the fact that the implementations beat the counterpart C implementations.
> 
> Can they be improved? yes. But how far should I go? Do I go for inlining assembly in Swift files and call it "Swift"? Or how far do I go for looking into what the compiler likes to produce given some code? I have gone pretty deep into this, but still, there is a point that I need to stop.
> 
> Realistically too, I'm also under real-world restrictions. For example about how to benchmark the code. The CI machine already costs $18-20/month out of pocket. I can't simply just add all possible benchmark types to the CI, that'd take too long, and will also take more time to maintain. I had already considered adding instructions count to the CI since it's a _fine_ unit of measurement. I can't exactly recall why that didn't happen, but probably it was to save some CI time. I remember at some point I was struggling with benchmark times and that's partially why `swift-idna` was moved to its dedicated repository.

---

<div class="post-metadata">

**Author:** ![MahdiBM](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mahdibm/32/45271_2.png) [@MahdiBM](https://forums.swift.org/u/MahdiBM)\
**Post date:** [June 17, 2026, 5:25am UTC](https://forums.swift.org/t/mutablespan-comes-with-performance-penalties-compared-to-unsafepointer-s/87385/16 "2026-06-17T05:25:55Z")

</div>

I see @ Alejandro has already done the work in [[stdlib] Collection span getters can use the unchecked initializer by Azoy · Pull Request #90002 · swiftlang/swift · GitHub](https://github.com/swiftlang/swift/pull/90002). Very appreciated. I'm assuming it'll get cherry-picked into 6.4 as well.

I wonder what it takes to have public unchecked initializers too. Wouldn't want to be forced into not using `MutableSpan` in the future just because I can't express that I'm already sure the alignment is all-good. I think we'll need an "amendment" to the proposal (Including its implications, for example a new evolution forums thread)?  
This is not without precedence, for example `UTF8Span` has `init(unchecked: Span<UInt8>)`.

I wonder what folks here think about such a public unchecked initializer.

---

<div class="post-metadata">

**Author:** ![MahdiBM](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mahdibm/32/45271_2.png) [@MahdiBM](https://forums.swift.org/u/MahdiBM)\
**Post date:** [June 17, 2026, 9:15pm UTC](https://forums.swift.org/t/mutablespan-comes-with-performance-penalties-compared-to-unsafepointer-s/87385/17 "2026-06-17T21:15:19Z")

</div>

> **More response to ""very high performance" rebuttal"**
>
> I just did a check and while it makes sense now, I didn't know that a mem load is _slower_ than a bitshift.
> 
> Again, not that I hadn't thought of using bitshifts (I did not think of byteSwapped-ing though), the initial code likely was using bitshift before I move it to mem load (it's likely somewhere in the git as well), but there is the compiler in between which is probably why the 2 codes don't have much of a difference in performance anyway.
> 
> So I guess I should accept defeat in that sense, and accept the "skill issue" 😉

---

<div class="post-metadata">

**Author:** ![MahdiBM](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mahdibm/32/45271_2.png) [@MahdiBM](https://forums.swift.org/u/MahdiBM)\
**Post date:** [June 24, 2026, 1:46pm UTC](https://forums.swift.org/t/mutablespan-comes-with-performance-penalties-compared-to-unsafepointer-s/87385/18 "2026-06-24T13:46:31Z")

</div>

I think I've finally figured out what is going on in here and how the compiler doesn't manage to optimize out such simple math.

> <https://github.com/swiftlang/swift/pull/90161>
>
> Let's preface the PR with: I'm not a compiler engineer. This fix was made with h…elp from Claude (max effort). While I've tested the effectiveness of the changes locally, I'm not sure how sound the changes actually are (I would think they are at least OK. If not actually good). You can feel free to close the PR and apply the fix yourself in a better way after reading the result of my investigations below.
> 
> Resolves https://github.com/swiftlang/swift/issues/89954.
> 
> \## 
> 
> \### Proof of Issue
> 
> \`\`\`bash
> cat \> issue-89954.swift \<\<'EOF'
> @inline(never)
> func asDecimal(\_ x: UInt8, writeByte: (UInt8) -\> Void) {
> let (q, r) = x.quotientAndRemainder(dividingBy: 10)
> writeByte(q &+ 48); writeByte(r &+ 48)
> }
> 
> // A) Captures a MutableSpan (~Escapable): closure is wrapped in mark\_dependence.
> @inline(never)
> public func viaMutableSpan(\_ p: UnsafeMutableRawPointer, \_ n: Int) -\> Int {
> var span = unsafe MutableSpan(\_unsafeStart: p.assumingMemoryBound(to: UInt8.self), count: n)
> var i = 0
> asDecimal(123) { span\[i\] = $0; i &+= 1 }
> asDecimal(45) { span\[i\] = $0; i &+= 1 }
> return i
> }
> 
> // B) Same shape, Copyable pointer (no mark\_dependence): specializes today.
> @inline(never)
> public func viaBuffer(\_ p: UnsafeMutableRawPointer, \_ n: Int) -\> Int {
> let buf = unsafe UnsafeMutableBufferPointer(start: p.assumingMemoryBound(to: UInt8.self), count: n)
> var i = 0
> asDecimal(123) { buf\[i\] = $0; i &+= 1 }
> asDecimal(45) { buf\[i\] = $0; i &+= 1 }
> return i
> }
> EOF
> swiftc -O -emit-sil issue-89954.swift | swift demangle
> \`\`\`
> 
> \<details\>
> \<summary\>Click for the swift code but with syntax highlighting\</summary\>
> 
> \`\`\`swift
> @inline(never)
> func asDecimal(\_ x: UInt8, writeByte: (UInt8) -\> Void) {
> let (q, r) = x.quotientAndRemainder(dividingBy: 10)
> writeByte(q &+ 48); writeByte(r &+ 48)
> }
> 
> // A) Captures a MutableSpan (~Escapable): closure is wrapped in mark\_dependence.
> @inline(never)
> public func viaMutableSpan(\_ p: UnsafeMutableRawPointer, \_ n: Int) -\> Int {
> var span = unsafe MutableSpan(\_unsafeStart: p.assumingMemoryBound(to: UInt8.self), count: n)
> var i = 0
> asDecimal(123) { span\[i\] = $0; i &+= 1 }
> asDecimal(45) { span\[i\] = $0; i &+= 1 }
> return i
> }
> 
> // B) Same shape, Copyable pointer (no mark\_dependence): specializes today.
> @inline(never)
> public func viaBuffer(\_ p: UnsafeMutableRawPointer, \_ n: Int) -\> Int {
> let buf = unsafe UnsafeMutableBufferPointer(start: p.assumingMemoryBound(to: UInt8.self), count: n)
> var i = 0
> asDecimal(123) { buf\[i\] = $0; i &+= 1 }
> asDecimal(45) { buf\[i\] = $0; i &+= 1 }
> return i
> }
> \`\`\`
> 
> \</details\>
> 
> \<details\>
> \<summary\>Click for full result of the command on my machine\</summary\>
> 
> \`\`\`bash
> sil\_stage canonical
> 
> import Builtin
> import Swift
> import SwiftShims
> 
> @inline(never) func asDecimal(\_ x: UInt8, writeByte: (UInt8) -\> ())
> 
> @inline(never) public func viaMutableSpan(\_ p: UnsafeMutableRawPointer, \_ n: Int) -\> Int
> 
> @inline(never) public func viaBuffer(\_ p: UnsafeMutableRawPointer, \_ n: Int) -\> Int
> 
> // main
> // Isolation: unspecified
> sil @main : $@convention(c) (Int32, UnsafeMutablePointer\<Optional\<UnsafeMutablePointer\<Int8\>\>\>) -\> Int32 {
> \[%1: noescape \*\*\]
> \[global: \]
> bb0(%0 : $Int32, %1 : $UnsafeMutablePointer\<Optional\<UnsafeMutablePointer\<Int8\>\>\>):
> %2 = integer\_literal $Builtin.Int32, 0 // user: %3
> %3 = struct $Int32 (%2) // user: %4
> return %3 // id: %4
> } // end sil function 'main'
> 
> // asDecimal(\_:writeByte:)
> // Isolation: unspecified
> sil hidden \[noinline\] @main.asDecimal(\_: Swift.UInt8, writeByte: (Swift.UInt8) -\> ()) -\> () : $@convention(thin) (UInt8, @guaranteed @noescape @callee\_guaranteed (UInt8) -\> ()) -\> () {
> \[%1: noescape \*\*, read v\*\*.c\*.v\*\*, write v\*\*.c\*.v\*\*, copy v\*\*.c\*.v\*\*, destroy v\*\*.c\*.v\*\*\]
> \[global: read,write,copy,destroy,allocate,deinit\_barrier\]
> // %0 "x" // users: %5, %2
> // %1 "writeByte" // users: %19, %15, %3
> bb0(%0 : $UInt8, %1 : $@noescape @callee\_guaranteed (UInt8) -\> ()):
> debug\_value %0, let, name "x", argno 1 // id: %2
> debug\_value %1, let, name "writeByte", argno 2 // id: %3
> %4 = integer\_literal $Builtin.Int8, 10 // users: %7, %6
> %5 = struct\_extract %0, #UInt8.\_value // users: %7, %6
> %6 = builtin "udiv\_Int8"(%5, %4) : $Builtin.Int8 // users: %9, %12
> %7 = builtin "urem\_Int8"(%5, %4) : $Builtin.Int8 // users: %8, %16
> debug\_value %7, let, name "r", type $UInt8, expr op\_fragment:#UInt8.\_value // id: %8
> debug\_value %6, let, name "q", type $UInt8, expr op\_fragment:#UInt8.\_value // id: %9
> %10 = integer\_literal $Builtin.Int8, 48 // users: %16, %12
> %11 = integer\_literal $Builtin.Int1, 0 // users: %16, %12
> %12 = builtin "uadd\_with\_overflow\_Int8"(%6, %10, %11) : $(Builtin.Int8, Builtin.Int1) // user: %13
> %13 = tuple\_extract %12, 0 // user: %14
> %14 = struct $UInt8 (%13) // user: %15
> %15 = apply %1(%14) : $@noescape @callee\_guaranteed (UInt8) -\> ()
> %16 = builtin "uadd\_with\_overflow\_Int8"(%7, %10, %11) : $(Builtin.Int8, Builtin.Int1) // user: %17
> %17 = tuple\_extract %16, 0 // user: %18
> %18 = struct $UInt8 (%17) // user: %19
> %19 = apply %1(%18) : $@noescape @callee\_guaranteed (UInt8) -\> ()
> %20 = tuple () // user: %21
> return %20 // id: %21
> } // end sil function 'main.asDecimal(\_: Swift.UInt8, writeByte: (Swift.UInt8) -\> ()) -\> ()'
> 
> // viaMutableSpan(\_:\_:)
> // Isolation: unspecified
> sil \[noinline\] @main.viaMutableSpan(Swift.UnsafeMutableRawPointer, Swift.Int) -\> Swift.Int : $@convention(thin) (UnsafeMutableRawPointer, Int) -\> Int {
> \[%0: noescape \*\*, read v\*\*.c\*.v\*\*, write v\*\*.c\*.v\*\*, copy v\*\*.c\*.v\*\*, destroy v\*\*.c\*.v\*\*\]
> \[global: read,write,copy,destroy,allocate,deinit\_barrier\]
> // %0 "p" // users: %11, %2
> // %1 "n" // users: %6, %3
> bb0(%0 : $UnsafeMutableRawPointer, %1 : $Int):
> debug\_value %0, let, name "p", argno 1 // id: %2
> debug\_value %1, let, name "n", argno 2 // id: %3
> %4 = alloc\_stack \[lexical\] \[var\_decl\] $MutableSpan\<UInt8\>, var, name "span", type $MutableSpan\<UInt8\> // users: %13, %29, %28, %21, %20, %34
> %5 = integer\_literal $Builtin.Int64, 0 // users: %15, %7
> %6 = struct\_extract %1, #Int.\_value // users: %9, %7
> %7 = builtin "cmp\_slt\_Int64"(%6, %5) : $Builtin.Int1 // user: %8
> cond\_fail %7, "Count must not be negative" // id: %8
> %9 = builtin "assumeNonNegative\_Int64"(%6) : $Builtin.Int64 // user: %10
> %10 = struct $Int (%9) // user: %12
> %11 = enum $Optional\<UnsafeMutableRawPointer\>, #Optional.some!enumelt, %0 // user: %12
> %12 = struct $MutableSpan\<UInt8\> (%11, %10) // user: %13
> store %12 to %4 // id: %13
> %14 = alloc\_stack \[var\_decl\] $Int, var, name "i", type $Int // users: %32, %16, %28, %20, %33
> %15 = struct $Int (%5) // user: %16
> store %15 to %14 // id: %16
> %17 = integer\_literal $Builtin.Int8, 123 // user: %18
> %18 = struct $UInt8 (%17) // user: %23
> // function\_ref closure #1 in viaMutableSpan(\_:\_:)
> %19 = function\_ref @closure #1 (Swift.UInt8) -\> () in main.viaMutableSpan(Swift.UnsafeMutableRawPointer, Swift.Int) -\> Swift.Int : $@convention(thin) (UInt8, @inout\_aliasable MutableSpan\<UInt8\>, @inout\_aliasable Int) -\> () // user: %20
> %20 = partial\_apply \[callee\_guaranteed\] \[on\_stack\] %19(%4, %14) : $@convention(thin) (UInt8, @inout\_aliasable MutableSpan\<UInt8\>, @inout\_aliasable Int) -\> () // users: %24, %21
> %21 = mark\_dependence \[nonescaping\] %20 on %4 // user: %23
> // function\_ref asDecimal(\_:writeByte:)
> %22 = function\_ref @main.asDecimal(\_: Swift.UInt8, writeByte: (Swift.UInt8) -\> ()) -\> () : $@convention(thin) (UInt8, @guaranteed @noescape @callee\_guaranteed (UInt8) -\> ()) -\> () // users: %30, %23
> %23 = apply %22(%18, %21) : $@convention(thin) (UInt8, @guaranteed @noescape @callee\_guaranteed (UInt8) -\> ()) -\> ()
> dealloc\_stack %20 // id: %24
> %25 = integer\_literal $Builtin.Int8, 45 // user: %26
> %26 = struct $UInt8 (%25) // user: %30
> // function\_ref closure #2 in viaMutableSpan(\_:\_:)
> %27 = function\_ref @closure #2 (Swift.UInt8) -\> () in main.viaMutableSpan(Swift.UnsafeMutableRawPointer, Swift.Int) -\> Swift.Int : $@convention(thin) (UInt8, @inout\_aliasable MutableSpan\<UInt8\>, @inout\_aliasable Int) -\> () // user: %28
> %28 = partial\_apply \[callee\_guaranteed\] \[on\_stack\] %27(%4, %14) : $@convention(thin) (UInt8, @inout\_aliasable MutableSpan\<UInt8\>, @inout\_aliasable Int) -\> () // users: %31, %29
> %29 = mark\_dependence \[nonescaping\] %28 on %4 // user: %30
> %30 = apply %22(%26, %29) : $@convention(thin) (UInt8, @guaranteed @noescape @callee\_guaranteed (UInt8) -\> ()) -\> ()
> dealloc\_stack %28 // id: %31
> %32 = load %14 // user: %35
> dealloc\_stack %14 // id: %33
> dealloc\_stack %4 // id: %34
> return %32 // id: %35
> } // end sil function 'main.viaMutableSpan(Swift.UnsafeMutableRawPointer, Swift.Int) -\> Swift.Int'
> 
> // closure #1 in viaMutableSpan(\_:\_:)
> // Isolation: nonisolated
> sil private @closure #1 (Swift.UInt8) -\> () in main.viaMutableSpan(Swift.UnsafeMutableRawPointer, Swift.Int) -\> Swift.Int : $@convention(thin) (UInt8, @inout\_aliasable MutableSpan\<UInt8\>, @inout\_aliasable Int) -\> () {
> \[%1: noescape \*\*, read v\*\*\]
> \[%2: read s0.v\*\*, write v\*\*\]
> \[global: read,write,deinit\_barrier\]
> // %0 "$0" // users: %25, %3
> // %1 "span" // users: %8, %17, %24, %4
> // %2 "i" // users: %32, %6, %5
> bb0(%0 : $UInt8, %1 : @closureCapture $\*MutableSpan\<UInt8\>, %2 : @closureCapture $\*Int):
> debug\_value %0, let, name "$0", argno 1 // id: %3
> debug\_value %1, var, name "span", argno 2, expr op\_deref // id: %4
> debug\_value %2, var, name "i", argno 3, expr op\_deref // id: %5
> %6 = struct\_element\_addr %2, #Int.\_value // users: %27, %7
> %7 = load %6 // users: %14, %13, %22
> %8 = struct\_element\_addr %1, #MutableSpan.\_count // user: %9
> %9 = struct\_element\_addr %8, #Int.\_value // user: %10
> %10 = load %9 // user: %12
> %11 = integer\_literal $Builtin.Int64, 0 // user: %13
> %12 = builtin "assumeNonNegative\_Int64"(%10) : $Builtin.Int64 // user: %14
> %13 = builtin "cmp\_slt\_Int64"(%7, %11) : $Builtin.Int1 // user: %15
> %14 = builtin "cmp\_sge\_Int64"(%7, %12) : $Builtin.Int1 // user: %15
> %15 = builtin "or\_Int1"(%13, %14) : $Builtin.Int1 // user: %16
> cond\_fail %15, "index out of bounds" // id: %16
> %17 = struct\_element\_addr %1, #MutableSpan.\_pointer // user: %18
> %18 = load %17 // user: %19
> %19 = unchecked\_enum\_data %18, #Optional.some!enumelt // user: %20
> %20 = struct\_extract %19, #UnsafeMutableRawPointer.\_rawValue // user: %21
> %21 = pointer\_to\_address %20 to \[strict\] $\*UInt8 // user: %23
> %22 = builtin "truncOrBitCast\_Int64\_Word"(%7) : $Builtin.Word // user: %23
> %23 = index\_addr %21, %22 // user: %24
> %24 = mark\_dependence \[nonescaping\] %23 on %1 // user: %25
> store %0 to %24 // id: %25
> %26 = integer\_literal $Builtin.Int64, 1 // user: %29
> %27 = load %6 // user: %29
> %28 = integer\_literal $Builtin.Int1, 0 // user: %29
> %29 = builtin "sadd\_with\_overflow\_Int64"(%27, %26, %28) : $(Builtin.Int64, Builtin.Int1) // user: %30
> %30 = tuple\_extract %29, 0 // user: %31
> %31 = struct $Int (%30) // user: %32
> store %31 to %2 // id: %32
> %33 = tuple () // user: %34
> return %33 // id: %34
> } // end sil function 'closure #1 (Swift.UInt8) -\> () in main.viaMutableSpan(Swift.UnsafeMutableRawPointer, Swift.Int) -\> Swift.Int'
> 
> // closure #2 in viaMutableSpan(\_:\_:)
> // Isolation: nonisolated
> sil private @closure #2 (Swift.UInt8) -\> () in main.viaMutableSpan(Swift.UnsafeMutableRawPointer, Swift.Int) -\> Swift.Int : $@convention(thin) (UInt8, @inout\_aliasable MutableSpan\<UInt8\>, @inout\_aliasable Int) -\> () {
> \[%1: noescape \*\*, read v\*\*\]
> \[%2: read s0.v\*\*, write v\*\*\]
> \[global: read,write,deinit\_barrier\]
> // %0 "$0" // users: %25, %3
> // %1 "span" // users: %8, %17, %24, %4
> // %2 "i" // users: %32, %6, %5
> bb0(%0 : $UInt8, %1 : @closureCapture $\*MutableSpan\<UInt8\>, %2 : @closureCapture $\*Int):
> debug\_value %0, let, name "$0", argno 1 // id: %3
> debug\_value %1, var, name "span", argno 2, expr op\_deref // id: %4
> debug\_value %2, var, name "i", argno 3, expr op\_deref // id: %5
> %6 = struct\_element\_addr %2, #Int.\_value // users: %27, %7
> %7 = load %6 // users: %14, %13, %22
> %8 = struct\_element\_addr %1, #MutableSpan.\_count // user: %9
> %9 = struct\_element\_addr %8, #Int.\_value // user: %10
> %10 = load %9 // user: %12
> %11 = integer\_literal $Builtin.Int64, 0 // user: %13
> %12 = builtin "assumeNonNegative\_Int64"(%10) : $Builtin.Int64 // user: %14
> %13 = builtin "cmp\_slt\_Int64"(%7, %11) : $Builtin.Int1 // user: %15
> %14 = builtin "cmp\_sge\_Int64"(%7, %12) : $Builtin.Int1 // user: %15
> %15 = builtin "or\_Int1"(%13, %14) : $Builtin.Int1 // user: %16
> cond\_fail %15, "index out of bounds" // id: %16
> %17 = struct\_element\_addr %1, #MutableSpan.\_pointer // user: %18
> %18 = load %17 // user: %19
> %19 = unchecked\_enum\_data %18, #Optional.some!enumelt // user: %20
> %20 = struct\_extract %19, #UnsafeMutableRawPointer.\_rawValue // user: %21
> %21 = pointer\_to\_address %20 to \[strict\] $\*UInt8 // user: %23
> %22 = builtin "truncOrBitCast\_Int64\_Word"(%7) : $Builtin.Word // user: %23
> %23 = index\_addr %21, %22 // user: %24
> %24 = mark\_dependence \[nonescaping\] %23 on %1 // user: %25
> store %0 to %24 // id: %25
> %26 = integer\_literal $Builtin.Int64, 1 // user: %29
> %27 = load %6 // user: %29
> %28 = integer\_literal $Builtin.Int1, 0 // user: %29
> %29 = builtin "sadd\_with\_overflow\_Int64"(%27, %26, %28) : $(Builtin.Int64, Builtin.Int1) // user: %30
> %30 = tuple\_extract %29, 0 // user: %31
> %31 = struct $Int (%30) // user: %32
> store %31 to %2 // id: %32
> %33 = tuple () // user: %34
> return %33 // id: %34
> } // end sil function 'closure #2 (Swift.UInt8) -\> () in main.viaMutableSpan(Swift.UnsafeMutableRawPointer, Swift.Int) -\> Swift.Int'
> 
> // viaBuffer(\_:\_:)
> // Isolation: unspecified
> sil \[noinline\] @main.viaBuffer(Swift.UnsafeMutableRawPointer, Swift.Int) -\> Swift.Int : $@convention(thin) (UnsafeMutableRawPointer, Int) -\> Int {
> \[%0: noescape \*\*, write s0.v\*\*.c\*.v\*\*\]
> \[global: write,deinit\_barrier\]
> // %0 "p" // users: %4, %2
> // %1 "n" // user: %3
> bb0(%0 : $UnsafeMutableRawPointer, %1 : $Int):
> debug\_value %0, let, name "p", argno 1 // id: %2
> debug\_value %1, let, name "n", argno 2 // id: %3
> %4 = struct\_extract %0, #UnsafeMutableRawPointer.\_rawValue // user: %5
> %5 = struct $UnsafeMutablePointer\<UInt8\> (%4) // user: %6
> %6 = enum $Optional\<UnsafeMutablePointer\<UInt8\>\>, #Optional.some!enumelt, %5 // users: %7, %19, %15
> debug\_value %6, let, name "buf", type $UnsafeMutableBufferPointer\<UInt8\>, expr op\_fragment:#UnsafeMutableBufferPointer.\_position // id: %7
> %8 = alloc\_stack \[var\_decl\] $Int, var, name "i", type $Int // users: %15, %19, %20, %11, %21
> %9 = integer\_literal $Builtin.Int64, 0 // user: %10
> %10 = struct $Int (%9) // user: %11
> store %10 to %8 // id: %11
> %12 = integer\_literal $Builtin.Int8, 123 // user: %13
> %13 = struct $UInt8 (%12) // user: %15
> // function\_ref specialized asDecimal(\_:writeByte:)
> %14 = function\_ref @function signature specialization \<Arg\[1\] = Exploded\> of function signature specialization \<Arg\[1\] = \[Closure Propagated : $s4main9viaBufferySiSv\_SitFys5UInt8VXEfU\_, Argument Types : \[Swift.UnsafeMutableBufferPointer\<Swift.UInt8\>Swift.Int\]\> of main.asDecimal(\_: Swift.UInt8, writeByte: (Swift.UInt8) -\> ()) -\> () : $@convention(thin) (UInt8, Optional\<UnsafeMutablePointer\<UInt8\>\>, @inout\_aliasable Int) -\> () // user: %15
> %15 = apply %14(%13, %6, %8) : $@convention(thin) (UInt8, Optional\<UnsafeMutablePointer\<UInt8\>\>, @inout\_aliasable Int) -\> ()
> %16 = integer\_literal $Builtin.Int8, 45 // user: %17
> %17 = struct $UInt8 (%16) // user: %19
> // function\_ref specialized asDecimal(\_:writeByte:)
> %18 = function\_ref @function signature specialization \<Arg\[1\] = Exploded\> of function signature specialization \<Arg\[1\] = \[Closure Propagated : $s4main9viaBufferySiSv\_SitFys5UInt8VXEfU0\_, Argument Types : \[Swift.UnsafeMutableBufferPointer\<Swift.UInt8\>Swift.Int\]\> of main.asDecimal(\_: Swift.UInt8, writeByte: (Swift.UInt8) -\> ()) -\> () : $@convention(thin) (UInt8, Optional\<UnsafeMutablePointer\<UInt8\>\>, @inout\_aliasable Int) -\> () // user: %19
> %19 = apply %18(%17, %6, %8) : $@convention(thin) (UInt8, Optional\<UnsafeMutablePointer\<UInt8\>\>, @inout\_aliasable Int) -\> ()
> %20 = load %8 // user: %22
> dealloc\_stack %8 // id: %21
> return %20 // id: %22
> } // end sil function 'main.viaBuffer(Swift.UnsafeMutableRawPointer, Swift.Int) -\> Swift.Int'
> 
> // specialized asDecimal(\_:writeByte:)
> // Isolation: unspecified
> sil shared \[noinline\] @function signature specialization \<Arg\[1\] = Exploded\> of function signature specialization \<Arg\[1\] = \[Closure Propagated : $s4main9viaBufferySiSv\_SitFys5UInt8VXEfU\_, Argument Types : \[Swift.UnsafeMutableBufferPointer\<Swift.UInt8\>Swift.Int\]\> of main.asDecimal(\_: Swift.UInt8, writeByte: (Swift.UInt8) -\> ()) -\> () : $@convention(thin) (UInt8, Optional\<UnsafeMutablePointer\<UInt8\>\>, @inout\_aliasable Int) -\> () {
> \[%1: noescape \*\*, write v\*\*.c\*.v\*\*\]
> \[%2: read s0.v\*\*, write v\*\*\]
> \[global: write,deinit\_barrier\]
> // %0 // users: %5, %3
> // %1 // user: %19
> // %2 // users: %30, %17, %16, %43, %35
> bb0(%0 : $UInt8, %1 : $Optional\<UnsafeMutablePointer\<UInt8\>\>, %2 : $\*Int):
> debug\_value %0, let, name "x", argno 1 // id: %3
> %4 = integer\_literal $Builtin.Int8, 10 // users: %7, %6
> %5 = struct\_extract %0, #UInt8.\_value // users: %7, %6
> %6 = builtin "udiv\_Int8"(%5, %4) : $Builtin.Int8 // users: %12, %9
> %7 = builtin "urem\_Int8"(%5, %4) : $Builtin.Int8 // users: %31, %8
> debug\_value %7, let, name "r", type $UInt8, expr op\_fragment:#UInt8.\_value // id: %8
> debug\_value %6, let, name "q", type $UInt8, expr op\_fragment:#UInt8.\_value // id: %9
> %10 = integer\_literal $Builtin.Int8, 48 // users: %31, %12
> %11 = integer\_literal $Builtin.Int1, 0 // users: %40, %27, %31, %12
> %12 = builtin "uadd\_with\_overflow\_Int8"(%6, %10, %11) : $(Builtin.Int8, Builtin.Int1) // user: %13
> %13 = tuple\_extract %12, 0 // user: %14
> %14 = struct $UInt8 (%13) // users: %24, %15
> debug\_value %14, let, name "$0", argno 1 // id: %15
> debug\_value %2, var, name "i", argno 3, expr op\_deref // id: %16
> %17 = struct\_element\_addr %2, #Int.\_value // users: %39, %26, %18
> %18 = load %17 // user: %21
> %19 = unchecked\_enum\_data %1, #Optional.some!enumelt // user: %20
> %20 = struct\_extract %19, #UnsafeMutablePointer.\_rawValue // user: %22
> %21 = builtin "truncOrBitCast\_Int64\_Word"(%18) : $Builtin.Word // user: %23
> %22 = pointer\_to\_address %20 to \[strict\] $\*UInt8 // users: %37, %23
> %23 = index\_addr \[stack\_protection\] %22, %21 // user: %24
> store %14 to %23 // id: %24
> %25 = integer\_literal $Builtin.Int64, 1 // users: %40, %27
> %26 = load %17 // user: %27
> %27 = builtin "sadd\_with\_overflow\_Int64"(%26, %25, %11) : $(Builtin.Int64, Builtin.Int1) // user: %28
> %28 = tuple\_extract %27, 0 // users: %36, %29
> %29 = struct $Int (%28) // user: %30
> store %29 to %2 // id: %30
> %31 = builtin "uadd\_with\_overflow\_Int8"(%7, %10, %11) : $(Builtin.Int8, Builtin.Int1) // user: %32
> %32 = tuple\_extract %31, 0 // user: %33
> %33 = struct $UInt8 (%32) // users: %38, %34
> debug\_value %33, let, name "$0", argno 1 // id: %34
> debug\_value %2, var, name "i", argno 3, expr op\_deref // id: %35
> %36 = builtin "truncOrBitCast\_Int64\_Word"(%28) : $Builtin.Word // user: %37
> %37 = index\_addr \[stack\_protection\] %22, %36 // user: %38
> store %33 to %37 // id: %38
> %39 = load %17 // user: %40
> %40 = builtin "sadd\_with\_overflow\_Int64"(%39, %25, %11) : $(Builtin.Int64, Builtin.Int1) // user: %41
> %41 = tuple\_extract %40, 0 // user: %42
> %42 = struct $Int (%41) // user: %43
> store %42 to %2 // id: %43
> %44 = tuple () // user: %45
> return %44 // id: %45
> } // end sil function 'function signature specialization \<Arg\[1\] = Exploded\> of function signature specialization \<Arg\[1\] = \[Closure Propagated : $s4main9viaBufferySiSv\_SitFys5UInt8VXEfU\_, Argument Types : \[Swift.UnsafeMutableBufferPointer\<Swift.UInt8\>Swift.Int\]\> of main.asDecimal(\_: Swift.UInt8, writeByte: (Swift.UInt8) -\> ()) -\> ()'
> 
> // specialized asDecimal(\_:writeByte:)
> // Isolation: unspecified
> sil shared \[noinline\] @function signature specialization \<Arg\[1\] = Exploded\> of function signature specialization \<Arg\[1\] = \[Closure Propagated : $s4main9viaBufferySiSv\_SitFys5UInt8VXEfU0\_, Argument Types : \[Swift.UnsafeMutableBufferPointer\<Swift.UInt8\>Swift.Int\]\> of main.asDecimal(\_: Swift.UInt8, writeByte: (Swift.UInt8) -\> ()) -\> () : $@convention(thin) (UInt8, Optional\<UnsafeMutablePointer\<UInt8\>\>, @inout\_aliasable Int) -\> () {
> \[%1: noescape \*\*, write v\*\*.c\*.v\*\*\]
> \[%2: read s0.v\*\*, write v\*\*\]
> \[global: write,deinit\_barrier\]
> // %0 // users: %5, %3
> // %1 // user: %19
> // %2 // users: %30, %17, %16, %43, %35
> bb0(%0 : $UInt8, %1 : $Optional\<UnsafeMutablePointer\<UInt8\>\>, %2 : $\*Int):
> debug\_value %0, let, name "x", argno 1 // id: %3
> %4 = integer\_literal $Builtin.Int8, 10 // users: %7, %6
> %5 = struct\_extract %0, #UInt8.\_value // users: %7, %6
> %6 = builtin "udiv\_Int8"(%5, %4) : $Builtin.Int8 // users: %12, %9
> %7 = builtin "urem\_Int8"(%5, %4) : $Builtin.Int8 // users: %31, %8
> debug\_value %7, let, name "r", type $UInt8, expr op\_fragment:#UInt8.\_value // id: %8
> debug\_value %6, let, name "q", type $UInt8, expr op\_fragment:#UInt8.\_value // id: %9
> %10 = integer\_literal $Builtin.Int8, 48 // users: %31, %12
> %11 = integer\_literal $Builtin.Int1, 0 // users: %40, %27, %31, %12
> %12 = builtin "uadd\_with\_overflow\_Int8"(%6, %10, %11) : $(Builtin.Int8, Builtin.Int1) // user: %13
> %13 = tuple\_extract %12, 0 // user: %14
> %14 = struct $UInt8 (%13) // users: %24, %15
> debug\_value %14, let, name "$0", argno 1 // id: %15
> debug\_value %2, var, name "i", argno 3, expr op\_deref // id: %16
> %17 = struct\_element\_addr %2, #Int.\_value // users: %39, %26, %18
> %18 = load %17 // user: %21
> %19 = unchecked\_enum\_data %1, #Optional.some!enumelt // user: %20
> %20 = struct\_extract %19, #UnsafeMutablePointer.\_rawValue // user: %22
> %21 = builtin "truncOrBitCast\_Int64\_Word"(%18) : $Builtin.Word // user: %23
> %22 = pointer\_to\_address %20 to \[strict\] $\*UInt8 // users: %37, %23
> %23 = index\_addr \[stack\_protection\] %22, %21 // user: %24
> store %14 to %23 // id: %24
> %25 = integer\_literal $Builtin.Int64, 1 // users: %40, %27
> %26 = load %17 // user: %27
> %27 = builtin "sadd\_with\_overflow\_Int64"(%26, %25, %11) : $(Builtin.Int64, Builtin.Int1) // user: %28
> %28 = tuple\_extract %27, 0 // users: %36, %29
> %29 = struct $Int (%28) // user: %30
> store %29 to %2 // id: %30
> %31 = builtin "uadd\_with\_overflow\_Int8"(%7, %10, %11) : $(Builtin.Int8, Builtin.Int1) // user: %32
> %32 = tuple\_extract %31, 0 // user: %33
> %33 = struct $UInt8 (%32) // users: %38, %34
> debug\_value %33, let, name "$0", argno 1 // id: %34
> debug\_value %2, var, name "i", argno 3, expr op\_deref // id: %35
> %36 = builtin "truncOrBitCast\_Int64\_Word"(%28) : $Builtin.Word // user: %37
> %37 = index\_addr \[stack\_protection\] %22, %36 // user: %38
> store %33 to %37 // id: %38
> %39 = load %17 // user: %40
> %40 = builtin "sadd\_with\_overflow\_Int64"(%39, %25, %11) : $(Builtin.Int64, Builtin.Int1) // user: %41
> %41 = tuple\_extract %40, 0 // user: %42
> %42 = struct $Int (%41) // user: %43
> store %42 to %2 // id: %43
> %44 = tuple () // user: %45
> return %44 // id: %45
> } // end sil function 'function signature specialization \<Arg\[1\] = Exploded\> of function signature specialization \<Arg\[1\] = \[Closure Propagated : $s4main9viaBufferySiSv\_SitFys5UInt8VXEfU0\_, Argument Types : \[Swift.UnsafeMutableBufferPointer\<Swift.UInt8\>Swift.Int\]\> of main.asDecimal(\_: Swift.UInt8, writeByte: (Swift.UInt8) -\> ()) -\> ()'
> 
> 
> 
> // Mappings from '#fileID' to '#filePath':
> // 'main/issue-89954.swift' =\> 'issue-89954.swift'
> \`\`\`
> 
> \</details\>
> 
> You can see that \`viaMutableSpan\`'s SIL is as follows:
> 
> \`\`\`bash
> // viaMutableSpan(\_:\_:)
> // Isolation: unspecified
> sil \[noinline\] @main.viaMutableSpan(Swift.UnsafeMutableRawPointer, Swift.Int) -\> Swift.Int : $@convention(thin) (UnsafeMutableRawPointer, Int) -\> Int {
> \[%0: noescape \*\*, read v\*\*.c\*.v\*\*, write v\*\*.c\*.v\*\*, copy v\*\*.c\*.v\*\*, destroy v\*\*.c\*.v\*\*\]
> \[global: read,write,copy,destroy,allocate,deinit\_barrier\]
> // %0 "p" // users: %11, %2
> // %1 "n" // users: %6, %3
> bb0(%0 : $UnsafeMutableRawPointer, %1 : $Int):
> debug\_value %0, let, name "p", argno 1 // id: %2
> debug\_value %1, let, name "n", argno 2 // id: %3
> %4 = alloc\_stack \[lexical\] \[var\_decl\] $MutableSpan\<UInt8\>, var, name "span", type $MutableSpan\<UInt8\> // users: %13, %29, %28, %21, %20, %34
> %5 = integer\_literal $Builtin.Int64, 0 // users: %15, %7
> %6 = struct\_extract %1, #Int.\_value // users: %9, %7
> %7 = builtin "cmp\_slt\_Int64"(%6, %5) : $Builtin.Int1 // user: %8
> cond\_fail %7, "Count must not be negative" // id: %8
> %9 = builtin "assumeNonNegative\_Int64"(%6) : $Builtin.Int64 // user: %10
> %10 = struct $Int (%9) // user: %12
> %11 = enum $Optional\<UnsafeMutableRawPointer\>, #Optional.some!enumelt, %0 // user: %12
> %12 = struct $MutableSpan\<UInt8\> (%11, %10) // user: %13
> store %12 to %4 // id: %13
> %14 = alloc\_stack \[var\_decl\] $Int, var, name "i", type $Int // users: %32, %16, %28, %20, %33
> %15 = struct $Int (%5) // user: %16
> store %15 to %14 // id: %16
> %17 = integer\_literal $Builtin.Int8, 123 // user: %18
> %18 = struct $UInt8 (%17) // user: %23
> // function\_ref closure #1 in viaMutableSpan(\_:\_:)
> %19 = function\_ref @closure #1 (Swift.UInt8) -\> () in main.viaMutableSpan(Swift.UnsafeMutableRawPointer, Swift.Int) -\> Swift.Int : $@convention(thin) (UInt8, @inout\_aliasable MutableSpan\<UInt8\>, @inout\_aliasable Int) -\> () // user: %20
> %20 = partial\_apply \[callee\_guaranteed\] \[on\_stack\] %19(%4, %14) : $@convention(thin) (UInt8, @inout\_aliasable MutableSpan\<UInt8\>, @inout\_aliasable Int) -\> () // users: %24, %21
> %21 = mark\_dependence \[nonescaping\] %20 on %4 // user: %23
> // function\_ref asDecimal(\_:writeByte:)
> %22 = function\_ref @main.asDecimal(\_: Swift.UInt8, writeByte: (Swift.UInt8) -\> ()) -\> () : $@convention(thin) (UInt8, @guaranteed @noescape @callee\_guaranteed (UInt8) -\> ()) -\> () // users: %30, %23
> %23 = apply %22(%18, %21) : $@convention(thin) (UInt8, @guaranteed @noescape @callee\_guaranteed (UInt8) -\> ()) -\> ()
> dealloc\_stack %20 // id: %24
> %25 = integer\_literal $Builtin.Int8, 45 // user: %26
> %26 = struct $UInt8 (%25) // user: %30
> // function\_ref closure #2 in viaMutableSpan(\_:\_:)
> %27 = function\_ref @closure #2 (Swift.UInt8) -\> () in main.viaMutableSpan(Swift.UnsafeMutableRawPointer, Swift.Int) -\> Swift.Int : $@convention(thin) (UInt8, @inout\_aliasable MutableSpan\<UInt8\>, @inout\_aliasable Int) -\> () // user: %28
> %28 = partial\_apply \[callee\_guaranteed\] \[on\_stack\] %27(%4, %14) : $@convention(thin) (UInt8, @inout\_aliasable MutableSpan\<UInt8\>, @inout\_aliasable Int) -\> () // users: %31, %29
> %29 = mark\_dependence \[nonescaping\] %28 on %4 // user: %30
> %30 = apply %22(%26, %29) : $@convention(thin) (UInt8, @guaranteed @noescape @callee\_guaranteed (UInt8) -\> ()) -\> ()
> dealloc\_stack %28 // id: %31
> %32 = load %14 // user: %35
> dealloc\_stack %14 // id: %33
> dealloc\_stack %4 // id: %34
> return %32 // id: %35
> } // end sil function 'main.viaMutableSpan(Swift.UnsafeMutableRawPointer, Swift.Int) -\> Swift.Int'
> \`\`\`
> 
> And for \`viaBuffer\`:
> 
> \`\`\`bash
> // viaBuffer(\_:\_:)
> // Isolation: unspecified
> sil \[noinline\] @main.viaBuffer(Swift.UnsafeMutableRawPointer, Swift.Int) -\> Swift.Int : $@convention(thin) (UnsafeMutableRawPointer, Int) -\> Int {
> \[%0: noescape \*\*, write s0.v\*\*.c\*.v\*\*\]
> \[global: write,deinit\_barrier\]
> // %0 "p" // users: %4, %2
> // %1 "n" // user: %3
> bb0(%0 : $UnsafeMutableRawPointer, %1 : $Int):
> debug\_value %0, let, name "p", argno 1 // id: %2
> debug\_value %1, let, name "n", argno 2 // id: %3
> %4 = struct\_extract %0, #UnsafeMutableRawPointer.\_rawValue // user: %5
> %5 = struct $UnsafeMutablePointer\<UInt8\> (%4) // user: %6
> %6 = enum $Optional\<UnsafeMutablePointer\<UInt8\>\>, #Optional.some!enumelt, %5 // users: %7, %19, %15
> debug\_value %6, let, name "buf", type $UnsafeMutableBufferPointer\<UInt8\>, expr op\_fragment:#UnsafeMutableBufferPointer.\_position // id: %7
> %8 = alloc\_stack \[var\_decl\] $Int, var, name "i", type $Int // users: %15, %19, %20, %11, %21
> %9 = integer\_literal $Builtin.Int64, 0 // user: %10
> %10 = struct $Int (%9) // user: %11
> store %10 to %8 // id: %11
> %12 = integer\_literal $Builtin.Int8, 123 // user: %13
> %13 = struct $UInt8 (%12) // user: %15
> // function\_ref specialized asDecimal(\_:writeByte:)
> %14 = function\_ref @function signature specialization \<Arg\[1\] = Exploded\> of function signature specialization \<Arg\[1\] = \[Closure Propagated : $s4main9viaBufferySiSv\_SitFys5UInt8VXEfU\_, Argument Types : \[Swift.UnsafeMutableBufferPointer\<Swift.UInt8\>Swift.Int\]\> of main.asDecimal(\_: Swift.UInt8, writeByte: (Swift.UInt8) -\> ()) -\> () : $@convention(thin) (UInt8, Optional\<UnsafeMutablePointer\<UInt8\>\>, @inout\_aliasable Int) -\> () // user: %15
> %15 = apply %14(%13, %6, %8) : $@convention(thin) (UInt8, Optional\<UnsafeMutablePointer\<UInt8\>\>, @inout\_aliasable Int) -\> ()
> %16 = integer\_literal $Builtin.Int8, 45 // user: %17
> %17 = struct $UInt8 (%16) // user: %19
> // function\_ref specialized asDecimal(\_:writeByte:)
> %18 = function\_ref @function signature specialization \<Arg\[1\] = Exploded\> of function signature specialization \<Arg\[1\] = \[Closure Propagated : $s4main9viaBufferySiSv\_SitFys5UInt8VXEfU0\_, Argument Types : \[Swift.UnsafeMutableBufferPointer\<Swift.UInt8\>Swift.Int\]\> of main.asDecimal(\_: Swift.UInt8, writeByte: (Swift.UInt8) -\> ()) -\> () : $@convention(thin) (UInt8, Optional\<UnsafeMutablePointer\<UInt8\>\>, @inout\_aliasable Int) -\> () // user: %19
> %19 = apply %18(%17, %6, %8) : $@convention(thin) (UInt8, Optional\<UnsafeMutablePointer\<UInt8\>\>, @inout\_aliasable Int) -\> ()
> %20 = load %8 // user: %22
> dealloc\_stack %8 // id: %21
> return %20 // id: %22
> } // end sil function 'main.viaBuffer(Swift.UnsafeMutableRawPointer, Swift.Int) -\> Swift.Int'
> \`\`\`
> 
> One difference is that in \`viaMutableSpan\` we have:
> \`\`\`bash
> function\_ref closure #1 in viaSpan(\_:\_:)
> \`\`\`
> But in \`viaBuffer\`:
> \`\`\`bash
> function\_ref specialized asDecimal(\_:writeByte:)
> \`\`\`
> 
> So clearly, in \`viaMutableSpan\`, the \`asDecimal(\_:writeByte:)\` function fails to get specialized/inlined.
> This then disables some optimizations of the compiler, which result in a relatively considerable amount of lower performance for \`MutableSpan\`.
> 
> In Claude's words:
> 
> \> Both functions are the same shape; the only difference is the captured type. MutableSpan (~Escapable) ⇒ mark\_dependence ⇒ optimizer gives up ⇒ generic call with an indirect closure call. UnsafeMutableBufferPointer (Copyable) ⇒ no mark\_dependence ⇒ optimizer specializes ⇒ direct call. That contrast is the bug.
> 
> \### The solution
> 
> I'm not a compiler engineer so the changes are made by Claude. I did persuade it a lot to try to make sure it has made proper changes, but I can't be sure that it actually did (well, apart from the fact that the changes are indeed working, since I asked Claude to compile and verify as well even though it takes a while to compile):
> 
> \`\`\`md
> \### Problem
> findSpecializableClosure's MarkDependenceInst case bailed unless visited.contains(mdi.base). A non-escaping closure capturing a ~Escapable value has a mark\_dependence whose base is a capture (not in the chain), so it never specialized — leaving a generic callee with a per-call indirect apply.
> 
> \### Fix
> Accept when the base is a captured operand of the root closure (computed lazily, so it's only evaluated when the visited check fails):
> 
> 
> var baseIsRootClosureCapture: Bool {
> (operandClosure as? PartialApplyInst)?.arguments.contains { $0 == mdi.base } ?? false
> }
> Existing gates still run via the recursion into mdi.value.
> 
> Resolve the base into the specialization. uniqueCaptureArguments rewrites the partial\_apply operands to identity casts but not the mark\_dependence base, so mdi.base == cast.fromValue. visitMarkDependenceInst resolves the base through the cloner map, so fold the pre-cast value too:
> 
> 
> if let cast = originalClosureArg as? UncheckedValueCastInst,
> !cloner.isCloned(value: cast.fromValue) {
> cloner.recordFoldedValue(cast.fromValue, mappedTo: capturedArg)
> }
> Guard: the cloner map is value-keyed, so bail if a base is captured \>1× (otherwise a reused specialization mis-maps). hasMultiplyCapturedDependenceBase walks the closure-argument chain (mirroring findSpecializableClosure, including reabstraction thunks) and short-circuits via isCapturedMoreThanOnce — no value accumulation.
> 
> \### Soundness
> Gated on isNoEscapeFunction ⇒ captures are forwarded @guaranteed and outlive the apply; the mark\_dependence is re-materialized on the new argument. Every accepted base is either cloned (in chain) or a uniquely-captured value folded to its arg.
> 
> \### Tests
> closure\_specialization\_attrs.sil updated (on-stack \[readonly\] now specializes, @guaranteed, no retain). Full test/SILOptimizer green (1167 passed, 0 failures); standard library rebuilds under -enable-sil-verify-all with no optimizer crash; the reproducer specializes (no residual partial\_apply/mark\_dependence).
> \`\`\`
> 
> \### Environment
> 
> \`\`\`bash
> $ swiftc --version
> swift-driver version: 1.167 Apple Swift version 6.4 (swiftlang-6.4.0.20.104 clang-2100.3.20.102)
> Target: arm64-apple-macosx27.0.0
> \`\`\`

Essentially It's a bug in the compiler which affects non-escapable types, where it can skip specializing/inlining functions.
