Skip to content

JIT: relax inliner heuristics for callees on [Intrinsic] types#127433

Merged
EgorBo merged 2 commits intodotnet:mainfrom
EgorBo:jit/inline-intrinsic-types
Apr 26, 2026
Merged

JIT: relax inliner heuristics for callees on [Intrinsic] types#127433
EgorBo merged 2 commits intodotnet:mainfrom
EgorBo:jit/inline-intrinsic-types

Conversation

@EgorBo
Copy link
Copy Markdown
Member

@EgorBo EgorBo commented Apr 26, 2026

As part of the "remove unsafe" work, we're introducing lots of new Span.Slice, Vector.Create, etc calls. It turns out these negatively impact on the inliner time budget and lead to bad regressions (I've hit it in #127429).

{B127EBE7-9CB9-4219-BD0D-9B4FE009E9CC}

I suggest we exclude these from the budget check just like we already do for small methods.

Just a few hits with PMI jit-diffs.

Copilot AI review requested due to automatic review settings April 26, 2026 12:26
@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 Apr 26, 2026
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

Adjusts CoreCLR JIT inlining heuristics to avoid penalizing callees whose declaring types are marked [Intrinsic], improving codegen quality for common intrinsic-heavy APIs (e.g., Span<T>, SIMD vectors, HW intrinsics) even in cold blocks or when the inline budget is exhausted.

Changes:

  • Introduces a new callee observation (CALLEE_IS_INTRINSIC_TYPE) based on CORINFO_FLG_INTRINSIC_TYPE.
  • Relaxes multiplier “caps”/penalties for intrinsic-type callees in DefaultPolicy and ExtendedDefaultPolicy (rare callsites, profile-based penalties, no-return region cap).
  • Allows over-budget inlining for intrinsic-type callees in DefaultPolicy.

Reviewed changes

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

File Description
src/coreclr/jit/inlinepolicy.h Tracks intrinsic-type state (m_IsIntrinsicType) in the default policy data.
src/coreclr/jit/inlinepolicy.cpp Applies intrinsic-type-aware budget and multiplier adjustments; emits the new state in XML dumps.
src/coreclr/jit/inline.def Adds the IS_INTRINSIC_TYPE observation for the callee scope.
src/coreclr/jit/fgbasic.cpp Records CALLEE_IS_INTRINSIC_TYPE from info.compClassAttr during inline candidate IL scanning.

Comment thread src/coreclr/jit/inlinepolicy.cpp Outdated
@EgorBo
Copy link
Copy Markdown
Member Author

EgorBo commented Apr 26, 2026

@MihuBot

Copilot AI review requested due to automatic review settings April 26, 2026 13:14
@EgorBo EgorBo force-pushed the jit/inline-intrinsic-types branch from 86187ff to 8a5f924 Compare April 26, 2026 13:14
@EgorBo EgorBo marked this pull request as ready for review April 26, 2026 13:16
@EgorBo
Copy link
Copy Markdown
Member Author

EgorBo commented Apr 26, 2026

@MihuBot

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 6 out of 6 changed files in this pull request and generated no new comments.

@EgorBo
Copy link
Copy Markdown
Member Author

EgorBo commented Apr 26, 2026

PTAL @AndyAyersMS
I need this to avoid perf issues from "reduce-unsafe" work like I've hit in #127429

For real world code this will only impact Span and ReadOnlySpan functions which are all quite small.

@EgorBo EgorBo requested a review from AndyAyersMS April 26, 2026 13:43
Copy link
Copy Markdown
Member

@AndyAyersMS AndyAyersMS left a comment

Choose a reason for hiding this comment

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

Curious how you chose 50 as a limit... do we ever get anywhere close to that?

@EgorBo
Copy link
Copy Markdown
Member Author

EgorBo commented Apr 26, 2026

Curious how you chose 50 as a limit... do we ever get anywhere close to that?

@AndyAyersMS good question, so basically just this code snippet:

static void Foo(ReadOnlySpan<int> src, Span<int> dst)
{
    Vector.Create(src.Slice(0 * Vector<int>.Count)).CopyTo(dst.Slice(0 * Vector<int>.Count));
    Vector.Create(src.Slice(1 * Vector<int>.Count)).CopyTo(dst.Slice(1 * Vector<int>.Count));
    Vector.Create(src.Slice(2 * Vector<int>.Count)).CopyTo(dst.Slice(2 * Vector<int>.Count));
    Vector.Create(src.Slice(3 * Vector<int>.Count)).CopyTo(dst.Slice(3 * Vector<int>.Count));
    Vector.Create(src.Slice(4 * Vector<int>.Count)).CopyTo(dst.Slice(4 * Vector<int>.Count));
    Vector.Create(src.Slice(5 * Vector<int>.Count)).CopyTo(dst.Slice(5 * Vector<int>.Count));
    Vector.Create(src.Slice(6 * Vector<int>.Count)).CopyTo(dst.Slice(6 * Vector<int>.Count));
    Vector.Create(src.Slice(7 * Vector<int>.Count)).CopyTo(dst.Slice(7 * Vector<int>.Count));
}

is already 32 call that we inline normally (they're not JIT intrinsics, but Vector and Span are marked as [Intrinsic]): Vector.Create(Span), Span.Slice(int), Vector.CopyTo, Span.Slice - so all of them are candidates for "ran out of time budget".

For reference, the unsafe variant for this:

Vector.LoadUnsafe(ref Unsafe.Add(ref src, 0)).StoreUnsafe(ref Unsafe.Add(ref dst, 0));
...

is 0 calls since all of these are JIT intrinsics.

Also, I see a lot of Span..ctor and Span.op_Implicit

I decided to add the limit because in the past we had terrible experience with inliner in huge methods without guardrails.

@EgorBo EgorBo merged commit 531240d into dotnet:main Apr 26, 2026
134 of 136 checks passed
@EgorBo EgorBo deleted the jit/inline-intrinsic-types branch April 26, 2026 17:02
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