diff --git a/docs/core/whats-new/dotnet-11/runtime.md b/docs/core/whats-new/dotnet-11/runtime.md index b920d1443134f..262b2cbbded2a 100644 --- a/docs/core/whats-new/dotnet-11/runtime.md +++ b/docs/core/whats-new/dotnet-11/runtime.md @@ -2,7 +2,7 @@ title: What's new in .NET 11 runtime description: Learn about the new features introduced in the .NET 11 runtime. titleSuffix: "" -ms.date: 08/12/2026 +ms.date: 08/15/2026 ai-usage: ai-assisted ms.update-cycle: 3650-days --- @@ -152,7 +152,6 @@ The runtime now detects when a continuation has nothing to restore and skips the - **Redundant checked context removal:** The JIT can now prove and remove redundant checked arithmetic contexts—for example, when a value is already known to be in range. This optimization eliminates unnecessary overflow checks in generated code. - **Switch expression folding:** Multi-target `switch` expressions now fold into simpler branchless checks when the targets are a small set of constants, for example `x is 0 or 1 or 2 or 3 or 4`. - **Faster uint-to-float/double casts:** Casting `uint` to `float` or `double` is faster on pre-AVX-512 x86 hardware. -- **Devirtualization in ReadyToRun images:** ReadyToRun (R2R) images can now devirtualize non-shared generic virtual method calls, improving performance of ahead-of-time compiled code for generic scenarios. - **SVE2 intrinsics:** New Arm SVE2 (Scalable Vector Extension 2) intrinsics are available: `ShiftRightLogicalNarrowingSaturate(Even|Odd)`. These expand the set of vectorized operations available on Arm hardware that supports SVE2. - **`Math.BigMul` on x64:** `Math.BigMul(long, long, out long)` is now significantly faster on x64. The JIT generates a single `MUL r/m64` instruction when both operands are 64-bit values and the caller requests the high half of the result, eliminating the previous helper call. - **Single-IG prolog restriction removed:** The JIT no longer requires the function prolog to fit in a single instruction group (IG). Complex prologues with many saved registers, large stack allocations, or runtime-async state setup no longer trigger fallback paths. @@ -173,6 +172,41 @@ static bool IsAdmin(string role) => role == "Admin"; The optimization applies to string literals, `const string` fields, and UTF-8 literals (for example, `"PNG"u8`). +### Devirtualization improvements + +The JIT can now devirtualize generic virtual methods, including non-shared generic virtual methods and shared generic virtual methods that don't require a runtime lookup. Default interface methods declared on generic interfaces can now be devirtualized as well. + +In addition, the result of `Activator.CreateInstance()` is now treated as having an exact type, allowing the JIT to devirtualize subsequent calls made on the returned object. + +These improvements unlock further downstream optimizations such as inlining and constant folding, reducing runtime dispatch overhead and improving performance. For example: + +```csharp +IfaceGeneric caller = new Caller(); +Iface obj = Activator.CreateInstance(); +caller.DefaultImpl(obj, "test"); // Can be optimized down to Console.WriteLine("test") + +interface Iface +{ + void GenericMethod(T value); +} + +interface IfaceGeneric +{ + void DefaultImpl(Iface obj, T value) => obj.GenericMethod(value); +} + +class Impl : Iface +{ + public void GenericMethod(T value) => Console.WriteLine(value); +} + +class Caller : IfaceGeneric +{ +} +``` + +Here the JIT can determine the exact implementation behind both interface calls, devirtualize them, inline through the call chain, and ultimately fold the code down to the equivalent of `Console.WriteLine("test")`. + ### Eliminating bounds checks after an empty-span guard The JIT now picks up the `length != 0` assertion from an empty-span check and uses it to prove the bounds check on the first element succeeds: