Skip to content

BitCast<TYP_REF>(TypeHandleToRuntimeTypeHandle(clsHandle)) => nongc obj #97955

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

Merged
merged 8 commits into from
Feb 7, 2024
Merged
Show file tree
Hide file tree
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
11 changes: 10 additions & 1 deletion src/coreclr/jit/assertionprop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2805,7 +2805,16 @@ GenTree* Compiler::optVNConstantPropOnTree(BasicBlock* block, GenTree* parent, G
{
if (tree->TypeGet() == TYP_REF)
{
conValTree = gtNewIconNode(vnStore->ConstantValue<size_t>(vnCns), TYP_REF);
const size_t value = vnStore->ConstantValue<size_t>(vnCns);
if (value == 0)
{
conValTree = gtNewNull();
}
else
{
assert(vnStore->IsVNObjHandle(vnCns));
conValTree = gtNewIconHandleNode(value, GTF_ICON_OBJ_HDL);
}
}
}
break;
Expand Down
10 changes: 9 additions & 1 deletion src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -2951,7 +2951,12 @@ class Compiler

GenTree* gtNewIndOfIconHandleNode(var_types indType, size_t addr, GenTreeFlags iconFlags, bool isInvariant);

GenTreeIntCon* gtNewIconHandleNode(size_t value, GenTreeFlags flags, FieldSeq* fields = nullptr);
GenTreeIntCon* gtNewIconHandleNode(size_t value, GenTreeFlags flags, FieldSeq* fields = nullptr);

static var_types gtGetTypeForIconFlags(GenTreeFlags flags)
{
return flags == GTF_ICON_OBJ_HDL ? TYP_REF : TYP_I_IMPL;
}

GenTreeFlags gtTokenToIconFlags(unsigned token);

Expand Down Expand Up @@ -5602,6 +5607,9 @@ class Compiler
// Does value-numbering for a call. We interpret some helper calls.
void fgValueNumberCall(GenTreeCall* call);

// Does value-numbering for a special intrinsic call.
bool fgValueNumberSpecialIntrinsic(GenTreeCall* call);

// Does value-numbering for a helper representing a cast operation.
void fgValueNumberCastHelper(GenTreeCall* call);

Expand Down
5 changes: 3 additions & 2 deletions src/coreclr/jit/compiler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1324,9 +1324,10 @@ inline GenTreeIntCon* Compiler::gtNewIconHandleNode(size_t value, GenTreeFlags f

GenTreeIntCon* node;
#if defined(LATE_DISASM)
node = new (this, LargeOpOpcode()) GenTreeIntCon(TYP_I_IMPL, value, fields DEBUGARG(/*largeNode*/ true));
node = new (this, LargeOpOpcode())
GenTreeIntCon(gtGetTypeForIconFlags(flags), value, fields DEBUGARG(/*largeNode*/ true));
#else
node = new (this, GT_CNS_INT) GenTreeIntCon(TYP_I_IMPL, value, fields);
node = new (this, GT_CNS_INT) GenTreeIntCon(gtGetTypeForIconFlags(flags), value, fields);
Comment on lines 1326 to +1330
Copy link
Member

Choose a reason for hiding this comment

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

Unrelated, but any idea why this needs a large node for LATE_DISASM?

Copy link
Contributor

@SingleAccretion SingleAccretion Feb 7, 2024

Choose a reason for hiding this comment

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

Pretty sure it's dead code.

Copy link
Member

Choose a reason for hiding this comment

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

We revived LATE_DISASM for use in SVE emitter unit tests in #96286, so it's not dead anymore.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, but I don't think this large opcode ifdefing is still needed. It seems in the past gtCompileTimeHandle was part of the requirement for a "large" node:

runtime/src/coreclr/jit/morph.cpp

Lines 12663 to 12669 in 24d149c

#if defined(LATE_DISASM)
// GT_CNS_INT is considered small, so ReplaceWith() won't copy all fields
if (tree->IsIconHandle())
{
copy->AsIntCon()->gtCompileTimeHandle = tree->AsIntCon()->gtCompileTimeHandle;
}
#endif

#endif
node->gtFlags |= flags;
return node;
Expand Down
24 changes: 17 additions & 7 deletions src/coreclr/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2124,8 +2124,12 @@ regMaskTP GenTreeCall::GetOtherRegMask() const
//
bool GenTreeCall::IsPure(Compiler* compiler) const
{
return (gtCallType == CT_HELPER) &&
compiler->s_helperCallProperties.IsPure(compiler->eeGetHelperNum(gtCallMethHnd));
if (IsHelperCall())
{
return compiler->s_helperCallProperties.IsPure(compiler->eeGetHelperNum(gtCallMethHnd));
}
// If needed, we can annotate other special intrinsic methods as pure as well.
return IsSpecialIntrinsic(compiler, NI_System_Type_GetTypeFromHandle);
}

//------------------------------------------------------------------------------
Expand Down Expand Up @@ -2308,6 +2312,12 @@ bool GenTreeCall::HasSideEffects(Compiler* compiler, bool ignoreExceptions, bool
// calls that can prove them side-effect-free.
if (gtCallType != CT_HELPER)
{
// If needed, we can annotate other special intrinsic methods as side effect free as well.
if (IsSpecialIntrinsic(compiler, NI_System_Type_GetTypeFromHandle))
{
return false;
}

return true;
}

Expand Down Expand Up @@ -7702,8 +7712,11 @@ GenTree* Compiler::gtNewIconEmbHndNode(void* value, void* pValue, GenTreeFlags i
{
iconNode->gtTargetHandle = (size_t)compileTimeHandle;
}
if (iconFlags == GTF_ICON_OBJ_HDL)
{
iconNode->gtTargetHandle = (size_t)value;
}
#endif

return handleNode;
}

Expand All @@ -7716,8 +7729,7 @@ GenTree* Compiler::gtNewStringLiteralNode(InfoAccessType iat, void* pValue)
{
case IAT_VALUE:
setMethodHasFrozenObjects();
tree = gtNewIconEmbHndNode(pValue, nullptr, GTF_ICON_OBJ_HDL, nullptr);
tree->gtType = TYP_REF;
tree = gtNewIconEmbHndNode(pValue, nullptr, GTF_ICON_OBJ_HDL, nullptr);
#ifdef DEBUG
tree->AsIntCon()->gtTargetHandle = (size_t)pValue;
#endif
Expand Down Expand Up @@ -8074,8 +8086,6 @@ GenTree* Compiler::gtNewGenericCon(var_types type, uint8_t* cnsVal)
// setMethodHasFrozenObjects here to make caller's life easier.
setMethodHasFrozenObjects();
GenTree* tree = gtNewIconEmbHndNode((void*)val, nullptr, GTF_ICON_OBJ_HDL, nullptr);
tree->gtType = TYP_REF;
INDEBUG(tree->AsIntCon()->gtTargetHandle = val);
return tree;
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/coreclr/jit/importercalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3216,6 +3216,11 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis,
op1->gtType = TYP_REF;
retNode = op1;
}
else if (GetRuntimeHandleUnderlyingType() == TYP_I_IMPL)
{
// We'll try to expand it later.
isSpecial = true;
}
break;
}

Expand Down
2 changes: 0 additions & 2 deletions src/coreclr/jit/morph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7757,8 +7757,6 @@ GenTree* Compiler::fgMorphCall(GenTreeCall* call)
{
setMethodHasFrozenObjects();
GenTree* retNode = gtNewIconEmbHndNode((void*)ptr, nullptr, GTF_ICON_OBJ_HDL, nullptr);
retNode->gtType = TYP_REF;
INDEBUG(retNode->AsIntCon()->gtTargetHandle = (size_t)ptr);
return fgMorphTree(retNode);
}
}
Expand Down
117 changes: 102 additions & 15 deletions src/coreclr/jit/valuenum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2033,18 +2033,17 @@ ValueNum ValueNumStore::VNForHandle(ssize_t cnsVal, GenTreeFlags handleFlags)
{
return res;
}
else
{
Chunk* const c = GetAllocChunk(TYP_I_IMPL, CEA_Handle);
unsigned const offsetWithinChunk = c->AllocVN();
VNHandle* const chunkSlots = reinterpret_cast<VNHandle*>(c->m_defs);

chunkSlots[offsetWithinChunk] = handle;
res = c->m_baseVN + offsetWithinChunk;
var_types type = Compiler::gtGetTypeForIconFlags(handleFlags);
Chunk* const c = GetAllocChunk(type, CEA_Handle);
unsigned const offsetWithinChunk = c->AllocVN();
VNHandle* const chunkSlots = reinterpret_cast<VNHandle*>(c->m_defs);

GetHandleMap()->Set(handle, res);
return res;
}
chunkSlots[offsetWithinChunk] = handle;
res = c->m_baseVN + offsetWithinChunk;

GetHandleMap()->Set(handle, res);
return res;
}

ValueNum ValueNumStore::VNZeroForType(var_types typ)
Expand Down Expand Up @@ -6119,6 +6118,11 @@ bool ValueNumStore::IsVNObjHandle(ValueNum vn)
return IsVNHandle(vn) && (GetHandleFlags(vn) == GTF_ICON_OBJ_HDL);
}

bool ValueNumStore::IsVNTypeHandle(ValueNum vn)
{
return IsVNHandle(vn) && (GetHandleFlags(vn) == GTF_ICON_CLASS_HDL);
}

//------------------------------------------------------------------------
// SwapRelop: return VNFunc for swapped relop
//
Expand Down Expand Up @@ -12542,6 +12546,66 @@ void Compiler::fgValueNumberHelperCallFunc(GenTreeCall* call, VNFunc vnf, ValueN
// not care.
}

//--------------------------------------------------------------------------------
// fgValueNumberSpecialIntrinsic: Value number a special intrinsic
//
// Arguments:
// call - The call node with the special intrinsic flag set
//
// Return Value:
// true if the intrinsic was handled, false otherwise (needs to be handled just like a regular call)
//
bool Compiler::fgValueNumberSpecialIntrinsic(GenTreeCall* call)
{
assert(call->IsSpecialIntrinsic());

switch (lookupNamedIntrinsic(call->gtCallMethHnd))
{
case NI_System_Type_GetTypeFromHandle:
{
// Optimize Type.GetTypeFromHandle(TypeHandleToRuntimeTypeHandle(clsHandle)) to a frozen handle.
// This, technically, works regardless of whether RuntimeTypeHandle is RuntimeType-based wrapper
// or not, but we expect this to be hit only for the TYP_I_IMPL case (NativeAOT).
assert(GetRuntimeHandleUnderlyingType() == TYP_I_IMPL);

VNFuncApp bitcastFuncApp;
ValueNum argVN = call->gtArgs.GetArgByIndex(0)->GetNode()->gtVNPair.GetConservative();
if (!vnStore->GetVNFunc(argVN, &bitcastFuncApp) || (bitcastFuncApp.m_func != VNF_BitCast))
{
break;
}

VNFuncApp typeHandleFuncApp;
if (!vnStore->GetVNFunc(bitcastFuncApp.m_args[0], &typeHandleFuncApp) ||
(typeHandleFuncApp.m_func != VNF_TypeHandleToRuntimeTypeHandle) ||
!vnStore->IsVNTypeHandle(typeHandleFuncApp.m_args[0]))
{
break;
}

ValueNum clsVN = typeHandleFuncApp.m_args[0];
ssize_t clsHandle;
if (!vnStore->EmbeddedHandleMapLookup(vnStore->ConstantValue<ssize_t>(clsVN), &clsHandle))
{
break;
}

CORINFO_OBJECT_HANDLE obj = info.compCompHnd->getRuntimeTypePointer((CORINFO_CLASS_HANDLE)clsHandle);
if (obj != nullptr)
{
setMethodHasFrozenObjects();
call->gtVNPair.SetBoth(vnStore->VNForHandle((ssize_t)obj, GTF_ICON_OBJ_HDL));
return true;
}
}
break;

default:
break;
}
return false;
}

