Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

arm64: Assertion failed 'loop->ContainsBlock(pred)' during 'Do value numbering' #110959

Closed
kunalspathak opened this issue Dec 26, 2024 · 16 comments · Fixed by #111364
Closed

arm64: Assertion failed 'loop->ContainsBlock(pred)' during 'Do value numbering' #110959

kunalspathak opened this issue Dec 26, 2024 · 16 comments · Fixed by #111364
Assignees
Labels
arch-arm64 area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI in-pr There is an active PR which will close this issue when it is merged
Milestone

Comments

@kunalspathak
Copy link
Member

kunalspathak commented Dec 26, 2024

// Found by Antigen
// Reduced from 14.87 KB to 3.01 KB.


using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86;
using System.Numerics;
public class TestClass
{
    public struct S1
    {
        public float float_0;
    }
    static bool s_bool_2 = false;
    static int s_int_8 = -2;
    static uint s_uint_14 = 2;
    static S1 s_s1_17 = new S1();
    int int_25 = 0;
    float float_28 = 2.0238094f;
    S1 s1_34 = new S1();
    private static List<string> toPrint = new List<string>();
    [MethodImpl(MethodImplOptions.NoInlining)]
    public int LeafMethod6()
    {
        unchecked
        {
            return 15>>4;
        }
    }
    public float LeafMethod9()
    {
        unchecked
        {
            return s_s1_17.float_0 *= float_28 = s1_34.float_0;
        }
    }
    public S1 LeafMethod15()
    {
        unchecked
        {
            return s1_34;
        }
    }
    public void Method0()
    {
        unchecked
        {
            int int_61 = -2;
            uint uint_67 = 0;
            while (s_int_8 >> (int_25 |= int_25)+ (s_int_8 -= LeafMethod6())== int_61)
            {
                if (15*4> (uint_67 = 15&4& s_uint_14 - 15*4))
{}                else
                {
                    switch (s_int_8)
                    {
                        case -1:
                        {
                            do
                            {
                            }
                            while (15>4&& s_bool_2);
                            break;
                        }
                        case -5:
                        {
                            try
                            {
                                LeafMethod9();
                            }
                            catch (System.StackOverflowException)
                            {
                            }
                            break;
                        }
                        default:
                        {
                            try
                            {
                                LeafMethod15();
                            }
                            catch (System.TimeZoneNotFoundException)
                            {
                            }
                            catch (System.NotSupportedException)
                            {
                            }
                            catch (System.ArrayTypeMismatchException)
                            {
                            }
                            catch (System.InsufficientMemoryException)
                            {
                            }
                            break;
                        }
                    }
                }
            }
            return;
        }
    }
    public static void Main(string[] args)
    {
       Antigen();
    }
    public static int Antigen()
    {
        new TestClass().Method0();
        return string.Join(Environment.NewLine, toPrint).GetHashCode();
    }
}
/*
Environment:

set DOTNET_JitRLCSEGreedy=1
set DOTNET_JitFakeProcedureSplitting=1
set DOTNET_JitRandomGuardedDevirtualization=1
set DOTNET_JitRandomOnStackReplacement=10
set DOTNET_TC_OnStackReplacement=1
set DOTNET_TC_PartialCompilation=1
set DOTNET_TC_QuickJitForLoops=1
set DOTNET_TieredCompilation=0
set DOTNET_TieredPGO=1
set DOTNET_EnablePCLMULQDQ=1
set DOTNET_TailcallStress=0
set DOTNET_JitThrowOnAssertionFailure=1
set DOTNET_LegacyExceptionHandling=1

Debug: -402375315

Release: 0
JIT assert failed:
Assertion failed 'loop->ContainsBlock(pred)' in 'TestClass:Method0():this' during 'Do value numbering' (IL size 136; hash 0x46e9aa75; FullOpts)

    File: /Users/runner/work/1/s/src/coreclr/jit/fgdiagnostic.cpp Line: 4713


*/
@kunalspathak kunalspathak added arch-arm64 area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI labels Dec 26, 2024
@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Dec 26, 2024
Copy link
Contributor

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

