Skip to content

Replace small stackallocs with collection literals#128389

Merged
EgorBo merged 1 commit into
dotnet:mainfrom
EgorBo:small-stackallocs
May 21, 2026
Merged

Replace small stackallocs with collection literals#128389
EgorBo merged 1 commit into
dotnet:mainfrom
EgorBo:small-stackallocs

Conversation

@EgorBo
Copy link
Copy Markdown
Member

@EgorBo EgorBo commented May 20, 2026

Replace very small stackallocs with collection literals (since stackallocs require unsafe context).

Copilot AI review requested due to automatic review settings May 20, 2026 00:26
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR replaces several very small stackalloc scratch buffers with C# collection expressions targeting Span<T> / ReadOnlySpan<T>, and removes unsafe modifiers that were only present to enable those small stackallocs.

Changes:

  • Replaced tiny stackalloc buffers (e.g., 1–4 elements) with collection expressions (e.g., [0, 0, 0, 0], ['\0', '\0']) across multiple libraries.
  • Removed unsafe from a number of methods / local functions where it was only needed for those stackallocs.
  • Kept existing behavior by continuing to use stack-based scratch spans for transient buffers (subject to compiler lowering), while simplifying method signatures.

Reviewed changes

Copilot reviewed 26 out of 26 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexNode.cs Removes an unsafe local function and replaces tiny scratch stackalloc spans with collection expressions.
src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs Replaces small stackalloc spans used for category / set-char probing with collection expressions.
src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.StringSegment.cs Removes unsafe from leftover-handling helpers and replaces small combined-buffer stackallocs with collection expressions.
src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X500DistinguishedNameBuilder.cs Removes unsafe from AddCountryOrRegion and uses a 2-char collection expression buffer.
src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/LiteHash.Windows.cs Replaces a 1-byte stackalloc scratch buffer with [0].
src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs Removes unsafe from the BigInteger(decimal) ctor and uses a 4-int collection expression buffer.
src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/DecimalUtilities.cs Removes unsafe from helpers and uses 4-int collection expression buffers.
src/libraries/System.Private.Uri/src/System/UriHelper.cs Removes unsafe from EscapeStringToBuilder and replaces a 4-byte scratch stackalloc with a collection expression.
src/libraries/System.Private.Uri/src/System/IriHelper.cs Removes unsafe and replaces a 4-byte scratch stackalloc with a collection expression.
src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryWriter.cs Removes unsafe from decimal write paths and replaces 4-int stackalloc with collection expressions.
src/libraries/System.Private.CoreLib/src/System/Text/Encoding.Internal.cs Removes unsafe from a virtual helper and updates a disabled #if false sample to use collection expressions.
src/libraries/System.Private.CoreLib/src/System/Text/DecoderNLS.cs Removes unsafe from leftover-drain helpers and replaces 4-byte combined-buffer stackallocs with collection expressions.
src/libraries/System.Private.CoreLib/src/System/SearchValues/Strings/Helpers/AhoCorasick.cs Removes unsafe from a helper and replaces a 2-char destination stackalloc with a collection expression.
src/libraries/System.Private.CoreLib/src/System/SearchValues/SearchValues.cs Replaces a 4-char stackalloc scratch buffer with a collection expression.
src/libraries/System.Private.CoreLib/src/System/Resources/ResourceReader.cs Removes unsafe from _LoadObjectV1 and replaces a 4-int stackalloc with a collection expression.
src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs Removes unsafe from EscapeStringToBuilder and replaces a 4-byte scratch stackalloc with a collection expression.
src/libraries/System.Private.CoreLib/src/System/IO/UnmanagedMemoryAccessor.cs Replaces a 4-int stackalloc scratch buffer with a collection expression.
src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.cs Removes unsafe from AddTitlecaseLetter and replaces a few 2-char scratch stackallocs with collection expressions.
src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/ILGen.cs Removes unsafe from EmitDecimal and replaces a 4-int stackalloc with a collection expression.
src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/PipeReaderStream.cs Removes unsafe from ReadByte and replaces a 1-byte stackalloc with [0].
src/libraries/System.IO.Compression/src/System/IO/Compression/Zstandard/ZstandardStream.Decompress.cs Removes unsafe from ReadByte and replaces a 1-byte stackalloc with [0].
src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Writer/CborWriter.Tag.cs Replaces 4-int stackalloc scratch buffers with collection expressions.
src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs Removes unsafe from Id-related members / ctor and replaces tiny stackalloc spans with collection expressions.
src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLDecimal.cs Removes unsafe from SqlDecimal(decimal) and replaces a 4-int stackalloc with a collection expression.
src/libraries/Common/src/System/Number.Formatting.Common.cs Replaces a 4-int stackalloc scratch buffer with a collection expression.
src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptEncryptDecrypt.RSA.cs Replaces a 1-byte stackalloc scratch span with a collection expression.

Comment thread src/libraries/System.Private.Uri/src/System/UriHelper.cs
@EgorBo
Copy link
Copy Markdown
Member Author

EgorBo commented May 20, 2026

@MihuBot

@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-meta
See info in area-owners.md if you want to be subscribed.

@JakeYallop
Copy link
Copy Markdown
Contributor

Out of my own curiosity, with SkipUnsafeInit, is this technically a small regression (not saying its not worth the change though)?

@EgorBo
Copy link
Copy Markdown
Member Author

EgorBo commented May 20, 2026

Out of my own curiosity, with SkipUnsafeInit, is this technically a small regression (not saying its not worth the change though)?

It's actually an improvement according to diffs:

  • no GS check
  • more inliner friendly (inliner is very conservative when it sees localloc)
  • struct promotion friendly it seems

The downside is one instruction (in all these cases) to zero a couple of bytes on stack.

But the main motivation was to remove unsafe

@EgorBo EgorBo marked this pull request as ready for review May 20, 2026 10:28
@EgorBo
Copy link
Copy Markdown
Member Author

EgorBo commented May 20, 2026

PTAL @MihaZupan @tannergooding removes 26 unsafe contexts. Not more than 4 elements to keep it readable.

Long terms we need either an opposite of SkipLocalsInit, or Buffer.Allocate (safe variant) API. Or inline arrays, but they need some polishment to work nicely (e.g. Span<byte> span = new InlineArray8<byte>();)

@EgorBo EgorBo enabled auto-merge (squash) May 21, 2026 19:40
@EgorBo EgorBo merged commit 96959a6 into dotnet:main May 21, 2026
160 of 165 checks passed
@EgorBo EgorBo deleted the small-stackallocs branch May 21, 2026 19:40
@dotnet-milestone-bot dotnet-milestone-bot Bot added this to the 11.0-preview6 milestone May 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants