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

JIT: Fold more always failing type checks #97005

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
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
115 changes: 64 additions & 51 deletions src/coreclr/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5303,70 +5303,83 @@ GenTree* Compiler::impOptimizeCastClassOrIsInst(GenTree* op1, CORINFO_RESOLVED_T
bool isNonNull = false;
CORINFO_CLASS_HANDLE fromClass = gtGetClassHandle(op1, &isExact, &isNonNull);

if (fromClass != nullptr)
if (fromClass == nullptr)
{
CORINFO_CLASS_HANDLE toClass = pResolvedToken->hClass;
JITDUMP("\nConsidering optimization of %s from %s%p (%s) to %p (%s)\n", isCastClass ? "castclass" : "isinst",
isExact ? "exact " : "", dspPtr(fromClass), eeGetClassName(fromClass), dspPtr(toClass),
eeGetClassName(toClass));
JITDUMP("\nCannot optimize since fromClass is unknown\n");
return nullptr;
}

// Perhaps we know if the cast will succeed or fail.
TypeCompareState castResult = info.compCompHnd->compareTypesForCast(fromClass, toClass);
CORINFO_CLASS_HANDLE toClass = pResolvedToken->hClass;
JITDUMP("\nConsidering optimization of %s from %s%p (%s) to %p (%s)\n", isCastClass ? "castclass" : "isinst",
isExact ? "exact " : "", dspPtr(fromClass), eeGetClassName(fromClass), dspPtr(toClass),
eeGetClassName(toClass));

if (castResult == TypeCompareState::Must)
{
// Cast will succeed, result is simply op1.
JITDUMP("Cast will succeed, optimizing to simply return input\n");
return op1;
}
else if (castResult == TypeCompareState::MustNot)
{
// See if we can sharpen exactness by looking for final classes
if (!isExact)
{
isExact = impIsClassExact(fromClass);
}
// Perhaps we know if the cast will succeed or fail.
TypeCompareState castResult = info.compCompHnd->compareTypesForCast(fromClass, toClass);

// Cast to exact type will fail. Handle case where we have
// an exact type (that is, fromClass is not a subtype)
// and we're not going to throw on failure.
if (isExact && !isCastClass)
{
JITDUMP("Cast will fail, optimizing to return null\n");
if (castResult == TypeCompareState::May)
{
JITDUMP("Result of cast unknown, must generate runtime test\n");
return nullptr;
}

// If the cast was fed by a box, we can remove that too.
if (op1->IsBoxedValue())
{
JITDUMP("Also removing upstream box\n");
gtTryRemoveBoxUpstreamEffects(op1);
}
if (castResult == TypeCompareState::Must)
{
// Even if op1 is more derived we know that it will still succeed.
JITDUMP("Cast will succeed, optimizing to simply return input\n");
return op1;
}

if (gtTreeHasSideEffects(op1, GTF_SIDE_EFFECT))
{
impAppendTree(op1, CHECK_SPILL_ALL, impCurStmtDI);
}
return gtNewNull();
}
else if (isExact)
{
JITDUMP("Not optimizing failing castclass (yet)\n");
}
else
{
JITDUMP("Can't optimize since fromClass is inexact\n");
}
assert(castResult == TypeCompareState::MustNot);
if (isCastClass)
{
// TODO-CQ: Throw cast exception inline? Probably not important.
JITDUMP("Cannot optimize potentially failing castclass\n");
return nullptr;
}

// We know that an object of type fromClass cannot be cast to toClass, but
// if it is more derived it could still succeed a cast.
if (!isExact)
{
// We can still fail some type casts if they are between two types that
// belong to disjoint type hierarchies.
// TODO: compareTypesForCast(interfaceType, otherType) returning
// MustNot seems less useful than just having it return May. If we
// change this on EE side we can avoid these getClassAttribs calls.
Comment on lines +5347 to +5349
Copy link
Member Author

Choose a reason for hiding this comment

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

@jkotas do you think it makes sense to change this in compareTypesForCast? From what I understand, the current semantics of compareTypesForCast is "if I have an object of exact type A, can it be cast to type B" -- which is a meaningless question when A is an interface type. Alternatively, do you have any suggestions on better ways to check what we're after here?

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm, looks like we fold Type.IsAssignableFrom/Type.IsAssignableTo using compareTypesForCast as well, so seems like we cannot really change its behavior.

Copy link
Member

Choose a reason for hiding this comment

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

do you think it makes sense to change this in compareTypesForCast?

Yes, I think this logic should be better done on the EE side.

looks like we fold Type.IsAssignableFrom/Type.IsAssignableTo using compareTypesForCast as well, so seems like we cannot really change its behavior.

You can add a flag to the compareTypesForCast or add a new JIT/EE interface method.

if ((info.compCompHnd->getClassAttribs(fromClass) & CORINFO_FLG_INTERFACE) != 0)
{
JITDUMP("Cannot optimize potentially failing isinst from interface type\n");
return nullptr;
}
else

if ((info.compCompHnd->getClassAttribs(toClass) & CORINFO_FLG_INTERFACE) != 0)
{
JITDUMP("Result of cast unknown, must generate runtime test\n");
JITDUMP("Cannot optimize potentially failing isinst to interface type\n");
return nullptr;
}

if (info.compCompHnd->compareTypesForCast(toClass, fromClass) != TypeCompareState::MustNot)
{
JITDUMP("Cannot optimize potentially failing isinst: derived type cast may succeed\n");
return nullptr;
}
}
else

JITDUMP("Cast will fail, optimizing to return null\n");

// If the cast was fed by a box, we can remove that too.
if (op1->IsBoxedValue())
{
JITDUMP("\nCan't optimize since fromClass is unknown\n");
JITDUMP("Also removing upstream box\n");
gtTryRemoveBoxUpstreamEffects(op1);
}

return nullptr;
if (gtTreeHasSideEffects(op1, GTF_SIDE_EFFECT))
{
impAppendTree(op1, CHECK_SPILL_ALL, impCurStmtDI);
}
return gtNewNull();
}

//------------------------------------------------------------------------
Expand Down
Loading