@jakobbotsch
Copy link
Member

jakobbotsch commented Dec 26, 2024

Bisected to #110273, cc @AndyAyersMS

Presumably the new removal phases are not properly maintaining canonicalized loop shapes. Can the second run of these phases be changed to run before loop finding/canonicalization happens?

@AndyAyersMS
Copy link
Member

Probably so...

@AndyAyersMS AndyAyersMS self-assigned this Jan 6, 2025
@AndyAyersMS
Copy link
Member

So far I can't repro.

@AndyAyersMS
Copy link
Member

Still can't repro with the above... let me see if there are other instances to try.

@AndyAyersMS
Copy link
Member

Maybe requires linux arm64... let me try a cross jit.

@kunalspathak
Copy link
Member Author

Maybe requires linux arm64... let me try a cross jit.

Yes, you need AltJit=Method0 and AltJitName=clrjit_universal_arm64_x64.dll

@AndyAyersMS
Copy link
Member

No luck with that either.

@jakobbotsch
Copy link
Member

The example provided above has an empty Main function. It probably needs to be manually edited to call Antigen.

@kunalspathak
Copy link
Member Author

The example provided above has an empty Main function. It probably needs to be manually edited to call Antigen.

Ugh. will fix it in Antigen.

@AndyAyersMS
Copy link
Member

Yeah I have to do that fix frequently, but in the case above I've verified the method is jitted.

@JulieLeeMSFT JulieLeeMSFT removed the untriaged New issue has not been triaged by the area owner label Jan 8, 2025
@JulieLeeMSFT JulieLeeMSFT added this to the 10.0.0 milestone Jan 8, 2025
@kunalspathak
Copy link
Member Author

Yeah I have to do that fix frequently, but in the case above I've verified the method is jitted.

Fixed - kunalspathak/Antigen@acc331f

@kunalspathak
Copy link
Member Author

I cannot repro it either which makes me think that it got fixed along with other fixes that went in from last few days. I also saw latest Antigen run that you triggered and don't see this repro. Closing for now.

@jakobbotsch
Copy link
Member

Here's one from Fuzzlyn that repros it on main:

// Generated by Fuzzlyn v2.4 on 2024-12-29 17:20:48
// Run on Arm64 MacOS
// Seed: 561497581619134845-vectort,vector64,vector128,armadvsimd,armadvsimdarm64,armaes,armarmbase,armarmbasearm64,armcrc32,armcrc32arm64,armdp,armrdm,armrdmarm64,armsha1,armsha256
// Reduced from 163.4 KiB to 0.9 KiB in 00:00:40
// Hits JIT assert in Release:
// Assertion failed 'loop->ContainsBlock(pred)' in 'Program:M0()' during 'Do value numbering' (IL size 92; hash 0xaf50ff37; FullOpts)
// 
//     File: /Users/runner/work/1/s/src/coreclr/jit/fgdiagnostic.cpp Line: 4713
// 
using System;
using System.Numerics;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.Arm;

public struct S0
{
    public Vector64<uint> F7;
}

public struct S1
{
    public S0 F3;
}

public class Program
{
    public static double s_1;
    public static void Main()
    {
        M0();
    }

    public static void M0()
    {
        S1 var5 = default(S1);
        Vector128<long>[][] var6 = default(Vector128<long>[][]);
        for (int var2 = 0; var2 < 0; var2++)
        {
            try
            {
                if ((0 >= s_1))
                {
                }
                else
                {
                    System.Console.WriteLine(var6[0][0]);
                    return;
                }
            }
            finally
            {
                var5.F3.F7 = var5.F3.F7;
            }
        }
    }
}

Repros on win-x64 as well.

@jakobbotsch jakobbotsch reopened this Jan 9, 2025
@AndyAyersMS
Copy link
Member

The original loop formation:

