Skip to content

Suboptimal codegen for is pattern compared to .NET 6/7 #80052

Description

@rameel

A simple method with a pattern match is generates less efficient code starting from .NET 8 compared to .NET 6/7.

Repro: godbolt

public static bool HasLeadingSlash(ReadOnlySpan<char> path)
{
    if (path.Length > 0 && path[0] is '/' or '\\')
        return true;

    return false;
}

.NET 8,9,10 (IL size: 49 bytes):

public static bool HasLeadingSlash(ReadOnlySpan<char> path)
{
    bool flag = path.Length > 0;
    if (flag)
    {
        char c = path[0];
        bool flag2 = ((c == '/' || c == '\\') ? true : false);
        flag = flag2;
    }

    if (flag)
    {
        return true;
    }

    return false;
}

.NET 6,7 (IL size: 34 bytes):

public static bool HasLeadingSlash(ReadOnlySpan<char> path)
{
    if (path.Length > 0)
    {
        char c = path[0];
        if (c == '/' || c == '\\')
        {
            return true;
        }
    }
    return false;
}

Roslyn now introduces unnecessary temporary bool variables and extra conditional branches. The JIT is unable to untangle this pattern, resulting in larger, slower code.

Activity

  1. added theissue type on Aug 29, 2025
  2. added this to the Next Next milestone on Aug 29, 2025
  3. self-assigned this
    on Aug 29, 2025
  4. added
    help wantedThe issue is "up for grabs" - add a comment if you are interested in working on it
    on Nov 3, 2025
  5. stevenelliottjr commented on Feb 17, 2026

    @stevenelliottjr
    Contributor

    I'd like to take this one. I'll investigate the pattern lowering regression and submit a PR.

  6. RikkiGibson commented on Feb 17, 2026

    @RikkiGibson
    Member

    It would be good to bisect and identify when this codegen changed, so we can understand if there was some reason for that change to occur, and based on that, what options we have for improving things.

  7. stevenelliottjr commented on Feb 17, 2026

    @stevenelliottjr
    Contributor

    I did the bisect. Here's the full history:

    Root cause: PR #65874 (Jan 2023) — "Enable switch dispatch for is-expressions" (fixes #45679). This routed or patterns through the general DAG lowering, which wraps results in a BoundSpillSequence with a bool temp. Before this, short or patterns went through the linear path and didn't have this overhead.

    Previous fix attempts (both reverted):

    1. PR Avoid creating a result temp for is-expressions #68694 (Aug 2023) — "Avoid creating a result temp for is-expressions" — Reverted in Revert "Avoid creating a result temp for is-expressions" #69582 due to test failures.

    2. PR Avoid creating result temp for is-expressions (follow-up) #72273 (Mar 2024) — Follow-up attempt, fixing issues from Avoid creating a result temp for is-expressions #68694 — Reverted in Revert "Avoid creating result temp for is-expressions (#72273)" #72827 due to a compiler crash (NullReferenceException in ILBuilder.BlockedBranchDestinationSlow) when the pattern appeared inside await using blocks ([dotnet-sdk-9.0.100-preview.3.24175.24] Microsoft.CSharp.Core.targets thrown errors when using "dotnet build" #72753). The crash was in exception handler scope handling.

    Both previous attempts modified the general DAG lowering to avoid the SpillSequence/result temp. That approach was fragile because the general path interacts with exception handler scopes (try/finally, await using), which led to the IL builder crash.

    My approach is different: Instead of modifying the general DAG lowering, I route short or patterns (≤4 tests) to the existing, well-tested linear rewriter with swapped labels. For is '/' or '\\', the inverted linear sequence produces !(c != '/' && c != '\\') — a clean boolean expression with no SpillSequence or bool temp. Large or patterns (5+ tests) still go through the general path with switch dispatch.

    This avoids the previous crashes because the linear rewriter doesn't use SpillSequence and doesn't interact with exception handler scopes.

    Related issues: #59615, #55334 (same underlying problem)

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

Metadata

Metadata

Assignees

Labels

Area-CompilersCode Gen QualityRoom for improvement in the quality of the compiler's generated codeOngoing-Quality-Candidatehelp wantedThe issue is "up for grabs" - add a comment if you are interested in working on it

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions