Skip to content

Remove x86 per-source-register write barrier helpers#128797

Closed
EgorBo wants to merge 2 commits into
dotnet:mainfrom
EgorBo:egor/x86-wb-cleanup
Closed

Remove x86 per-source-register write barrier helpers#128797
EgorBo wants to merge 2 commits into
dotnet:mainfrom
EgorBo:egor/x86-wb-cleanup

Conversation

@EgorBo
Copy link
Copy Markdown
Member

@EgorBo EgorBo commented May 30, 2026

Removes the per-source-register x86 write barrier helpers (CORINFO_HELP_ASSIGN_REF_EAX etc.) and the related dead infrastructure across the JIT-EE interface, R2R format, AOT tools, and the CoreCLR + NativeAOT runtimes. The JIT now always emits the unified CORINFO_HELP_ASSIGN_REF / CORINFO_HELP_CHECKED_ASSIGN_REF on x86, matching x64.

Bumps JITEEVersionIdentifier and R2R major version (19 → 20).

Note

This pull request was created with help from GitHub Copilot.

Match what x64 and other targets already do: always emit a call to the unified CORINFO_HELP_ASSIGN_REF / CORINFO_HELP_CHECKED_ASSIGN_REF helper instead of selecting between per-source-register specialized helpers (CORINFO_HELP_ASSIGN_REF_EAX, etc.).

The runtime universal helper already has the narrow ABI: ecx = dst, edx = src, only eax/edx trashed, all callee-saved registers and ecx preserved. The JIT continues to use RBM_CALLEE_TRASH_WRITEBARRIER (RBM_EAX | RBM_EDX) as the kill set, so it knows what's preserved across the call.

This follows the same JIT-only first-step pattern as dotnet#128542 (Remove CORINFO_HELP_ASSIGN_BYREF). The enum values, jithelpers.h, readytorunhelpers.h, R2R format, runtime asm helpers (jithelp.asm/S, WriteBarriers.asm/S), jitinterfacex86.cpp, excep.cpp, and AOT tools are intentionally left alone for a follow-up cleanup similar to dotnet#128687.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 30, 2026 04:42
@github-actions github-actions Bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label May 30, 2026
@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

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

Removes the per-source-register x86 write barrier infrastructure (CORINFO_HELP_ASSIGN_REF_<reg> / CORINFO_HELP_CHECKED_ASSIGN_REF_<reg> and the matching Rhp*Reg, JIT_WriteBarrier<reg>, READYTORUN_HELPER_WriteBarrier_<reg> helpers) so x86 codegen always uses the unified CORINFO_HELP_ASSIGN_REF / CORINFO_HELP_CHECKED_ASSIGN_REF with a fixed ECX = dst, EDX = src ABI, matching x64. JIT-EE GUID and R2R major version are bumped to reflect the breaking change.

Changes:

  • Delete per-register helpers from CorInfo, jithelpers, ReadyToRun, AOT tools, CoreCLR i386 asm/cpp, NativeAOT i386 asm and EH tables.
  • Collapse JIT write-barrier lowering on x86 to the unified path (genUseOptimizedWriteBarriers, NOGC_WRITE_BARRIERS, per-arch defines, and genEmitOptimizedGCWriteBarrier all removed).
  • Bump JITEEVersionIdentifier, READYTORUN_MAJOR_VERSION and MINIMUM_READYTORUN_MAJOR_VERSION from 19 to 20; mark the old R2R helper IDs as unused.

Reviewed changes

Copilot reviewed 36 out of 36 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/coreclr/inc/corinfo.h, tools/Common/JitInterface/CorInfoHelpFunc.cs Remove CORINFO_HELP_(CHECKED_)ASSIGN_REF_<reg> enum entries
src/coreclr/inc/jithelpers.h Drop x86 per-register Rhp(Checked)AssignRef<reg> mappings
src/coreclr/inc/jiteeversionguid.h Bump JIT/EE GUID
src/coreclr/inc/readytorun.h, readytorunhelpers.h, tools/Common/Internal/Runtime/ReadyToRunConstants.cs, ModuleHeaders.cs, nativeaot/Runtime/inc/ModuleHeaders.h Bump R2R version to 20 and annotate retired x86 helper IDs
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs, ILCompiler.ReadyToRun/.../CorInfoImpl.ReadyToRun.cs, ILCompiler.RyuJit/.../CorInfoImpl.RyuJit.cs, ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs Remove AOT/R2R mappings + display names for the deleted helpers
src/coreclr/jit/codegencommon.cpp, codegeninterface.h, codegen.h, codegenxarch.cpp, lsrabuild.cpp, utils.cpp Drop genUseOptimizedWriteBarriers / genEmitOptimizedGCWriteBarrier / RBM_OPTIMIZED_WRITE_BARRIER_* and per-register kill-sets; route x86 through unified helper
src/coreclr/jit/targetx86.h, targetamd64.h, targetarm.h, targetarm64.h, targetloongarch64.h, targetriscv64.h, targetwasm.h Remove NOGC_WRITE_BARRIERS and RBM_OPTIMIZED_WRITE_BARRIER_* defines; update x86 write-barrier ABI comments
src/coreclr/vm/jitinterface.h, threads.cpp, i386/jithelp.asm, i386/jithelp.S, i386/jitinterfacex86.cpp Keep only the EAX template; simplify init/patch/stomp logic; remove per-register init loop
src/coreclr/vm/excep.cpp, nativeaot/Runtime/EHHelpers.cpp Drop x86 per-register Rhp(Checked)AssignRef<reg>AVLocation AV tables
src/coreclr/runtime/i386/WriteBarriers.asm, WriteBarriers.S Remove the per-source-register DEFINE_(CHECKED_)WRITE_BARRIER EDX, <reg> instantiations
docs/design/coreclr/botr/readytorun-format.md Annotate retired x86 R2R write-barrier helper IDs

