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

Assertion failed 'argInfo.argIsUsed == false' during 'Morph - Inlining' #112092

Closed
MichalStrehovsky opened this issue Feb 3, 2025 · 20 comments · Fixed by #112119
Closed

Assertion failed 'argInfo.argIsUsed == false' during 'Morph - Inlining' #112092

MichalStrehovsky opened this issue Feb 3, 2025 · 20 comments · Fixed by #112119
Assignees
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI blocking-outerloop Blocking the 'runtime-coreclr outerloop' and 'runtime-libraries-coreclr outerloop' runs in-pr There is an active PR which will close this issue when it is merged
Milestone

Comments

@MichalStrehovsky
Copy link
Member

MichalStrehovsky commented Feb 3, 2025

Sample failure log: https://dev.azure.com/dnceng-public/public/_build/results?buildId=937252&view=logs&j=6a7e26fa-36e7-5a45-28af-dc6c8e6724e6&t=089ef86b-599a-543a-2c8c-82b31601e3ec

  ILC: /__w/1/s/src/coreclr/jit/fginline.cpp:1888
  ILC: Assertion failed 'argInfo.argIsUsed == false' in 'System.Threading.Tasks.Tests.ParallelForTests+MyPartitioner`1[System.__Canon]:GetDynamicPartitions():System.Collections.Generic.IEnumerable`1[System.__Canon]:this' during 'Morph - Inlining' (IL size 23; hash 0x3ce6bc1d; FullOpts)
  
/__w/1/s/artifacts/bin/coreclr/linux.x64.Checked/build/Microsoft.NETCore.Native.targets(316,5): error MSB3073: The command ""/__w/1/s/artifacts/bin/coreclr/linux.x64.Checked/x64/ilc/ilc" @"/__w/1/s/artifacts/obj/System.Threading.Tasks.Parallel.Tests/Release/net10.0/native/System.Threading.Tasks.Parallel.Tests.ilc.rsp"" exited with code 134. [/__w/1/s/src/libraries/System.Threading.Tasks.Parallel/tests/System.Threading.Tasks.Parallel.Tests.csproj]

This is failing in native AOT OptimizationPreference=Speed runs on both Windows and Linux. I.e. you need to add /p:OptimizationPreference=Speed to the build command line, in addition to /p:TestNativeAot=true.

This started failing sometime between ad7b829 (last good run) and 6f221b4 (first bad run).

@MichalStrehovsky MichalStrehovsky added area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI blocking-outerloop Blocking the 'runtime-coreclr outerloop' and 'runtime-libraries-coreclr outerloop' runs labels Feb 3, 2025
@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Feb 3, 2025
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

#111948 is the most likely culprit, cc @hez2010

@hez2010
Copy link
Contributor

hez2010 commented Feb 3, 2025

This is failing in native AOT OptimizationPreference=Size runs on both Windows and Linux. I.e. you need to add /p:OptimizationPreference=Speed to the build command line

Do you mean adding /p:OptimizationPreference=Size?

But I don't get how can this affect OptimizationPreference=Size only.

@MichalStrehovsky
Copy link
Member Author

But I don't get how can this affect OptimizationPreference=Size only.

I meant speed (the comment has both size and speed, but this is for speed).

Speed enables tier-1 inliner and will inline more.

@MichalStrehovsky
Copy link
Member Author

I've created #112097 and running outerloop on it just to check the theory.

@amanasifkhalid amanasifkhalid removed the untriaged New issue has not been triaged by the area owner label Feb 3, 2025
@amanasifkhalid amanasifkhalid added this to the 10.0.0 milestone Feb 3, 2025
@hez2010
Copy link
Contributor

hez2010 commented Feb 3, 2025

I can repro this locally. While I didn't see any fault of #111948, it's doing the right thing.
Late devirt managed to devirt

               [000013] --C-G------                         *  CALLV vt-ind ref    System.Collections.Concurrent.OrderablePartitioner`1[System.__Canon]:GetOrderableDynamicPartitions():System.Collections.Generic.IEnumerable`1[System.Collections.Generic.KeyValuePair`2[long,System.__Canon]]:this
               [000044] ----------- this                    \--*  LCL_VAR   ref    V04 tmp3

into

               [000013] --C-G------                         *  CALL nullcheck ref    System.Collections.Concurrent.Partitioner+DynamicPartitionerForIndexRange_Abstract`2[System.__Canon,System.__Canon]:GetOrderableDynamicPartitions():System.Collections.Generic.IEnumerable`1[System.Collections.Generic.KeyValuePair`2[long,System.__Canon]]:this
               [000044] ----------- this                    \--*  LCL_VAR   ref    V04 tmp3

Then we are trying to inline it, so that we split the tree

STMT00003 ( ??? ... ??? )
               [000014] I-C-G------                         *  CALL      ref    MyPartitioner`1[System.__Canon]:DropIndices(System.Collections.Generic.IEnumerable`1[System.Collections.Generic.KeyValuePair`2[long,System.__Canon]]):System.Collections.Generic.IEnumerable`1[System.__Canon] (exactContextHandle=0x00007FF9B9899D51)
               [000021] ----------- gctx                    +--*  RUNTIMELOOKUP long   0x7ff9b9899d50 class
               [000020] -----------                         |  \--*  LCL_VAR   long   V03 tmp2
               [000013] I-C-G------ arg1                    \--*  CALL nullcheck ref    System.Collections.Concurrent.Partitioner+DynamicPartitionerForIndexRange_Abstract`2[System.__Canon,System.__Canon]:GetOrderableDynamicPartitions():System.Collections.Generic.IEnumerable`1[System.Collections.Generic.KeyValuePair`2[long,System.__Canon]]:this (exactContextHandle=0x00007FF9B9A58D09)
               [000044] ----------- this                       \--*  LCL_VAR   ref    V04 tmp3

into

STMT00022 ( ??? ... ??? )
               [000084] DA---------                         *  STORE_LCL_VAR long   V09 tmp8
               [000021] -----------                         \--*  RUNTIMELOOKUP long   0x7ff9b9899d50 class
               [000020] -----------                            \--*  LCL_VAR   long   V03 tmp2

STMT00003 ( ??? ... ??? )
               [000014] I-C-G------                         *  CALL      ref    MyPartitioner`1[System.__Canon]:DropIndices(System.Collections.Generic.IEnumerable`1[System.Collections.Generic.KeyValuePair`2[long,System.__Canon]]):System.Collections.Generic.IEnumerable`1[System.__Canon] (exactContextHandle=0x00007FF9B9899D51)
               [000085] ----------- gctx                    +--*  LCL_VAR   long   V09 tmp8
               [000013] I-C-G------ arg1                    \--*  CALL nullcheck ref    System.Collections.Concurrent.Partitioner+DynamicPartitionerForIndexRange_Abstract`2[System.__Canon,System.__Canon]:GetOrderableDynamicPartitions():System.Collections.Generic.IEnumerable`1[System.Collections.Generic.KeyValuePair`2[long,System.__Canon]]:this (exactContextHandle=0x00007FF9B9A58D09)
               [000044] ----------- this                       \--*  LCL_VAR   ref    V04 tmp3

Then it managed to inline it, and resulted in the following tree:

STMT00002 ( ??? ... ??? )
               [000019] DACXG------                         *  STORE_LCL_VAR long   V03 tmp2
               [000018] --CXG------                         \--*  CALL help long   CORINFO_HELP_RUNTIMEHANDLE_CLASS
               [000016] #--X------- arg0                       +--*  IND       long
               [000015] !----------                            |  \--*  LCL_VAR   ref    V00 this
               [000017] H------N--- arg1                       \--*  CNS_INT(h) long   0x7ff9b9a63af8 global ptr

STMT00022 ( ??? ... ??? )
               [000084] DA---------                         *  STORE_LCL_VAR long   V09 tmp8
               [000021] -----------                         \--*  RUNTIMELOOKUP long   0x7ff9b9899d50 class
               [000020] -----------                            \--*  LCL_VAR   long   V03 tmp2

STMT00024 ( INL08 @ 0x000[E-] ... ??? ) <- INLRT @ ???
               [000090] I-CXG------                         *  CALL nullcheck ref    System.Collections.Concurrent.Partitioner+DynamicPartitionerForIList`1[System.__Canon]:GetOrderableDynamicPartitions_Factory(System.Collections.Generic.IList`1[System.__Canon]):System.Collections.Generic.IEnumerable`1[System.Collections.Generic.KeyValuePair`2[long,System.__Canon]]:this (exactContextHandle=0x00007FF9B9A59031)
               [000044] ----------- this                    +--*  LCL_VAR   ref    V04 tmp3
               [000089] n--XG------ arg1                    \--*  IND       ref
               [000088] ---X-------                            \--*  FIELD_ADDR byref  System.Collections.Concurrent.Partitioner+DynamicPartitionerForIndexRange_Abstract`2[System.__Canon,System.__Canon]:_data
               [000087] -----------                               \--*  LCL_VAR   ref    V04 tmp3

STMT00003 ( ??? ... ??? )
               [000014] I-C-G------                         *  CALL      ref    MyPartitioner`1[System.__Canon]:DropIndices(System.Collections.Generic.IEnumerable`1[System.Collections.Generic.KeyValuePair`2[long,System.__Canon]]):System.Collections.Generic.IEnumerable`1[System.__Canon] (exactContextHandle=0x00007FF9B9899D51)
               [000085] ----------- gctx                    +--*  LCL_VAR   long   V09 tmp8
               [000086] --C-------- arg1                    \--*  RET_EXPR  ref   (for [000013]) -> [000091]

STMT00004 ( ??? ... ??? )
               [000023] --C--------                         *  RETURN    ref
               [000022] --C--------                         \--*  RET_EXPR  ref   (for [000014])

Then the inliner tries to inline [000090], and expands the placeholder [000086] with [000104], so we end up tree like

STMT00002 ( ??? ... ??? )
               [000019] DACXG------                         *  STORE_LCL_VAR long   V03 tmp2
               [000018] --CXG------                         \--*  CALL help long   CORINFO_HELP_RUNTIMEHANDLE_CLASS
               [000016] #--X------- arg0                       +--*  IND       long
               [000015] !----------                            |  \--*  LCL_VAR   ref    V00 this
               [000017] H------N--- arg1                       \--*  CNS_INT(h) long   0x7ff9b9a63af8 global ptr

STMT00022 ( ??? ... ??? )
               [000084] DA---------                         *  STORE_LCL_VAR long   V09 tmp8
               [000021] -----------                         \--*  RUNTIMELOOKUP long   0x7ff9b9899d50 class
               [000020] -----------                            \--*  LCL_VAR   long   V03 tmp2

STMT00028 ( INL08 @ 0x000[E-] ... ??? ) <- INLRT @ ???
               [000107] DA-XG------                         *  STORE_LCL_VAR ref    V10 tmp9
               [000089] n--XG------                         \--*  IND       ref
               [000088] ---X-------                            \--*  FIELD_ADDR byref  System.Collections.Concurrent.Partitioner+DynamicPartitionerForIndexRange_Abstract`2[System.__Canon,System.__Canon]:_data
               [000087] -----------                               \--*  LCL_VAR   ref    V04 tmp3

STMT00029 ( INL08 @ 0x000[E-] ... ??? ) <- INLRT @ ???
               [000106] ---X-------                         *  NULLCHECK byte
               [000105] -----------                         \--*  LCL_VAR   ref    V04 tmp3

STMT00025 ( INL09 @ 0x000[E-] ... ??? ) <- INL08 @ 0x000[E-] <- INLRT @ ???
               [000097] DACXG------                         *  STORE_LCL_VAR long   V12 tmp11
               [000096] --CXG------                         \--*  CALL help long   CORINFO_HELP_RUNTIMEHANDLE_CLASS
               [000094] #--X------- arg0                       +--*  IND       long
               [000044] !----------                            |  \--*  LCL_VAR   ref    V04 tmp3
               [000095] H------N--- arg1                       \--*  CNS_INT(h) long   0x7ff9b9a6a7a8 global ptr

STMT00026 ( INL09 @ ??? ... ??? ) <- INL08 @ 0x000[E-] <- INLRT @ ???
               [000101] DA---------                         *  STORE_LCL_VAR ref    V11 tmp10
               [000100] -----------                         \--*  ALLOCOBJ  ref
               [000099] -----------                            \--*  RUNTIMELOOKUP long   0x7ff9b9a5b3e0 class
               [000098] -----------                               \--*  LCL_VAR   long   V12 tmp11

