Skip to content

Conversation

@jakobbotsch
Copy link
Member

@jakobbotsch jakobbotsch commented Nov 27, 2025

  • Introduce ExceptionSetFlags::UnknownException. This special value marks that an operation may throw an exception that we cannot say anything about. Consumers of flags must be specially aware about this exception type.
  • Make GenTree::OperExceptions callable on all trees by changing arbitrary user calls to return the new UnknownException type.
  • Switch OperMayThrow to be literally OperExceptions() != None
  • Enhance precision about exceptions thrown by helper calls. Instead of just modelling "may throw" and "may not throw", we model the exact exceptions a helper may throw via ExceptionSetFlags.
  • Add helper call properties for CORINFO_HELP_VIRTUAL_FUNC_PTR, CORINFO_HELP_GVMLOOKUP_FOR_SLOT, CORINFO_HELP_READYTORUN_VIRTUAL_FUNC_PTR. These do not mutate the heap and we use the new mechanism above to indicate that the only exception they can throw is NRE. They are also pure, so add a VN function so VN can model them. This allows us to CSE and hoist these helpers.
  • Update SideEffectSet to track and precisely handle interference for exceptions. We can now reorder two operations if we know they can only throw the exact same exception.
  • Generalize OptimizeCallIndirectTargetEvaluation; manual interference handling is no longer necessary, and we can use it for all calls.

Fix #86173

Example:

public static void Test(Base b)
{
    b.Foo<string>();
    b.Foo<int>();
    b.Foo<string>();
    b.Foo<int>();

    for (int i = 0; i < 10; i++)
        b.Foo<double>();
}

public abstract class Base
{
    public abstract void Foo<T>();
}

public class Derived : Base
{
    public override void Foo<T>()
    {
    }
}

Before:

G_M7145_IG02:
       mov      rcx, rbx
       mov      rsi, 0xD1FFAB1E      ; Program+Base
       mov      rdx, rsi
       mov      r8, 0xD1FFAB1E      ; token handle
       call     CORINFO_HELP_VIRTUAL_FUNC_PTR
       mov      rcx, rbx
       call     rax
       mov      rcx, rbx
       mov      rdx, rsi
       mov      r8, 0xD1FFAB1E      ; token handle
       call     CORINFO_HELP_VIRTUAL_FUNC_PTR
       mov      rcx, rbx
       call     rax
       mov      rcx, rbx
       mov      rdx, rsi
       mov      r8, 0xD1FFAB1E      ; token handle
       call     CORINFO_HELP_VIRTUAL_FUNC_PTR
       mov      rcx, rbx
       call     rax
       mov      rcx, rbx
       mov      rdx, rsi
       mov      r8, 0xD1FFAB1E      ; token handle
       call     CORINFO_HELP_VIRTUAL_FUNC_PTR
       mov      rcx, rbx
       call     rax
       mov      edi, 10
						;; size=119 bbWeight=1 PerfScore 20.50
G_M7145_IG03:
       mov      rcx, rbx
       mov      rdx, rsi
       mov      r8, 0xD1FFAB1E      ; token handle
       call     CORINFO_HELP_VIRTUAL_FUNC_PTR
       mov      rcx, rbx
       call     rax
       dec      edi
       jne      SHORT G_M7145_IG03
						;; size=30 bbWeight=8 PerfScore 50.00

After:

G_M7145_IG02:
       mov      rcx, rbx
       mov      rsi, 0xD1FFAB1E      ; Program+Base
       mov      rdx, rsi
       mov      r8, 0xD1FFAB1E      ; token handle
       call     CORINFO_HELP_VIRTUAL_FUNC_PTR
       mov      rcx, rbx
       mov      rdi, rax
       call     rdi
       mov      rcx, rbx
       mov      rdx, rsi
       mov      r8, 0xD1FFAB1E      ; token handle
       call     CORINFO_HELP_VIRTUAL_FUNC_PTR
       mov      rcx, rbx
       mov      rbp, rax
       call     rbp
       mov      rcx, rbx
       call     rdi
       mov      rcx, rbx
       call     rbp
       mov      rcx, rbx
       mov      rdx, rsi
       mov      r8, 0xD1FFAB1E      ; token handle
       call     CORINFO_HELP_VIRTUAL_FUNC_PTR
       mov      rsi, rax
       mov      edi, 10
						;; size=107 bbWeight=1 PerfScore 19.50
G_M7145_IG03:
       mov      rcx, rbx
       call     rsi
       dec      edi
       jne      SHORT G_M7145_IG03
						;; size=9 bbWeight=8 PerfScore 36.00