@jakobbotsch
Copy link
Copy Markdown
Member

jakobbotsch commented May 30, 2026

Isn't this just a deoptimization? What is the motivation?
We have multiple PRs clashing over taking R2R version changes (#128664, #128706, #128397) so I would suggest waiting until the dust settles with some of this.

@EgorBo
Copy link
Copy Markdown
Member Author

EgorBo commented May 30, 2026

Isn't this just a deoptimization? What is the motivation? We have multiple PRs clashing over taking R2R version changes (#128664, #128706, #128397) so I would suggest waiting until the dust settles with some of this.

The motiviation is simplification if the diffs imply it's not worth having multiple versions of WB and +700 LOC. My assumption was "if we don't do this on any other arch - maybe it's not worth it?" I'm fine keeping this open till other PRs you listed land.

@EgorBo EgorBo force-pushed the egor/x86-wb-cleanup branch from 75d9d11 to 6fcdf3b Compare May 30, 2026 12:56
Copilot AI review requested due to automatic review settings May 30, 2026 13:12
@EgorBo EgorBo force-pushed the egor/x86-wb-cleanup branch from 6fcdf3b to 69d63c6 Compare May 30, 2026 13:12
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

Copilot reviewed 32 out of 32 changed files in this pull request and generated 3 comments.

Comment thread src/coreclr/inc/corinfo.h Outdated
Comment thread src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs Outdated
Comment thread src/coreclr/inc/readytorun.h Outdated
… code

Follow-up to the prior JIT-only cleanup. Removes the now-dead infrastructure for the per-source-register x86 write barriers across the JIT-EE interface, R2R format, AOT tools (crossgen2 / NativeAOT compiler), and the CoreCLR + NativeAOT runtimes.

Changes:

 - JIT-EE interface: remove the 12 CORINFO_HELP_(CHECKED_)ASSIGN_REF_E[reg] enum values from corinfo.h, jithelpers.h, and the managed mirror; bump JITEEVersionIdentifier; remove the X86 case-label block in utils.cpp's HelperCallProperties::init.

 - R2R format: bump major version to 20; mark slots 0x100-0x10B (READYTORUN_HELPER_(Checked)WriteBarrier_E[reg]) as unused (kept for image-compat parsing); update botr/readytorun-format.md, ModuleHeaders.h/cs, ReadyToRunConstants.cs; remove HELPER() lines in readytorunhelpers.h.

 - AOT tools: remove switch cases in CorInfoImpl.RyuJit.cs, CorInfoImpl.ReadyToRun.cs, ReadyToRunSignature.cs, and JitHelper.cs.

 - NativeAOT runtime: remove the per-register DEFINE_(CHECKED_)WRITE_BARRIER invocations and the corresponding RhpAssignRefE[reg]AVLocation references in EHHelpers.cpp.

 - CoreCLR x86 VM: remove all per-register variants except EAX from vm/i386/jithelp.asm/.S (EAX is kept as the patched target of the universal @JIT_WriteBarrier@8 trampoline). Drop the never-called @JIT_CheckedWriteBarrier@8 trampoline entirely (x86's CORINFO_HELP_CHECKED_ASSIGN_REF maps to RhpCheckedAssignRef, not JIT_CheckedWriteBarrier). Simplify the c_rgWriteBarriers/c_rgDebugWriteBarriers tables and patching loops in vm/i386/jitinterfacex86.cpp to operate on the single EAX entry. Drop the dead per-reg AV location declarations from vm/excep.cpp and the X86_WRITE_BARRIER_REGISTER macro in vm/threads.cpp and vm/jitinterface.h.

Validation: clr+libs builds cleanly for x86 and x64; clr.tools and clr.nativeaotruntime build cleanly; x64 GC write-barrier exercising test passes with the rebuilt JIT (exit 100). The same x86 e2e test cannot be run on this machine due to a pre-existing testhost startup issue that affects baseline main as well.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@EgorBo EgorBo force-pushed the egor/x86-wb-cleanup branch from 69d63c6 to d9ac263 Compare May 30, 2026 13:51
@EgorBo
Copy link
Copy Markdown
Member Author

EgorBo commented May 30, 2026

Seems like it's quite a big size regression (obviously, I did expect it, but not that big):

Found 474 files with textual diffs.

Summary of Code Size diffs:
(Lower is better)

Total bytes of base: 61393713
Total bytes of diff: 62753916
Total bytes of delta: 1360203 (2.22 % of base)
Total relative delta: NaN
    diff is a regression.
    relative diff is a regression.


Top file regressions (bytes):
      244175 : Microsoft.CodeAnalysis.CSharp.dasm (3.02% of base)
      194476 : Microsoft.CodeAnalysis.VisualBasic.dasm (3.13% of base)
       92669 : Microsoft.Diagnostics.Tracing.TraceEvent.dasm (2.98% of base)
       88840 : System.Private.Xml.dasm (2.82% of base)
       86927 : System.Linq.Parallel.dasm (5.37% of base)
       81700 : Microsoft.CodeAnalysis.dasm (1.85% of base)
       58590 : System.Private.CoreLib.dasm (0.91% of base)
       43821 : FSharp.Core.dasm (6.43% of base)
       23036 : CommandLine.dasm (5.38% of base)
       19631 : System.Collections.Immutable.dasm (1.15% of base)
       18174 : System.Threading.Tasks.Dataflow.dasm (1.87% of base)
       17458 : System.Data.Common.dasm (1.38% of base)
       16912 : System.Linq.Expressions.dasm (2.62% of base)
       16682 : System.Text.Json.dasm (1.32% of base)
       16106 : System.Management.dasm (5.86% of base)
       15203 : System.Linq.dasm (1.36% of base)
       14865 : System.Net.Http.dasm (2.14% of base)
       14199 : Newtonsoft.Json.dasm (1.89% of base)
       13796 : System.Security.Cryptography.dasm (1.41% of base)
       12744 : Microsoft.CSharp.dasm (4.06% of base)
       12257 : System.Text.RegularExpressions.dasm (2.07% of base)
       12080 : System.Collections.dasm (2.25% of base)
       11967 : System.Private.DataContractSerialization.dasm (1.70% of base)
       10594 : System.Linq.AsyncEnumerable.dasm (0.50% of base)
        8537 : System.DirectoryServices.AccountManagement.dasm (2.89% of base)

Top file improvements (bytes):
          -8 : System.Security.Cryptography.ProtectedData.dasm (-0.15% of base)
          -2 : System.Runtime.CompilerServices.VisualC.dasm (-8.00% of base)

191 total files with Code Size differences (2 improved, 189 regressed), 90 unchanged.

Top method regressions (bytes):
        3792 (25.94% of base) : System.Private.Xml.dasm - System.Xml.Schema.XsdBuilder:.cctor() (FullOpts)
        2662 (14.21% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - Microsoft.Diagnostics.Tracing.Parsers.ClrPrivateTraceEventParser:EnumerateTemplates(System.Func`3[System.String,System.String,int],System.Action`1[Microsoft.Diagnostics.Tracing.TraceEvent]):this (FullOpts)
        2456 (13.94% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - Microsoft.Diagnostics.Tracing.Parsers.ClrTraceEventParser:EnumerateTemplates(System.Func`3[System.String,System.String,int],System.Action`1[Microsoft.Diagnostics.Tracing.TraceEvent]):this (FullOpts)
        1933 (22.94% of base) : System.DirectoryServices.AccountManagement.dasm - System.DirectoryServices.AccountManagement.ADStoreCtx:.cctor() (FullOpts)
        1537 (20.31% of base) : System.DirectoryServices.AccountManagement.dasm - System.DirectoryServices.AccountManagement.ADAMStoreCtx:.cctor() (FullOpts)
        1517 (26.06% of base) : System.Private.Xml.dasm - System.Xml.Schema.DatatypeImplementation:.cctor() (FullOpts)
        1491 (17.37% of base) : System.DirectoryServices.AccountManagement.dasm - System.DirectoryServices.AccountManagement.SAMStoreCtx:.cctor() (FullOpts)
        1362 (16.53% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - Microsoft.Diagnostics.Tracing.Parsers.MicrosoftAntimalwareEngineTraceEventParser:EnumerateTemplates(System.Func`3[System.String,System.String,int],System.Action`1[Microsoft.Diagnostics.Tracing.TraceEvent]):this (FullOpts)
        1302 (13.98% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - Microsoft.Diagnostics.Tracing.Parsers.AspNet.AspNetTraceEventParser:EnumerateTemplates(System.Func`3[System.String,System.String,int],System.Action`1[Microsoft.Diagnostics.Tracing.TraceEvent]):this (FullOpts)
        1269 ( 9.61% of base) : FSharp.Core.dasm - Microsoft.FSharp.Quotations.FSharpExpr:GetLayout(bool):Microsoft.FSharp.Text.StructuredPrintfImpl.Layout:this (FullOpts)
        1195 ( 9.25% of base) : System.Management.dasm - System.Management.ManagementClassGenerator:GenerateMethods():this (FullOpts)
        1135 (14.07% of base) : Microsoft.CSharp.dasm - Microsoft.CSharp.RuntimeBinder.Semantics.PredefinedMembers:.cctor() (FullOpts)
        1132 (21.54% of base) : System.Private.Xml.dasm - System.Xml.Schema.XdrBuilder:.cctor() (FullOpts)
        1093 (12.85% of base) : Microsoft.CodeAnalysis.CSharp.dasm - Microsoft.CodeAnalysis.CSharp.Symbols.SynthesizedReadOnlyListTypeSymbol:.ctor(Microsoft.CodeAnalysis.CSharp.Symbols.SourceModuleSymbol,System.String,int,bool):this (FullOpts)
        1042 (16.83% of base) : System.Management.dasm - System.Management.ManagementClassGenerator:AddToDMTFTimeIntervalFunction():this (FullOpts)
        1000 ( 3.33% of base) : Microsoft.CodeAnalysis.VisualBasic.dasm - Microsoft.CodeAnalysis.VisualBasic.VisualBasicCommandLineParser:Parse(System.Collections.Generic.IEnumerable`1[System.String],System.String,System.String,System.String):Microsoft.CodeAnalysis.VisualBasic.VisualBasicCommandLineArguments:this (FullOpts)
         973 (11.21% of base) : System.Management.dasm - System.Management.ManagementClassGenerator:AddToDateTimeFunction():this (FullOpts)
         966 (13.44% of base) : System.Management.dasm - System.Management.ManagementClassGenerator:AddToDMTFDateTimeFunction():this (FullOpts)
         958 ( 8.91% of base) : Microsoft.CodeAnalysis.dasm - Microsoft.CodeAnalysis.AttributeDescription:.cctor() (FullOpts)
         951 (17.72% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - Microsoft.Diagnostics.Tracing.Etlx.TraceLog:SetupCallbacks(Microsoft.Diagnostics.Tracing.TraceEventDispatcher):this (FullOpts)
         905 ( 3.49% of base) : Microsoft.CodeAnalysis.CSharp.dasm - Microsoft.CodeAnalysis.CSharp.CSharpCommandLineParser:Parse(System.Collections.Generic.IEnumerable`1[System.String],System.String,System.String,System.String):Microsoft.CodeAnalysis.CSharp.CSharpCommandLineArguments:this (FullOpts)
         863 (46.80% of base) : System.Management.dasm - System.Management.WmiNetUtilsHelper:LoadPlatformNotSupportedDelegates(System.String) (FullOpts)
         846 (14.54% of base) : Microsoft.CodeAnalysis.VisualBasic.dasm - Microsoft.CodeAnalysis.VisualBasic.Symbols.SourceMemberContainerTypeSymbol:GenerateVarianceDiagnosticsForTypeRecursively(Microsoft.CodeAnalysis.VisualBasic.Symbols.TypeSymbol,short,int,Microsoft.CodeAnalysis.VisualBasic.Symbols.SourceMemberContainerTypeSymbol+VarianceDiagnosticsTargetTypeParameter,int,byref):this (FullOpts)
         789 (10.35% of base) : System.Text.RegularExpressions.dasm - System.Text.RegularExpressions.Symbolic.RegexNodeConverter:ConvertToSymbolicRegexNode(System.Text.RegularExpressions.RegexNode):System.Text.RegularExpressions.Symbolic.SymbolicRegexNode`1[System.Text.RegularExpressions.Symbolic.BDD]:this (FullOpts)
         786 ( 9.94% of base) : System.Management.dasm - System.Management.ManagementClassGenerator:GenerateProperties():this (FullOpts)

Top method improvements (bytes):
        -254 (-1.79% of base) : ILCompiler.Reflection.ReadyToRun.dasm - ILCompiler.Reflection.ReadyToRun.x86.InfoHdrDecoder:.cctor() (FullOpts)
        -230 (-3.74% of base) : ILCompiler.Reflection.ReadyToRun.dasm - ILCompiler.Reflection.ReadyToRun.SignatureDecoder:ParseHelper(System.Text.StringBuilder):this (FullOpts)
        -202 (-14.10% of base) : System.Private.Xml.dasm - System.Xml.Schema.XsdBuilder:SetContainer(int,System.Object):this (FullOpts)
        -190 (-3.63% of base) : ILCompiler.Reflection.ReadyToRun.dasm - ILCompiler.Reflection.ReadyToRun.SignatureDecoder:ParseSignature(int,System.Text.StringBuilder):ILCompiler.Reflection.ReadyToRun.ReadyToRunSignature:this (FullOpts)
        -165 (-3.68% of base) : System.Threading.Tasks.Dataflow.dasm - System.Threading.Tasks.Dataflow.TransformManyBlock`2+<StoreOutputItemsNonReorderedWithIterationAsync>d__48[System.__Canon,System.Nullable`1[int]]:MoveNext():this (FullOpts)
        -162 (-4.00% of base) : System.Linq.AsyncEnumerable.dasm - System.Linq.AsyncEnumerable+<TakeRangeFromEndIterator>d__194`1[System.Numerics.Vector`1[float]]:MoveNext():this (FullOpts)
        -143 (-6.72% of base) : Microsoft.CodeAnalysis.VisualBasic.dasm - Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxNode:DoGetSyntaxErrors(Microsoft.CodeAnalysis.SyntaxTree,Microsoft.CodeAnalysis.SyntaxNodeOrToken):System.Collections.ObjectModel.ReadOnlyCollection`1[Microsoft.CodeAnalysis.Diagnostic] (FullOpts)
        -117 (-0.41% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - Microsoft.Diagnostics.Tracing.Parsers.KernelTraceEventParser:EnumerateTemplates(System.Func`3[System.String,System.String,int],System.Action`1[Microsoft.Diagnostics.Tracing.TraceEvent]):this (FullOpts)
        -116 (-2.87% of base) : System.Linq.AsyncEnumerable.dasm - System.Linq.AsyncEnumerable+<<MaxByAsync>g__Impl|117_0>d`2[byte,System.Nullable`1[int]]:MoveNext():this (FullOpts)
        -116 (-2.88% of base) : System.Linq.AsyncEnumerable.dasm - System.Linq.AsyncEnumerable+<<MaxByAsync>g__Impl|117_0>d`2[int,System.Nullable`1[int]]:MoveNext():this (FullOpts)
        -116 (-2.78% of base) : System.Linq.AsyncEnumerable.dasm - System.Linq.AsyncEnumerable+<<MaxByAsync>g__Impl|117_0>d`2[long,System.Nullable`1[int]]:MoveNext():this (FullOpts)
        -116 (-2.85% of base) : System.Linq.AsyncEnumerable.dasm - System.Linq.AsyncEnumerable+<<MaxByAsync>g__Impl|117_0>d`2[short,System.Nullable`1[int]]:MoveNext():this (FullOpts)
        -116 (-2.68% of base) : System.Linq.AsyncEnumerable.dasm - System.Linq.AsyncEnumerable+<<MaxByAsync>g__Impl|117_0>d`2[System.Nullable`1[int],System.Nullable`1[int]]:MoveNext():this (FullOpts)
        -116 (-2.86% of base) : System.Linq.AsyncEnumerable.dasm - System.Linq.AsyncEnumerable+<<MinByAsync>g__Impl|124_0>d`2[byte,System.Nullable`1[int]]:MoveNext():this (FullOpts)
        -116 (-2.87% of base) : System.Linq.AsyncEnumerable.dasm - System.Linq.AsyncEnumerable+<<MinByAsync>g__Impl|124_0>d`2[int,System.Nullable`1[int]]:MoveNext():this (FullOpts)
        -116 (-2.77% of base) : System.Linq.AsyncEnumerable.dasm - System.Linq.AsyncEnumerable+<<MinByAsync>g__Impl|124_0>d`2[long,System.Nullable`1[int]]:MoveNext():this (FullOpts)
        -116 (-2.85% of base) : System.Linq.AsyncEnumerable.dasm - System.Linq.AsyncEnumerable+<<MinByAsync>g__Impl|124_0>d`2[short,System.Nullable`1[int]]:MoveNext():this (FullOpts)
        -116 (-2.68% of base) : System.Linq.AsyncEnumerable.dasm - System.Linq.AsyncEnumerable+<<MinByAsync>g__Impl|124_0>d`2[System.Nullable`1[int],System.Nullable`1[int]]:MoveNext():this (FullOpts)
        -114 (-5.59% of base) : Microsoft.CodeAnalysis.CSharp.dasm - Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree:BuildPreprocessorStateChangeMap():this (FullOpts)
        -112 (-7.76% of base) : System.Data.Common.dasm - System.Data.Common.DbConnectionOptions:GetKeyValuePair(System.String,int,System.Text.StringBuilder,bool,byref,byref):int (FullOpts)
        -112 (-7.76% of base) : System.Data.OleDb.dasm - System.Data.Common.DbConnectionOptions:GetKeyValuePair(System.String,int,System.Text.StringBuilder,bool,byref,byref):int (FullOpts)
        -112 (-7.76% of base) : System.Data.Odbc.dasm - System.Data.Common.DbConnectionOptions:GetKeyValuePair(System.String,int,System.Text.StringBuilder,bool,byref,byref):int (FullOpts)
        -111 (-2.64% of base) : System.Linq.AsyncEnumerable.dasm - System.Linq.AsyncEnumerable+<<MaxByAsync>g__Impl|117_0>d`2[double,System.Nullable`1[int]]:MoveNext():this (FullOpts)
        -111 (-2.64% of base) : System.Linq.AsyncEnumerable.dasm - System.Linq.AsyncEnumerable+<<MinByAsync>g__Impl|124_0>d`2[double,System.Nullable`1[int]]:MoveNext():this (FullOpts)
        -106 (-10.23% of base) : xunit.runner.utility.netcoreapp10.dasm - Xunit.DelegatingXmlCreationSink:OnMessageWithTypes(Xunit.Abstractions.IMessageSinkMessage,System.Collections.Generic.HashSet`1[System.String]):bool:this (FullOpts)

Top method regressions (percentages):
          15 (107.14% of base) : Microsoft.CodeAnalysis.CSharp.dasm - Microsoft.CodeAnalysis.CSharp.LambdaUtilities:IsLambdaBodyStatementOrExpression(Microsoft.CodeAnalysis.SyntaxNode,byref):bool (FullOpts)
          12 (100.00% of base) : System.Private.Xml.dasm - System.Xml.XPath.XPathDocument:GetCollapsedTextNode(byref):int:this (FullOpts)
          12 (100.00% of base) : System.Private.Xml.dasm - System.Xml.XPath.XPathDocument:GetRootNode(byref):int:this (FullOpts)
          12 (100.00% of base) : System.Private.Xml.dasm - System.Xml.XPath.XPathDocument:GetXmlNamespaceNode(byref):int:this (FullOpts)
          14 (82.35% of base) : System.Private.Xml.dasm - MS.Internal.Xml.XPath.UnionExpr:ProcessSamePosition(System.Xml.XPath.XPathNavigator):System.Xml.XPath.XPathNavigator:this (FullOpts)
          12 (80.00% of base) : System.Private.Xml.dasm - MS.Internal.Xml.Cache.XPathNode:GetParent(byref):int:this (FullOpts)
          12 (80.00% of base) : System.Private.Xml.dasm - MS.Internal.Xml.Cache.XPathNode:GetSibling(byref):int:this (FullOpts)
          12 (80.00% of base) : System.Private.Xml.dasm - MS.Internal.Xml.Cache.XPathNode:GetSimilarElement(byref):int:this (FullOpts)
          15 (78.95% of base) : System.Net.Http.dasm - System.Net.Http.HttpContent:SetHeaders(System.Net.Http.Headers.HttpContentHeaders):this (FullOpts)
          15 (78.95% of base) : System.Text.Json.dasm - System.Text.Json.Serialization.Metadata.JsonTypeInfo:SetPolymorphismOptions(System.Text.Json.Serialization.Metadata.JsonPolymorphismOptions):this (FullOpts)
          14 (73.68% of base) : Microsoft.CSharp.dasm - Microsoft.CSharp.RuntimeBinder.ComInterop.IDispatchComObject:.ctor(Microsoft.CSharp.RuntimeBinder.ComInterop.IDispatch):this (FullOpts)
          14 (73.68% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.Converters.XDocumentTypeWrapper:.ctor(System.Xml.Linq.XDocumentType):this (FullOpts)
          14 (73.68% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.Converters.XmlDeclarationWrapper:.ctor(System.Xml.XmlDeclaration):this (FullOpts)
          14 (73.68% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.Converters.XmlDocumentTypeWrapper:.ctor(System.Xml.XmlDocumentType):this (FullOpts)
          14 (73.68% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.Converters.XmlDocumentWrapper:.ctor(System.Xml.XmlDocument):this (FullOpts)
          14 (73.68% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.Converters.XmlElementWrapper:.ctor(System.Xml.XmlElement):this (FullOpts)
          14 (73.68% of base) : System.IO.Pipelines.dasm - System.IO.Pipelines.BufferSegment:set_NextSegment(System.IO.Pipelines.BufferSegment):this (FullOpts)
          14 (73.68% of base) : System.Reflection.MetadataLoadContext.dasm - System.Reflection.TypeLoading.Ecma.EcmaPinnedTypeProvider:.ctor(System.Reflection.TypeLoading.Ecma.EcmaModule):this (FullOpts)
          14 (73.68% of base) : System.Reflection.MetadataLoadContext.dasm - System.Reflection.TypeLoading.Ecma.EcmaWrappedTypeProvider:.ctor(System.Reflection.TypeLoading.Ecma.EcmaModule):this (FullOpts)
          14 (73.68% of base) : System.Speech.dasm - System.Speech.Internal.SrgsCompiler.State:Add(System.Speech.Internal.SrgsCompiler.State):System.Speech.Internal.SrgsCompiler.State:this (FullOpts)
          14 (73.68% of base) : System.Threading.Tasks.Parallel.dasm - System.Threading.Tasks.ParallelLoopState`1[byte]:.ctor(System.Threading.Tasks.ParallelLoopStateFlags`1[byte]):this (FullOpts)
          14 (73.68% of base) : System.Threading.Tasks.Parallel.dasm - System.Threading.Tasks.ParallelLoopState`1[int]:.ctor(System.Threading.Tasks.ParallelLoopStateFlags`1[int]):this (FullOpts)
          14 (73.68% of base) : System.Threading.Tasks.Parallel.dasm - System.Threading.Tasks.ParallelLoopState`1[long]:.ctor(System.Threading.Tasks.ParallelLoopStateFlags`1[long]):this (FullOpts)
          14 (73.68% of base) : System.Threading.Tasks.Parallel.dasm - System.Threading.Tasks.ParallelLoopState`1[short]:.ctor(System.Threading.Tasks.ParallelLoopStateFlags`1[short]):this (FullOpts)
          14 (73.68% of base) : System.Private.Xml.dasm - System.Xml.ValidatingReaderNodeData:SetItemData(System.String):this (FullOpts)

Top method improvements (percentages):
          -4 (-40.00% of base) : ILCompiler.Reflection.ReadyToRun.dasm - ILCompiler.Reflection.ReadyToRun.NativeVarInfo:set_Variable(ILCompiler.Reflection.ReadyToRun.Variable):this (FullOpts)
          -4 (-40.00% of base) : ILCompiler.Reflection.ReadyToRun.dasm - ILCompiler.Reflection.ReadyToRun.ReadyToRunImportSection:set_Entries(System.Collections.Generic.List`1[ILCompiler.Reflection.ReadyToRun.ReadyToRunImportSection+ImportSectionEntry]):this (FullOpts)
          -4 (-40.00% of base) : ILCompiler.Reflection.ReadyToRun.dasm - ILCompiler.Reflection.ReadyToRun.ReadyToRunReader+DecodedMethodSignature:set_OwningType(System.String):this (FullOpts)
          -4 (-40.00% of base) : ILCompiler.Reflection.ReadyToRun.dasm - ILCompiler.Reflection.ReadyToRun.WebcilImageReader+WasmFunctionInfo:set_Locals(System.Collections.Generic.IReadOnlyList`1[System.ValueTuple`2[uint,byte]]):this (FullOpts)
          -4 (-40.00% of base) : ILCompiler.Reflection.ReadyToRun.dasm - ILCompiler.Reflection.ReadyToRun.x86.InfoHdrSmall:set_Epilogs(System.Collections.Generic.List`1[int]):this (FullOpts)
          -4 (-40.00% of base) : System.Private.CoreLib.dasm - Internal.Runtime.InteropServices.ComActivator+BasicClassFactory+ValidatedInterfaceType:set_ManagedType(System.Type):this (FullOpts)
          -4 (-40.00% of base) : Microsoft.CodeAnalysis.dasm - Microsoft.CodeAnalysis.AttributeUsageInfo+ValidTargetsStringLocalizableErrorArgument:.ctor(System.String[]):this (FullOpts)
          -4 (-40.00% of base) : Microsoft.CodeAnalysis.dasm - Microsoft.CodeAnalysis.CodeAnalysisResourcesLocalizableErrorArgument:.ctor(System.String):this (FullOpts)
          -4 (-40.00% of base) : Microsoft.CodeAnalysis.dasm - Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder+KeyCollection[byte,System.Nullable`1[int]]:.ctor(Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder[byte,System.Nullable`1[int]]):this (FullOpts)
          -4 (-40.00% of base) : Microsoft.CodeAnalysis.dasm - Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder+KeyCollection[double,System.Nullable`1[int]]:.ctor(Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder[double,System.Nullable`1[int]]):this (FullOpts)
          -4 (-40.00% of base) : Microsoft.CodeAnalysis.dasm - Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder+KeyCollection[int,System.Nullable`1[int]]:.ctor(Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder[int,System.Nullable`1[int]]):this (FullOpts)
          -4 (-40.00% of base) : Microsoft.CodeAnalysis.dasm - Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder+KeyCollection[long,System.Nullable`1[int]]:.ctor(Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder[long,System.Nullable`1[int]]):this (FullOpts)
          -4 (-40.00% of base) : Microsoft.CodeAnalysis.dasm - Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder+KeyCollection[short,System.Nullable`1[int]]:.ctor(Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder[short,System.Nullable`1[int]]):this (FullOpts)
          -4 (-40.00% of base) : Microsoft.CodeAnalysis.dasm - Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder+KeyCollection[System.Nullable`1[int],System.Nullable`1[int]]:.ctor(Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder[System.Nullable`1[int],System.Nullable`1[int]]):this (FullOpts)
          -4 (-40.00% of base) : Microsoft.CodeAnalysis.dasm - Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder+KeyCollection[System.Numerics.Vector`1[float],System.Nullable`1[int]]:.ctor(Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder[System.Numerics.Vector`1[float],System.Nullable`1[int]]):this (FullOpts)
          -4 (-40.00% of base) : Microsoft.CodeAnalysis.dasm - Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder+TestAccessor[byte,System.Nullable`1[int]]:.ctor(Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder[byte,System.Nullable`1[int]]):this (FullOpts)
          -4 (-40.00% of base) : Microsoft.CodeAnalysis.dasm - Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder+TestAccessor[double,System.Nullable`1[int]]:.ctor(Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder[double,System.Nullable`1[int]]):this (FullOpts)
          -4 (-40.00% of base) : Microsoft.CodeAnalysis.dasm - Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder+TestAccessor[int,System.Nullable`1[int]]:.ctor(Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder[int,System.Nullable`1[int]]):this (FullOpts)
          -4 (-40.00% of base) : Microsoft.CodeAnalysis.dasm - Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder+TestAccessor[long,System.Nullable`1[int]]:.ctor(Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder[long,System.Nullable`1[int]]):this (FullOpts)
          -4 (-40.00% of base) : Microsoft.CodeAnalysis.dasm - Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder+TestAccessor[short,System.Nullable`1[int]]:.ctor(Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder[short,System.Nullable`1[int]]):this (FullOpts)
          -4 (-40.00% of base) : Microsoft.CodeAnalysis.dasm - Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder+TestAccessor[System.Nullable`1[int],System.Nullable`1[int]]:.ctor(Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder[System.Nullable`1[int],System.Nullable`1[int]]):this (FullOpts)
          -4 (-40.00% of base) : Microsoft.CodeAnalysis.dasm - Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder+TestAccessor[System.Numerics.Vector`1[float],System.Nullable`1[int]]:.ctor(Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder[System.Numerics.Vector`1[float],System.Nullable`1[int]]):this (FullOpts)
          -4 (-40.00% of base) : Microsoft.CodeAnalysis.dasm - Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder+ValueCollection[byte,System.Nullable`1[int]]:.ctor(Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder[byte,System.Nullable`1[int]]):this (FullOpts)
          -4 (-40.00% of base) : Microsoft.CodeAnalysis.dasm - Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder+ValueCollection[double,System.Nullable`1[int]]:.ctor(Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder[double,System.Nullable`1[int]]):this (FullOpts)
          -4 (-40.00% of base) : Microsoft.CodeAnalysis.dasm - Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder+ValueCollection[int,System.Nullable`1[int]]:.ctor(Microsoft.CodeAnalysis.Collections.ImmutableSegmentedDictionary`2+Builder[int,System.Nullable`1[int]]):this (FullOpts)

106592 total methods with Code Size differences (10237 improved, 96355 regressed), 374498 unchanged.

--------------------------------------------------------------------------------

cc @jkotas @dotnet/jit-contrib I am not sure what that is telling us for x64/arm64 whether we want to do that for those

@EgorBo EgorBo closed this May 30, 2026
@EgorBo
Copy link
Copy Markdown
Member Author

EgorBo commented May 30, 2026

Example codegen regression

Newtonsoft.Json.Converters.XDocumentTypeWrapper:.ctor(System.Xml.Linq.XDocumentType):this

 ; Assembly listing for method Newtonsoft.Json.Converters.XDocumentTypeWrapper:.ctor(System.Xml.Linq.XDocumentType):this (FullOpts)
 ; Emitting BLENDED_CODE for x86 + VEX + EVEX on Windows
 ; FullOpts code
 ; optimized code
 ; esp based frame
 ; partially interruptible
 ; No PGO data
 ; 0 inlinees with PGO data; 2 single block inlinees; 0 inlinees without PGO data
 ; Final local variable assignments
 ;
-;  V00 this         [V00,T00] (  4,  4   )     ref  ->  ecx         this class-hnd single-def <Newtonsoft.Json.Converters.XDocumentTypeWrapper>
-;  V01 arg1         [V01,T01] (  4,  4   )     ref  ->  eax         class-hnd single-def <System.Xml.Linq.XDocumentType>
+;  V00 this         [V00,T00] (  4,  4   )     ref  ->  [esp+0x00]  this class-hnd single-def <Newtonsoft.Json.Converters.XDocumentTypeWrapper>
+;  V01 arg1         [V01,T01] (  4,  4   )     ref  ->  esi         class-hnd single-def <System.Xml.Linq.XDocumentType>
 ;
-; Lcl frame size = 0
+; Lcl frame size = 4

 G_M23744_IG01:
-       mov      eax, edx
-                                                ;; size=2 bbWeight=1 PerfScore 0.25
+       push     esi
+       push     eax
+       mov      esi, edx
+                                                ;; size=4 bbWeight=1 PerfScore 2.25
 G_M23744_IG02:
-       lea      edx, bword ptr [ecx+0x04]
-       call     CORINFO_HELP_ASSIGN_REF_EAX
-       lea      edx, bword ptr [ecx+0x08]
-       call     CORINFO_HELP_ASSIGN_REF_EAX
-                                                ;; size=16 bbWeight=1 PerfScore 3.00
+       mov      gword ptr [esp], ecx
+       lea      ecx, bword ptr [ecx+0x04]
+       mov      edx, esi
+       call     CORINFO_HELP_ASSIGN_REF
+       mov      ecx, gword ptr [esp]
+       lea      ecx, bword ptr [ecx+0x08]
+       mov      edx, esi
+       call     CORINFO_HELP_ASSIGN_REF
+                                                ;; size=26 bbWeight=1 PerfScore 5.50
 G_M23744_IG03:
+       pop      ecx
+       pop      esi
        ret
-                                                ;; size=1 bbWeight=1 PerfScore 1.00
+                                                ;; size=3 bbWeight=1 PerfScore 2.00

-; Total bytes of code 19, prolog size 0, PerfScore 4.25, instruction count 6, allocated bytes for code 19 (MethodHash=0332a33f) for method Newtonsoft.Json.Converters.XDocumentTypeWrapper:.ctor(System.Xml.Linq.XDocumentType):this (FullOpts)
+; Total bytes of code 33, prolog size 2, PerfScore 9.75, instruction count 14, allocated bytes for code 35 (MethodHash=0332a33f) for method Newtonsoft.Json.Converters.XDocumentTypeWrapper:.ctor(System.Xml.Linq.XDocumentType):this (FullOpts)

Summary

base (main) diff (PR) delta
Code size (bytes) 19 33 +14 (+73.7%)
Instruction count 6 14 +8
PerfScore 4.25 9.75 +5.50

Why it regressed

  • Baseline (per-reg WB): this stays in ECX (callee-saved across ASSIGN_REF_EAX); arg1 moves to EAX once; two short call CORINFO_HELP_ASSIGN_REF_EAX.
  • Diff (unified WB): this can't stay in ECX because ECX is now the dst arg, and arg1 can't stay in EAX because EAX is now in the WB kill set. LSRA spills this to the stack ([esp+0x00]) and parks arg1 in callee-saved ESI. Each store needs to reload this from stack into ECX, LEA the field address, copy ESI→EDX, and call — plus prolog push esi/eax and epilog pop ecx/esi.

@EgorBo
Copy link
Copy Markdown
Member Author

EgorBo commented May 30, 2026

I wonder if it's mostly just the typical this (ECX) is the issue so we can change the write barrier to never touch ECX despite it being caller-saved. Same idea for x64

@EgorBo
Copy link
Copy Markdown
Member Author

EgorBo commented May 30, 2026

I've modified the JIT to assume that whatever CC this register is not changed (and is not used to pass parameters) and got -1,339,638 diff on win-x64 (optimized code only)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants