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

Eliminate dead branches around typeof comparisons #102248

Merged
merged 4 commits into from
Jun 19, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,12 @@ public bool CanInline(MethodDesc caller, MethodDesc callee)

public bool CanReferenceConstructedMethodTable(TypeDesc type)
{
return NodeFactory.DevirtualizationManager.CanReferenceConstructedMethodTable(type);
return NodeFactory.DevirtualizationManager.CanReferenceConstructedMethodTable(type.NormalizeInstantiation());
}

public bool CanReferenceConstructedTypeOrCanonicalFormOfType(TypeDesc type)
{
return NodeFactory.DevirtualizationManager.CanReferenceConstructedTypeOrCanonicalFormOfType(type);
return NodeFactory.DevirtualizationManager.CanReferenceConstructedTypeOrCanonicalFormOfType(type.NormalizeInstantiation());
}

public DelegateCreationInfo GetDelegateCtor(TypeDesc delegateType, MethodDesc target, TypeDesc constrainedType, bool followVirtualDispatch)
Expand Down Expand Up @@ -266,9 +266,7 @@ public bool NeedsRuntimeLookup(ReadyToRunHelperId lookupKind, object targetOfLoo

public ReadyToRunHelperId GetLdTokenHelperForType(TypeDesc type)
{
bool canConstructPerWholeProgramAnalysis = NodeFactory.DevirtualizationManager.CanReferenceConstructedMethodTable(type);
bool creationAllowed = ConstructedEETypeNode.CreationAllowed(type);
return (canConstructPerWholeProgramAnalysis && creationAllowed)
return (ConstructedEETypeNode.CreationAllowed(type) && NodeFactory.DevirtualizationManager.CanReferenceConstructedMethodTable(type.NormalizeInstantiation()))
? ReadyToRunHelperId.TypeHandle
: ReadyToRunHelperId.NecessaryTypeHandle;
}
Expand Down
12 changes: 10 additions & 2 deletions src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -703,10 +703,18 @@ protected override MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefTyp
}

public override bool CanReferenceConstructedMethodTable(TypeDesc type)
=> _constructedMethodTables.Contains(type);
{
Debug.Assert(type.NormalizeInstantiation() == type);
Debug.Assert(ConstructedEETypeNode.CreationAllowed(type));
return _constructedMethodTables.Contains(type);
Copy link
Member

Choose a reason for hiding this comment

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

Should we also assert that we are only adding normalizations into _constructedMethodTables when it is populated?

}

public override bool CanReferenceConstructedTypeOrCanonicalFormOfType(TypeDesc type)
=> _constructedMethodTables.Contains(type) || _canonConstructedMethodTables.Contains(type);
{
Debug.Assert(type.NormalizeInstantiation() == type);
Debug.Assert(ConstructedEETypeNode.CreationAllowed(type));
return _constructedMethodTables.Contains(type) || _canonConstructedMethodTables.Contains(type);
}

public override TypeDesc[] GetImplementingClasses(TypeDesc type)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1021,7 +1021,11 @@ private bool TryExpandTypeEquality_TokenOther(MethodIL methodIL, byte[] body, Op
if (knownType.IsCanonicalDefinitionType(CanonicalFormKind.Any))
return false;

if (_devirtualizationManager.CanReferenceConstructedTypeOrCanonicalFormOfType(knownType))
// We don't track types without a constructed MethodTable very well.
if (!ConstructedEETypeNode.CreationAllowed(knownType))
return false;

if (_devirtualizationManager.CanReferenceConstructedTypeOrCanonicalFormOfType(knownType.NormalizeInstantiation()))
return false;

constant = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ public override IEETypeNode NecessaryTypeSymbolIfPossible(TypeDesc type)
// information proving that it isn't, give RyuJIT the constructed symbol even
// though we just need the unconstructed one.
// https://github.com/dotnet/runtimelab/issues/1128
bool canPotentiallyConstruct = NodeFactory.DevirtualizationManager.CanReferenceConstructedMethodTable(type);
bool canPotentiallyConstruct = ConstructedEETypeNode.CreationAllowed(type)
&& NodeFactory.DevirtualizationManager.CanReferenceConstructedMethodTable(type);
if (canPotentiallyConstruct)
return _nodeFactory.MaximallyConstructableType(type);

Expand All @@ -81,7 +82,8 @@ public override IEETypeNode NecessaryTypeSymbolIfPossible(TypeDesc type)

public FrozenRuntimeTypeNode NecessaryRuntimeTypeIfPossible(TypeDesc type)
{
bool canPotentiallyConstruct = NodeFactory.DevirtualizationManager.CanReferenceConstructedMethodTable(type);
bool canPotentiallyConstruct = ConstructedEETypeNode.CreationAllowed(type)
&& NodeFactory.DevirtualizationManager.CanReferenceConstructedMethodTable(type);
if (canPotentiallyConstruct)
return _nodeFactory.SerializedMaximallyConstructableRuntimeTypeObject(type);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -351,9 +351,14 @@ class Canary2 { }
class Never3 { }
class Canary3 { }

class Maybe1<T, U> { }

[MethodImpl(MethodImplOptions.NoInlining)]
static Type GetTheType() => null;

[MethodImpl(MethodImplOptions.NoInlining)]
static Type GetThePointerType() => typeof(void*);

[MethodImpl(MethodImplOptions.NoInlining)]
static object GetTheObject() => new object();

Expand All @@ -371,19 +376,61 @@ public static void Run()
#if !DEBUG
ThrowIfPresent(typeof(TestTypeEquals), nameof(Never));

Type someType = GetTheType();
if (someType == typeof(Never2))
{
s_sink = new Canary2();
RunCheck(GetTheType());

static void RunCheck(Type t)
{
if (t == typeof(Never2))
{
s_sink = new Canary2();
}
}

ThrowIfPresentWithUsableMethodTable(typeof(TestTypeEquals), nameof(Canary2));
}

{

RunCheck(GetTheObject());

static void RunCheck(object o)
{
if (o.GetType() == typeof(Never3))
{
s_sink = new Canary3();
}
}

ThrowIfPresentWithUsableMethodTable(typeof(TestTypeEquals), nameof(Canary3));
}

{
RunCheck(GetThePointerType());

static void RunCheck(Type t)
{
if (t == typeof(void*))
{
return;
}
throw new Exception();
}
}
ThrowIfPresentWithUsableMethodTable(typeof(TestTypeEquals), nameof(Canary2));

object someObject = GetTheObject();
if (someObject.GetType() == typeof(Never3))
{
s_sink = new Canary3();
RunCheck<object>(typeof(Maybe1<object, string>));

[MethodImpl(MethodImplOptions.NoInlining)]
static void RunCheck<T>(Type t)
{
if (t == typeof(Maybe1<T, string>))
{
return;
}
throw new Exception();
}
}
ThrowIfPresentWithUsableMethodTable(typeof(TestTypeEquals), nameof(Canary3));
#endif
}
}
Expand Down
Loading