void Compiler::fgValueNumberCall(GenTreeCall* call)
{
if (call->gtCallType == CT_HELPER)
Expand All @@ -12556,17 +12620,20 @@ void Compiler::fgValueNumberCall(GenTreeCall* call)
}
else
{
if (call->TypeGet() == TYP_VOID)
if (call->TypeIs(TYP_VOID))
{
call->gtVNPair.SetBoth(ValueNumStore::VNForVoid());

// For now, arbitrary side effect on GcHeap/ByrefExposed.
fgMutateGcHeap(call DEBUGARG("CALL"));
}
else
else if (!call->IsSpecialIntrinsic() || !fgValueNumberSpecialIntrinsic(call))
{
call->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, call->TypeGet()));
}

// For now, arbitrary side effect on GcHeap/ByrefExposed.
fgMutateGcHeap(call DEBUGARG("CALL"));
// For now, arbitrary side effect on GcHeap/ByrefExposed.
fgMutateGcHeap(call DEBUGARG("CALL"));
}
}

// If the call generates a definition, because it uses "return buffer", then VN the local
Expand Down Expand Up @@ -12947,6 +13014,26 @@ bool Compiler::fgValueNumberHelperCall(GenTreeCall* call)
fgValueNumberCastHelper(call);
return false;

case CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE:
{
// If RuntimeTypeHandle is just a wrapper around RuntimeType object, we can
// just number it as BitCast<TYP_STRUCT>(nonGcRuntimeTypeObj);
const ValueNum argVN = call->gtArgs.GetArgByIndex(0)->GetNode()->gtVNPair.GetConservative();
if ((GetRuntimeHandleUnderlyingType() == TYP_REF) && vnStore->IsVNTypeHandle(argVN))
{
CORINFO_CLASS_HANDLE cls = (CORINFO_CLASS_HANDLE)vnStore->ConstantValue<ssize_t>(argVN);
CORINFO_OBJECT_HANDLE typeObj = info.compCompHnd->getRuntimeTypePointer(cls);
if (typeObj != nullptr)
{
setMethodHasFrozenObjects();
ValueNum typeObjVN = vnStore->VNForHandle((ssize_t)typeObj, GTF_ICON_OBJ_HDL);
call->gtVNPair.SetBoth(vnStore->VNForBitCast(typeObjVN, TYP_STRUCT, genTypeSize(TYP_REF)));
return false;
}
}
}
break;

