1
0
Fork 0
mirror of https://github.com/VSadov/Satori.git synced 2025-06-12 10:40:30 +09:00

JIT: Fold more casts (#98528)

This commit is contained in:
Egor Bogatov 2024-02-21 10:47:21 +01:00 committed by GitHub
parent 8cc7586135
commit 8fb9f4b9fa
Signed by: github
GPG key ID: B5690EEEBB952194
5 changed files with 182 additions and 35 deletions

View file

@ -2464,6 +2464,87 @@ ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN)
return resultVN;
}
//----------------------------------------------------------------------------------------
// VNForCast: Returns VN associated with castclass/isinst
//
// Arguments:
// func - Either VNF_CastClass or VNF_IsInstanceOf
// castToVN - VN of the "Cast to" argument
// objVN - VN of the "Cast object" argument
//
// Return Value:
// ValueNum associated with castclass/isinst
//
ValueNum ValueNumStore::VNForCast(VNFunc func, ValueNum castToVN, ValueNum objVN)
{
assert((func == VNF_CastClass) || (func == VNF_IsInstanceOf));
if (objVN == VNForNull())
{
// CastClass(cls, null) -> null
// IsInstanceOf(cls, null) -> null
//
return VNForNull();
}
//
// Fold "CAST(IsInstanceOf(obj, cls), cls)" to "IsInstanceOf(obj, cls)"
// where CAST is either ISINST or CASTCLASS.
//
VNFuncApp funcApp;
if (GetVNFunc(objVN, &funcApp) && (funcApp.m_func == VNF_IsInstanceOf) && (funcApp.m_args[0] == castToVN))
{
// The outer cast is redundant, remove it and preserve its side effects
// We do ignoreRoot here because the actual cast node never throws any exceptions.
return objVN;
}
// Check if we can fold the cast based on the runtime types of the arguments.
//
if (IsVNTypeHandle(castToVN))
{
bool isExact;
bool isNonNull;
CORINFO_CLASS_HANDLE castFrom = GetObjectType(objVN, &isExact, &isNonNull);
CORINFO_CLASS_HANDLE castTo;
if ((castFrom != NO_CLASS_HANDLE) &&
EmbeddedHandleMapLookup(ConstantValue<ssize_t>(castToVN), (ssize_t*)&castTo))
{
TypeCompareState castResult = m_pComp->info.compCompHnd->compareTypesForCast(castFrom, castTo);
if (castResult == TypeCompareState::Must)
{
// IsInstanceOf/CastClass is guaranteed to succeed (we don't need to check for isExact here)
return objVN;
}
if ((castResult == TypeCompareState::MustNot) && isExact && (func == VNF_IsInstanceOf))
{
// IsInstanceOf is guaranteed to fail -> return null (we need to check for isExact here)
return VNForNull();
}
}
}
if (func == VNF_CastClass)
{
// CastClass(cls, obj) -> obj (may throw InvalidCastException)
//
ValueNum vnExcSet = VNExcSetSingleton(VNForFuncNoFolding(TYP_REF, VNF_InvalidCastExc, objVN, castToVN));
return VNWithExc(objVN, vnExcSet);
}
// IsInstanceOf(cls, obj) -> either obj or null - we don't know
//
assert(func == VNF_IsInstanceOf);
Chunk* const c = GetAllocChunk(TYP_REF, CEA_Func2);
unsigned const offsetWithinChunk = c->AllocVN();
VNDefFuncAppFlexible* fapp = c->PointerToFuncApp(offsetWithinChunk, 2);
fapp->m_func = VNF_IsInstanceOf;
fapp->m_args[0] = castToVN;
fapp->m_args[1] = objVN;
return c->m_baseVN + offsetWithinChunk;
}
//----------------------------------------------------------------------------------------
// VNForFunc - Returns the ValueNum associated with 'func'('arg0VN','arg1VN')
// There is a one-to-one relationship between the ValueNum
@ -2527,24 +2608,9 @@ ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN, V
}
else
{
if (func == VNF_CastClass)
if ((func == VNF_CastClass) || (func == VNF_IsInstanceOf))
{
if (arg1VN == VNForNull())
{
// CastClass(cls, null) -> null
resultVN = VNForNull();
}
else
{
// CastClass(cls, obj) -> obj (may throw InvalidCastException)
ValueNum vnExcSet = VNExcSetSingleton(VNForFuncNoFolding(TYP_REF, VNF_InvalidCastExc, arg1VN, arg0VN));
resultVN = VNWithExc(arg1VN, vnExcSet);
}
}
else if ((func == VNF_IsInstanceOf) && (arg1VN == VNForNull()))
{
// IsInstanceOf(cls, null) -> null
resultVN = VNForNull();
resultVN = VNForCast(func, arg0VN, arg1VN);
}
else
{
@ -13910,3 +13976,68 @@ bool ValueNumPair::BothDefined() const
{
return (m_liberal != ValueNumStore::NoVN) && (m_conservative != ValueNumStore::NoVN);
}
//--------------------------------------------------------------------------------
// GetObjectType: Try to get a class handle (hopefully, exact) for given object via VN
//
// Arguments:
// vn - Value number of the object
// pIsExact - [out] set to true if the class handle is exact
// pIsNonNull - [out] set to true if the object is known to be non-null
//
// Return Value:
// Class handle for the object, or NO_CLASS_HANDLE if not available
//
CORINFO_CLASS_HANDLE ValueNumStore::GetObjectType(ValueNum vn, bool* pIsExact, bool* pIsNonNull)
{
*pIsNonNull = false;
*pIsExact = false;
if (TypeOfVN(vn) != TYP_REF)
{
// Not an object
return NO_CLASS_HANDLE;
}
if (IsVNObjHandle(vn))
{
// We know exact type for nongc objects, and they can never be null
*pIsNonNull = true;
*pIsExact = true;
size_t handle = CoercedConstantValue<size_t>(vn);
return m_pComp->info.compCompHnd->getObjectType((CORINFO_OBJECT_HANDLE)handle);
}
VNFuncApp funcApp;
if (!GetVNFunc(vn, &funcApp))
{
// We can't make any assumptions about the object
return NO_CLASS_HANDLE;
}
// CastClass/IsInstanceOf/JitNew all have the class handle as the first argument
const VNFunc func = funcApp.m_func;
if ((func == VNF_CastClass) || (func == VNF_IsInstanceOf) || (func == VNF_JitNew))
{
ssize_t clsHandle;
ValueNum clsVN = funcApp.m_args[0];
if (IsVNTypeHandle(clsVN) && EmbeddedHandleMapLookup(ConstantValue<ssize_t>(clsVN), &clsHandle))
{
// JitNew returns an exact and non-null obj, castclass and isinst do not have this guarantee.
*pIsNonNull = func == VNF_JitNew;
*pIsExact = func == VNF_JitNew;
return (CORINFO_CLASS_HANDLE)clsHandle;
}
}
// obj.GetType() is guaranteed to return a non-null RuntimeType object
if (func == VNF_ObjGetType)
{
*pIsNonNull = true;
// Let's not assume whether RuntimeType is exact or not here (it was not in the past for NAOT)
// Callers usually call isExact anyway.
return m_pComp->info.compCompHnd->getBuiltinClass(CLASSID_RUNTIME_TYPE);
}
return NO_CLASS_HANDLE;
}