---------------------------------------------------------------------------------------------------------------------------------------------------------------------
BBnum BBid ref try hnd preds           weight   [IL range]   [jump]                            [EH region]        [flags]
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
BB01 [0000]  1                             1    [000..00E)-> BB04(0.5),BB02(0.5)     ( cond )                     i
BB02 [0002]  2  0    BB01,BB08             1    [00E..01F)-> BB05(0.5),BB03(0.5)     ( cond ) T0      try {       i keep bwd
BB03 [0004]  1  0    BB02                  1    [021..036)-> BB04(1)                 (always) T0      }           i hascall gcsafe idxlen newobj bwd
BB04 [0009]  3       BB01,BB03,BB08        1    [036..057)                           (return)                     i bwd
BB05 [0011]  1       BB02                  1    [???..???)-> BB07(1)                 (callf )                     i internal
BB06 [0012]  1       BB07                  1    [???..???)-> BB08(1)                 (callfr)                     i internal
BB07 [0005]  2     0 BB05                  1    [036..04E)-> BB06(0.5)               (finret)    H0   finally { } i keep bwd
BB08 [0006]  1       BB06                  1    [04E..056)-> BB02(0.5),BB04(0.5)     ( cond )                     i bwd
---------------------------------------------------------------------------------------------------------------------------------------------------------------------

Identifying loops in DFS tree with following reverse post order:
RPO -> BB [pre, post]
00 -> BB01[0, 7]
01 -> BB02[1, 6]
02 -> BB05[7, 5]
03 -> BB03[2, 4]
04 -> BB07[4, 3]
05 -> BB06[5, 2]
06 -> BB08[6, 1]
07 -> BB04[3, 0]

BB08 -> BB02 is a backedge
BB02 is the header of a DFS loop with 1 back edges
Loop has 6 blocks
BB03 -> BB04 is an exit edge
BB08 -> BB04 is an exit edge
BB01 -> BB02 is an entry edge
Added loop L00 with header BB02

Why is BB03 in the loop? It's because of EH: an exception in BB03 can cause BB07 (which is in the loop) to be executed next.

We canonicalize the exit here by introducing BB13 and update flow so we have BB03, BB04 -> BB13 and BB013 -> BB04.

We later realize the finally is empty and remove the try/finally, so this EH flow possibility goes away. But that means the exit canonicalization (loop exit blocks have only loop blocks as preds) we did earlier is no longer valid, one exit now (effectively) moves to BB03 and so BB13 (the other exit) now has a non-loop block as pred.

@AndyAyersMS
Copy link
Member

This shows up as an assert in VN because the EH opt invalidates DFS and hence loops. SSA rebuilds DFS and then VN rebuilds loops and so VN is where post-phase loop checks are first re-enabled.

We could move these EH passes before loop finding, but thought was that cloning would suppress range checks which might make loops bodies inside trys be exception-free and so enable EH region removal before we run the optimization phases. I guess I can try moving the EH phases earlier and see what diffs we end up with.

AndyAyersMS added a commit to AndyAyersMS/runtime that referenced this issue Jan 13, 2025
to avoid messing up loop exit canonicalization (which is sensitive
to loops with internal EH flow).

Fixed dotnet#110959.

Also, remove some of the ceremony around `fgModified` -- seems like this
is no longer useful/needed.

Defer deciding if we need a PSPSym until we create funclets. This removes
unused PSPSym allocations in some cases. Also, there is no benefit to
tracking the PSPSym, so stop doing that.
@dotnet-policy-service dotnet-policy-service bot added the in-pr There is an active PR which will close this issue when it is merged label Jan 13, 2025
AndyAyersMS added a commit that referenced this issue Jan 14, 2025
to avoid messing up loop exit canonicalization (which is sensitive
to loops with internal EH flow).

Fixed #110959.

Also, remove some of the ceremony around `fgModified` -- seems like this
is no longer useful/needed.

Defer deciding if we need a PSPSym until we create funclets. This removes
unused PSPSym allocations in some cases. Also, there is no benefit to
tracking the PSPSym, so stop doing that.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
arch-arm64 area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI in-pr There is an active PR which will close this issue when it is merged
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants