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

Fold always false type checks #99761

Merged
merged 14 commits into from
Mar 18, 2024
Merged

Conversation

MichalStrehovsky
Copy link
Member

@MichalStrehovsky MichalStrehovsky commented Mar 14, 2024

Fixes #97601

If we have a program like:

class Never { }

class Program
{
    static void Main()
    {
        Test(null);
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    static void Test(object o)
    {
        if (o is Never)
            Console.WriteLine("Hello!");
        if (o.GetType() == typeof(Never))
            Console.WriteLine("Hello!");
    }
}

We know these checks are never going to be true thanks to the whole program view. Fold these to false.

If we have a program like:

```csharp
class Never { }

class Program
{
    static void Main()
    {
        Test(null);
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    static void Test(object o)
    {
        if (o is Never)
            Console.WriteLine("Hello!");
        if (o.GetType() == typeof(Never))
            Console.WriteLine("Hello!");
    }
}
```

We know these checks are never going to be true thanks to the whole program view. Fold these to `false`.
@EgorBo
Copy link
Member

EgorBo commented Mar 14, 2024

Closes #97601 I presume?

@MichalStrehovsky
Copy link
Member Author

/azp run runtime-nativeaot-outerloop

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@jkoritzinsky
Copy link
Member

Does this optimization kick in for interfaces? If so, can we add a test around IDynamicInterfaceCastable to ensure that we don't fold casts to types that have the DynamicCastableImplementationAttribute or interfaces they implement (as its possible for no types to statically implement these interfaces but casts to them to still succeed)?

@MichalStrehovsky
Copy link
Member Author

@EgorBo @jakobbotsch either of your could psychically debug the reason for hitting a Assertion failed '!"Unexpected tree op after call marked as tailcall"' in 'Microsoft.CodeAnalysis.Collections.ImmutableSegmentedList1+Builder[System.__Canon]:System.Collections.IList.Contains(System.Object):ubyte:this' during 'Morph - Global' (IL size 13; hash 0x40e822e6; FullOpts)?

@jakobbotsch
Copy link
Member

@EgorBo @jakobbotsch either of your could psychically debug the reason for hitting a Assertion failed '!"Unexpected tree op after call marked as tailcall"' in 'Microsoft.CodeAnalysis.Collections.ImmutableSegmentedList1+Builder[System.__Canon]:System.Collections.IList.Contains(System.Object):ubyte:this' during 'Morph - Global' (IL size 13; hash 0x40e822e6; FullOpts)?

No good guess from my side without the jitdump, I'm afraid.

@MichalStrehovsky
Copy link
Member Author

Does this optimization kick in for interfaces? If so, can we add a test around IDynamicInterfaceCastable to ensure that we don't fold casts to types that have the DynamicCastableImplementationAttribute or interfaces they implement (as its possible for no types to statically implement these interfaces but casts to them to still succeed)?

It does kick in for interfaces. I cannot come up with a targeted test for this in regards to this optimization. I tried writing one, and ended up with exactly the same thing we already test for IDynamicInterfaceCastable.

@EgorBo
Copy link
Member

EgorBo commented Mar 14, 2024

@EgorBo @jakobbotsch either of your could psychically debug the reason for hitting a Assertion failed '!"Unexpected tree op after call marked as tailcall"' in 'Microsoft.CodeAnalysis.Collections.ImmutableSegmentedList1+Builder[System.__Canon]:System.Collections.IList.Contains(System.Object):ubyte:this' during 'Morph - Global' (IL size 13; hash 0x40e822e6; FullOpts)?

hm.. I tried to repro it locally on win-x64 using the CI steps and it succesfully completed on your branch, let's see what your new commit will show

@MichalStrehovsky
Copy link
Member Author

@EgorBo @jakobbotsch either of your could psychically debug the reason for hitting a Assertion failed '!"Unexpected tree op after call marked as tailcall"' in 'Microsoft.CodeAnalysis.Collections.ImmutableSegmentedList1+Builder[System.__Canon]:System.Collections.IList.Contains(System.Object):ubyte:this' during 'Morph - Global' (IL size 13; hash 0x40e822e6; FullOpts)?

hm.. I tried to repro it locally on win-x64 using the CI steps and it succesfully completed on your branch, let's see what your new commit will show

Yeah, the same happened to me. I hope we don't have nondeterminism :(

@MichalStrehovsky
Copy link
Member Author

/azp run runtime-nativeaot-outerloop

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@EgorBo
Copy link
Member

EgorBo commented Mar 14, 2024

@MichalStrehovsky I suspect you need to change return value in getExactClasses for CoreCLR and R2R, I presume those always return 0 now

@MichalStrehovsky
Copy link
Member Author

@MichalStrehovsky I suspect you need to change return value in getExactClasses for CoreCLR and R2R, I presume those always return 0 now

Good point. I'll let the nativeaot outerloop finish and change it tomorrow together with the JitInterface guid.

@MichalStrehovsky
Copy link
Member Author

@EgorBo @jakobbotsch either of your could psychically debug the reason for hitting a Assertion failed '!"Unexpected tree op after call marked as tailcall"' in 'Microsoft.CodeAnalysis.Collections.ImmutableSegmentedList1+Builder[System.__Canon]:System.Collections.IList.Contains(System.Object):ubyte:this' during 'Morph - Global' (IL size 13; hash 0x40e822e6; FullOpts)?

hm.. I tried to repro it locally on win-x64 using the CI steps and it succesfully completed on your branch, let's see what your new commit will show

Yeah, the same happened to me. I hope we don't have nondeterminism :(

It's not nondeterministic - the assert is hitting in main. It very likely broke sometime after 136b100 because that's where my branch was when neither me nor Egor could repro the assert locally. I'll grab a jitdump and file is as a bug, it's unrelated to this PR.

@MichalStrehovsky MichalStrehovsky marked this pull request as ready for review March 15, 2024 06:17
@MichalStrehovsky
Copy link
Member Author

This is ready for review.

@dotnet/ilc-contrib could someone have a look at the ILC part? @EgorBo could you have a look at the JIT part?

@MichalStrehovsky
Copy link
Member Author

/azp run runtime-nativeaot-outerloop

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@MichalStrehovsky
Copy link
Member Author

Sigh, I think I have a fix for the SPMI failures but want to wait until nativeaot outerloop finishes.


#if !READYTORUN
if (result == TypeCompareState.May
&& (canNeverHaveInstanceOfSubclassOf(type1) || canNeverHaveInstanceOfSubclassOf(type2)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can compareTypesForEquality operate on types that are not allocated, but otherwise exist in the system? This change seems to make hidden assumptions about how this API is used by the JIT.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll think about this some more. Worst case I'll just pull this part out. I already used up my JitInterface update quota for the month.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would not be a problem today but it adds a hidden assumption. Removed it. I'll measure how much it impacts things and whether we want to track that as a separate bug.

I know the typeof optimization would help CsWinRT but dont' have numbers.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can fix that by adding a bool flag that says the query is specifically for System.Type operator==/!= and only execute this logic for that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would not be a problem today but it adds a hidden assumption. Removed it. I'll measure how much it impacts things and whether we want to track that as a separate bug.

I removed the wrong part. Fixed it in the latest iteration.

Copy link
Member

@EgorBo EgorBo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JIT part specifically LGTM. There is also one use of getExactClasses in the helperexpansion phase, but that one doesn't need any treatment.

MichalStrehovsky and others added 2 commits March 16, 2024 11:55
@@ -2248,14 +2248,42 @@ private void getFieldInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_MET
// and STS::AccessCheck::CanAccess.
}

private bool canNeverHaveInstanceOfSubclassOf(TypeDesc type)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private bool canNeverHaveInstanceOfSubclassOf(TypeDesc type)
private bool CanNeverHaveInstanceOfSubclassOf(TypeDesc type)

Nit: We seem to use regular naming convention in this file for methods that are not directly exposed on JIT/EE interface

public override bool CanReferenceConstructedMethodTable(TypeDesc type)
=> _constructedMethodTables.Contains(type);

public override bool CanTypeOrCanonicalFormOfTypeBeAllocated(TypeDesc type)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two methods do very similar things, but they have very different names. Should these two methods have similar names, e.g. CanReferenceConstructedMethodTable + CanReferenceConstructedTypeOrCanonicalFormOfType; CanTypeBeAllocated + CanTypeOrCanonicalFormOfTypeBeAllocated ?

Copy link
Member

@jkotas jkotas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM otherwise. Thanks!

@MichalStrehovsky
Copy link
Member Author

/azp run runtime-nativeaot-outerloop

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@MichalStrehovsky MichalStrehovsky merged commit 21cfd9f into dotnet:main Mar 18, 2024
119 of 121 checks passed
@MichalStrehovsky MichalStrehovsky deleted the fix97601 branch March 18, 2024 21:28
@github-actions github-actions bot locked and limited conversation to collaborators Apr 18, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

NAOT: fold "always false" typechecks
5 participants