STMT00027 ( INL09 @ ??? ... ??? ) <- INL08 @ 0x000[E-] <- INLRT @ ???
               [000103] I-C-G------                         *  CALL      void   System.Collections.Concurrent.Partitioner+DynamicPartitionerForIList`1+InternalPartitionEnumerable[System.__Canon]:.ctor(System.Collections.Generic.IList`1[System.__Canon]):this (exactContextHandle=0x00007FF9B9A5B3E1)
               [000102] ----------- this                    +--*  LCL_VAR   ref    V11 tmp10
               [000093] ----------- arg1                    \--*  LCL_VAR   ref    V10 tmp9

STMT00003 ( ??? ... ??? )
               [000014] I-C-G------                         *  CALL      ref    MyPartitioner`1[System.__Canon]:DropIndices(System.Collections.Generic.IEnumerable`1[System.Collections.Generic.KeyValuePair`2[long,System.__Canon]]):System.Collections.Generic.IEnumerable`1[System.__Canon] (exactContextHandle=0x00007FF9B9899D51)
               [000085] ----------- gctx                    +--*  LCL_VAR   long   V09 tmp8
               [000086] ----------- arg1                    \--*  LCL_VAR   ref    V11 tmp10

STMT00004 ( ??? ... ??? )
               [000023] --C--------                         *  RETURN    ref
               [000022] --C--------                         \--*  RET_EXPR  ref   (for [000014])

So far everything is fine.

Then the inliner tries to inline [000014], while inserting inlinee arguments, the JIT thinks the arg [000085] has side effects and has been used, which triggers the assertion:

----------- Statements (and blocks) added due to the inlining of call [000014] -----------

Assert failure(PID 81556 [0x00013e94], Thread: 82940 [0x143fc]): Assertion failed 'argInfo.argIsUsed == false' in 'MyPartitioner`1[System.__Canon]:GetDynamicPartitions():System.Collections.Generic.IEnumerable`1[System.__Canon]:this' during 'Morph - Inlining' (IL size 23; hash 0xa43b6333; FullOpts)

    File: D:\runtime\src\coreclr\jit\fginline.cpp:1888

And the inlinee's tree containing [000085] that is being inserted at the moment is:

STMT00030 ( 0x000[E-] ... ??? ) <- INLRT @ ???
               [000112] DAC-G------                         *  STORE_LCL_VAR long   V14 tmp13
               [000111] --C-G------                         \--*  CALL help long   CORINFO_HELP_RUNTIMEHANDLE_CLASS
               [000085] ----------- arg0                       +--*  LCL_VAR   long   V09 tmp8
               [000110] H------N--- arg1                       \--*  CNS_INT(h) long   0x7ff9c42fc4b0 global ptr

@jakobbotsch I don't think this issue is introduced by my change, without #111948 we would not try to inline [000013] so that's why we didn't see this assertion before. So my change seems to expose some preexisting issues in the JIT that need to be fixed. Does this ring a bell?

BTW it seems that it doesn't affect the correctness.

A repro is

using System.Collections.Concurrent;
using System.Runtime.CompilerServices;

new MyPartitioner<string>([]).GetDynamicPartitions();

public class MyPartitioner<TSource> : Partitioner<TSource>
{
    private IList<TSource> _data;

    public MyPartitioner(IList<TSource> data)
    {
        _data = data;
    }

    public override IList<IEnumerator<TSource>> GetPartitions(int partitionCount)
    {
        if (partitionCount <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(partitionCount));
        }
        IEnumerator<TSource>[] partitions
            = new IEnumerator<TSource>[partitionCount];
        IEnumerable<KeyValuePair<long, TSource>> partitionEnumerable = Partitioner.Create(_data, true).GetOrderableDynamicPartitions();
        for (int i = 0; i < partitionCount; i++)
        {
            partitions[i] = DropIndices(partitionEnumerable.GetEnumerator());
        }
        return partitions;
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    public override IEnumerable<TSource> GetDynamicPartitions()
    {
        return DropIndices(Partitioner.Create(_data, true).GetOrderableDynamicPartitions());
    }

    private static IEnumerable<TSource> DropIndices(IEnumerable<KeyValuePair<long, TSource>> source)
    {
        foreach (KeyValuePair<long, TSource> pair in source)
        {
            yield return pair.Value;
        }
    }

    private static IEnumerator<TSource> DropIndices(IEnumerator<KeyValuePair<long, TSource>> source)
    {
        while (source.MoveNext())
        {
            yield return source.Current.Value;
        }
    }

    public override bool SupportsDynamicPartitions
    {
        get { return true; }
    }
}

@AndyAyersMS
Copy link
Member

My guess is that since [000014] was already an inline candidate when this all started, its inline info was based on the old trees, and splitting a child out from under it has invalidated this info.

@hez2010
Copy link
Contributor

hez2010 commented Feb 3, 2025

So the fix seems to be: if we split an arg of a call that is already an inline candidate, we should update its inline info?

@AndyAyersMS
Copy link
Member

Maybe. I don't know if that is feasible.

It might be easier to just skip late inlining for cases where the parent is already an inline candidate. Apparently, this doesn't happen all that often or we would have caught this in the testing we did before merging.

@jakobbotsch
Copy link
Member

I think it will be fixed if we switch the order of inlining and SubstitutePlaceholdersAndDevirtualizeWalker::WalkStatement in fgInline. That should work assuming we still end up visiting all the statements after the inlining happens (or does not happen).

@jakobbotsch
Copy link
Member

Hmm, I guess we really want to do the substitution of GT_RET_EXPR but not the late-devirt/inlining part, so maybe it's not quite that simple.

@AndyAyersMS
Copy link
Member

We could notice during the substitution if we form new inlining candidates and re-walk to do those inlines, perhaps.

@hez2010
Copy link
Contributor

hez2010 commented Feb 3, 2025

else
{
// Conservative approach
ctxInfo->argHasSideEff = true;
ctxInfo->argHasGlobRef = true;
}

And seems that we are conservatively marking all non-constant inlinee args which are inst param as argHasSideEff while doing inlining.

@jakobbotsch
Copy link
Member

runtime/src/coreclr/jit/importer.cpp

Lines 13143 to 13148 in ca99c80

else
{
// Conservative approach
ctxInfo->argHasSideEff = true;
ctxInfo->argHasGlobRef = true;
}
And seems that we are conservatively marking all non-constant inlinee args as argHasSideEff while doing inlining.

That's just for InstParam, and I am unifying that with all other args in #112010.

@hez2010
Copy link
Contributor

hez2010 commented Feb 3, 2025

That's just for InstParam, and I am unifying that with all other args in #112010.

I can confirm #112010 fixes this issue.
The arg V09 causing the assertion is exactly an InstParam.

/cc @MichalStrehovsky, the fix seems to be #112010.

@AndyAyersMS
Copy link
Member

I think the underlying problem still remains. It is not safe to modify a call's arguments once we have formed the inline candidate info.

@AndyAyersMS
Copy link
Member

Though presumably we do exactly this when we substitute for ret exprs; perhaps that works because those look like calls during the initial analysis and so get "worst case" treatement (that is they have no special properties we can exploit).

@hez2010
Copy link
Contributor

hez2010 commented Feb 3, 2025

I think the underlying problem still remains. It is not safe to modify a call's arguments once we have formed the inline candidate info.

The inline candidate info seems to contain nothing about the call's arguments. We don't form the inlinee's arg info until we are actually inlining it. My thought is that the issue is about an edge case when it comes to an InstParam arg.

@AndyAyersMS
Copy link
Member

Ah, ok, right, we don't gather the inline arg info early. That makes sense. So maybe the issue is just restricted to instance params.

@jakobbotsch
Copy link
Member

Then let me grab this one. #112010 still needs a bit of work to investigate some regressions.

@jakobbotsch jakobbotsch assigned jakobbotsch and unassigned hez2010 Feb 3, 2025
jakobbotsch added a commit to jakobbotsch/runtime that referenced this issue Feb 4, 2025
* Handle InstParam in the same way as all other arguments
* Insert the evaluation of the InstParam in the right spot when inlining
  succeeded

Fix dotnet#112092
@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 Feb 4, 2025
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 blocking-outerloop Blocking the 'runtime-coreclr outerloop' and 'runtime-libraries-coreclr outerloop' runs 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.

5 participants