default:
break;
}
Expand Down
10 changes: 9 additions & 1 deletion src/coreclr/jit/valuenum.h
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,11 @@ class ValueNumStore
m_embeddedToCompileTimeHandleMap.AddOrUpdate(embeddedHandle, compileTimeHandle);
}

bool EmbeddedHandleMapLookup(ssize_t embeddedHandle, ssize_t* compileTimeHandle)
{
return m_embeddedToCompileTimeHandleMap.TryGetValue(embeddedHandle, compileTimeHandle);
}

void AddToFieldAddressToFieldSeqMap(ValueNum fldAddr, FieldSeq* fldSeq)
{
m_fieldAddressToFieldSeqMap.AddOrUpdate(fldAddr, fldSeq);
Expand Down Expand Up @@ -1023,6 +1028,9 @@ class ValueNumStore
// Returns true iff the VN represents an object handle constant.
bool IsVNObjHandle(ValueNum vn);

// Returns true iff the VN represents a Type handle constant.
bool IsVNTypeHandle(ValueNum vn);

// Returns true iff the VN represents a relop
bool IsVNRelop(ValueNum vn);

Expand Down Expand Up @@ -1084,7 +1092,7 @@ class ValueNumStore
switch (c->m_typ)
{
case TYP_REF:
assert(0 <= offset && offset <= 1); // Null or exception.
assert((offset <= 1) || IsVNObjHandle(vn)); // Null, exception or nongc obj handle
FALLTHROUGH;

case TYP_BYREF:
Expand Down