- Introduce `ExceptionSetFlags::UnknownException`. This special value
  marks that an operation may throw an exception that we cannot say
  anything about. Consumers of flags must be specially aware about this
  exception type.
- Make `GenTree::OperExceptions` callable on all trees by changing
  arbitrary user calls to return the new `UnknownException` type.
- Switch `OperMayThrow` to be literally `OperExceptions() != None`
- Enhance precision about exceptions thrown by helper calls. Instead of
  just modelling "may throw" and "may not throw", we model the exact
  exceptions a helper may throw via `ExceptionSetFlags`.
- Add helper call properties for `CORINFO_HELP_VIRTUAL_FUNC_PTR`,
  `CORINFO_HELP_GVMLOOKUP_FOR_SLOT`,
  `CORINFO_HELP_READYTORUN_VIRTUAL_FUNC_PTR`. These do not mutate the
  heap and we use the new mechanism above to indicate that the only
  exception they can throw is NRE. They are also pure, so add a VN
  function so VN can model them.
- Update `SideEffectSet` to track and precisely handle interference for
  exceptions. We can now reorder two operations if we know they can only
  throw the exact same exception.
- Generalize `OptimizeCallIndirectTargetEvaluation`; manual interference
  handling is no longer necessary, and we can use it for all calls.
@jakobbotsch jakobbotsch changed the title JIT: Improve interference checks for thrown exceptions JIT: Improve interference checks for thrown exceptions and model GVM resolution helpers precisely Nov 28, 2025
@jakobbotsch jakobbotsch changed the title JIT: Improve interference checks for thrown exceptions and model GVM resolution helpers precisely JIT: Improve interference checks for thrown exceptions and enable CSE/hoisting of GVM resolution helpers Nov 28, 2025
@jakobbotsch jakobbotsch marked this pull request as ready for review November 28, 2025 21:21
Copilot AI review requested due to automatic review settings November 28, 2025 21:21
@jakobbotsch
Copy link
Member Author

cc @dotnet/jit-contrib PTAL @AndyAyersMS @EgorBo

Diffs. On xarch this allows more containment of indirections, and on all platforms it allows us to CSE/hoist GVM resolution helpers (most of the diffs of that comes from tests).

Copilot finished reviewing on behalf of jakobbotsch November 28, 2025 21:27
Copy link
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 improves the JIT's ability to optimize GVM (Generic Virtual Method) resolution helpers by introducing more precise exception tracking and enabling CSE/hoisting optimizations.

Key changes:

  • Introduces ExceptionSetFlags::UnknownException to model operations with unknown exception types
  • Extends exception modeling to all helper calls with precise exception sets instead of just "may throw" vs "may not throw"
  • Marks CORINFO_HELP_VIRTUAL_FUNC_PTR, CORINFO_HELP_GVMLOOKUP_FOR_SLOT, and CORINFO_HELP_READYTORUN_VIRTUAL_FUNC_PTR as pure with only NRE exceptions, enabling CSE/hoisting

Reviewed changes

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

Show a summary per file
File Description
src/coreclr/jit/valuenumfuncs.h Adds VN function definitions for the three GVM resolution helpers
src/coreclr/jit/valuenum.cpp Implements VN function handling and precise NRE exception modeling for GVM helpers; refactors fgValueNumberIndirNullCheckExceptions for reuse
src/coreclr/jit/utils.h Moves ExceptionSetFlags enum and adds UnknownException value; updates HelperCallProperties to track precise exceptions
src/coreclr/jit/utils.cpp Updates helper property initialization to use ExceptionSetFlags; marks GVM helpers as pure with NRE only
src/coreclr/jit/sideeffects.h Adds m_preciseExceptions field to track precise exception sets
src/coreclr/jit/sideeffects.cpp Implements precise exception interference checking - allows reordering when both sides throw the exact same single exception
src/coreclr/jit/morph.cpp Swaps argument order to match new helper signature; updates exception interference checks for unknown exceptions
src/coreclr/jit/lower.cpp Generalizes OptimizeCallIndirectTargetEvaluation to work with any call target; removes manual NRE handling now handled by SideEffectSet
src/coreclr/jit/gentree.h Updates OperEffects signature to output precise exceptions; removes duplicate ExceptionSetFlags definition
src/coreclr/jit/gentree.cpp Makes OperExceptions callable on all nodes including calls; simplifies OperMayThrow to use OperExceptions; updates OperEffects to populate precise exceptions
src/coreclr/jit/forwardsub.cpp Updates exception accumulation to handle UnknownException and stop tracking once multiple/unknown exceptions are seen
src/coreclr/jit/compiler.h Adds declaration for fgValueNumberIndirNullCheckExceptions

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.

JIT: Allow containment to reorder the same single exceptions

1 participant