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

[release/6.0] Fix VN incorrect optimizations with a new JitEEInterface function. #58005

Merged
merged 10 commits into from
Aug 27, 2021
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
1 change: 1 addition & 0 deletions src/coreclr/ToolBox/superpmi/superpmi-shared/lwmlist.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ LWM(TryResolveToken, Agnostic_CORINFO_RESOLVED_TOKENin, TryResolveTokenValue)
LWM(SatisfiesClassConstraints, DWORDLONG, DWORD)
LWM(SatisfiesMethodConstraints, DLDL, DWORD)
LWM(GetUnmanagedCallConv, MethodOrSigInfoValue, DD)
LWM(DoesFieldBelongToClass, DLDL, DWORD)
DENSELWM(SigInstHandleMap, DWORDLONG)

#undef LWM
Expand Down
34 changes: 34 additions & 0 deletions src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6265,6 +6265,40 @@ DWORD MethodContext::repGetExpectedTargetArchitecture()
return value;
}

void MethodContext::recDoesFieldBelongToClass(CORINFO_FIELD_HANDLE fld, CORINFO_CLASS_HANDLE cls, bool result)
{
if (DoesFieldBelongToClass == nullptr)
DoesFieldBelongToClass = new LightWeightMap<DLDL, DWORD>();

DLDL key;
ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
key.A = CastHandle(fld);
key.B = CastHandle(cls);

DWORD value = (DWORD)result;
DoesFieldBelongToClass->Add(key, value);
DEBUG_REC(dmpDoesFieldBelongToClass(key, result));
}

void MethodContext::dmpDoesFieldBelongToClass(DLDL key, bool value)
{
printf("DoesFieldBelongToClass key fld=%016llX, cls=%016llx, result=%d", key.A, key.B, value);
}

bool MethodContext::repDoesFieldBelongToClass(CORINFO_FIELD_HANDLE fld, CORINFO_CLASS_HANDLE cls)
{
DLDL key;
ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
key.A = CastHandle(fld);
key.B = CastHandle(cls);

AssertMapAndKeyExist(DoesFieldBelongToClass, key, ": key %016llX %016llX", key.A, key.B);

bool value = (bool)DoesFieldBelongToClass->Get(key);
DEBUG_REP(dmpDoesFieldBelongToClass(key, value));
return value;
}

void MethodContext::recIsValidToken(CORINFO_MODULE_HANDLE module, unsigned metaTOK, bool result)
{
if (IsValidToken == nullptr)
Expand Down
6 changes: 5 additions & 1 deletion src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,10 @@ class MethodContext
void dmpGetExpectedTargetArchitecture(DWORD key, DWORD result);
DWORD repGetExpectedTargetArchitecture();

void recDoesFieldBelongToClass(CORINFO_FIELD_HANDLE fld, CORINFO_CLASS_HANDLE cls, bool result);
void dmpDoesFieldBelongToClass(DLDL key, bool value);
bool repDoesFieldBelongToClass(CORINFO_FIELD_HANDLE fld, CORINFO_CLASS_HANDLE cls);

void recIsValidToken(CORINFO_MODULE_HANDLE module, unsigned metaTOK, bool result);
void dmpIsValidToken(DLD key, DWORD value);
bool repIsValidToken(CORINFO_MODULE_HANDLE module, unsigned metaTOK);
Expand Down Expand Up @@ -1063,7 +1067,7 @@ enum mcPackets
Packet_TryResolveToken = 158, // Added 4/26/2016
Packet_SatisfiesClassConstraints = 110,
Packet_SatisfiesMethodConstraints = 111,
Packet_ShouldEnforceCallvirtRestriction = 112, // Retired 2/18/2020
Packet_DoesFieldBelongToClass = 112, // Added 8/12/2021
Packet_SigInstHandleMap = 184,
Packet_AllocPgoInstrumentationBySchema = 186, // Added 1/4/2021
Packet_GetPgoInstrumentationResults = 187, // Added 1/4/2021
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2010,3 +2010,13 @@ bool interceptor_ICJI::notifyInstructionSetUsage(CORINFO_InstructionSet instruct
{
return original_ICorJitInfo->notifyInstructionSetUsage(instructionSet, supported);
}

bool interceptor_ICJI::doesFieldBelongToClass(
CORINFO_FIELD_HANDLE fldHnd,
CORINFO_CLASS_HANDLE cls)
{
mc->cr->AddCall("doesFieldBelongToClass");
bool result = original_ICorJitInfo->doesFieldBelongToClass(fldHnd, cls);
mc->recDoesFieldBelongToClass(fldHnd, cls, result);
return result;
}
Original file line number Diff line number Diff line change
Expand Up @@ -1389,3 +1389,11 @@ uint32_t interceptor_ICJI::getJitFlags(
return original_ICorJitInfo->getJitFlags(flags, sizeInBytes);
}

bool interceptor_ICJI::doesFieldBelongToClass(
CORINFO_FIELD_HANDLE fldHnd,
CORINFO_CLASS_HANDLE cls)
{
mcs->AddCall("doesFieldBelongToClass");
return original_ICorJitInfo->doesFieldBelongToClass(fldHnd, cls);
}

Original file line number Diff line number Diff line change
Expand Up @@ -1217,3 +1217,10 @@ uint32_t interceptor_ICJI::getJitFlags(
return original_ICorJitInfo->getJitFlags(flags, sizeInBytes);
}

bool interceptor_ICJI::doesFieldBelongToClass(
CORINFO_FIELD_HANDLE fldHnd,
CORINFO_CLASS_HANDLE cls)
{
return original_ICorJitInfo->doesFieldBelongToClass(fldHnd, cls);
}

7 changes: 7 additions & 0 deletions src/coreclr/ToolBox/superpmi/superpmi/icorjitinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1574,6 +1574,13 @@ uint32_t MyICJI::getJitFlags(CORJIT_FLAGS* jitFlags, uint32_t sizeInBytes)
return ret;
}

bool MyICJI::doesFieldBelongToClass(CORINFO_FIELD_HANDLE fldHnd, CORINFO_CLASS_HANDLE cls)
{
jitInstance->mc->cr->AddCall("doesFieldBelongToClass");
bool result = jitInstance->mc->repDoesFieldBelongToClass(fldHnd, cls);
return result;
}

// Runs the given function with the given parameter under an error trap
// and returns true if the function completes successfully. We fake this
// up a bit for SuperPMI and simply catch all exceptions.
Expand Down
6 changes: 6 additions & 0 deletions src/coreclr/inc/corjit.h
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,12 @@ class ICorJitInfo : public ICorDynamicInfo
uint32_t sizeInBytes /* IN: The size of the buffer. Note that this is effectively a
version number for the CORJIT_FLAGS value. */
) = 0;

// Checks if a field belongs to a given class.
virtual bool doesFieldBelongToClass(
CORINFO_FIELD_HANDLE fldHnd, /* IN: the field that we are checking */
CORINFO_CLASS_HANDLE cls /* IN: the class that we are checking */
) = 0;
};

/**********************************************************************************/
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/inc/icorjitinfoimpl_generated.h
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,10 @@ uint32_t getJitFlags(
CORJIT_FLAGS* flags,
uint32_t sizeInBytes) override;

bool doesFieldBelongToClass(
CORINFO_FIELD_HANDLE fldHnd,
CORINFO_CLASS_HANDLE cls) override;

/**********************************************************************************/
// clang-format on
/**********************************************************************************/
12 changes: 6 additions & 6 deletions src/coreclr/inc/jiteeversionguid.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,12 @@ typedef const GUID *LPCGUID;
#define GUID_DEFINED
#endif // !GUID_DEFINED

constexpr GUID JITEEVersionIdentifier = { /* c2e4a466-795d-4e64-a776-0ae7b2eed65b */
0xc2e4a466,
0x795d,
0x4e64,
{0xa7, 0x76, 0x0a, 0xe7, 0xb2, 0xee, 0xd6, 0x5b}
};
constexpr GUID JITEEVersionIdentifier = { /* 5ed35c58-857b-48dd-a818-7c0136dc9f73 */
0x5ed35c58,
0x857b,
0x48dd,
{0xa8, 0x18, 0x7c, 0x01, 0x36, 0xdc, 0x9f, 0x73}
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////
//
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/jit/ICorJitInfo_API_names.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,5 +176,6 @@ DEF_CLR_API(recordRelocation)
DEF_CLR_API(getRelocTypeHint)
DEF_CLR_API(getExpectedTargetArchitecture)
DEF_CLR_API(getJitFlags)
DEF_CLR_API(doesFieldBelongToClass)

#undef DEF_CLR_API
10 changes: 10 additions & 0 deletions src/coreclr/jit/ICorJitInfo_API_wrapper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1690,6 +1690,16 @@ uint32_t WrapICorJitInfo::getJitFlags(
return temp;
}

bool WrapICorJitInfo::doesFieldBelongToClass(
CORINFO_FIELD_HANDLE fldHnd,
CORINFO_CLASS_HANDLE cls)
{
API_ENTER(doesFieldBelongToClass);
bool temp = wrapHnd->doesFieldBelongToClass(fldHnd, cls);
API_LEAVE(doesFieldBelongToClass);
return temp;
}

/**********************************************************************************/
// clang-format on
/**********************************************************************************/
2 changes: 2 additions & 0 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -5204,6 +5204,8 @@ class Compiler
// Does value-numbering for a block assignment.
void fgValueNumberBlockAssignment(GenTree* tree);

bool fgValueNumberIsStructReinterpretation(GenTreeLclVarCommon* lhsLclVarTree, GenTreeLclVarCommon* rhsLclVarTree);

// Does value-numbering for a cast tree.
void fgValueNumberCastTree(GenTree* tree);

Expand Down
39 changes: 0 additions & 39 deletions src/coreclr/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17275,45 +17275,6 @@ bool Compiler::impReturnInstruction(int prefixFlags, OPCODE& opcode)
#endif
}

// If we are inlining a method that returns a struct byref, check whether we are "reinterpreting" the
// struct.
GenTree* effectiveRetVal = op2->gtEffectiveVal();
if ((returnType == TYP_BYREF) && (info.compRetType == TYP_BYREF) &&
(effectiveRetVal->OperGet() == GT_ADDR))
{
GenTree* addrChild = effectiveRetVal->gtGetOp1();
if (addrChild->OperGet() == GT_LCL_VAR)
{
LclVarDsc* varDsc = lvaGetDesc(addrChild->AsLclVarCommon());

if (varTypeIsStruct(addrChild->TypeGet()) && !isOpaqueSIMDLclVar(varDsc))
{
CORINFO_CLASS_HANDLE referentClassHandle;
CorInfoType referentType =
info.compCompHnd->getChildType(info.compMethodInfo->args.retTypeClass,
&referentClassHandle);
if (varTypeIsStruct(JITtype2varType(referentType)) &&
(varDsc->GetStructHnd() != referentClassHandle))
{
// We are returning a byref to struct1; the method signature specifies return type as
// byref
// to struct2. struct1 and struct2 are different so we are "reinterpreting" the struct.
// This may happen in, for example, System.Runtime.CompilerServices.Unsafe.As<TFrom,
// TTo>.
// We need to mark the source struct variable as having overlapping fields because its
// fields may be accessed using field handles of a different type, which may confuse
// optimizations, in particular, value numbering.

JITDUMP("\nSetting lvOverlappingFields to true on V%02u because of struct "
"reinterpretation\n",
addrChild->AsLclVarCommon()->GetLclNum());

varDsc->lvOverlappingFields = true;
}
}
}
}

#ifdef DEBUG
if (verbose)
{
Expand Down
75 changes: 71 additions & 4 deletions src/coreclr/jit/lclmorph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ class LocalAddressVisitor final : public GenTreeVisitor<LocalAddressVisitor>
// Arguments:
// val - the input value
// field - the FIELD node that uses the input address value
// fieldSeqStore - the compiler's field sequence store
// compiler - the compiler instance
//
// Return Value:
// `true` if the value was consumed. `false` if the input value
Expand All @@ -224,8 +224,9 @@ class LocalAddressVisitor final : public GenTreeVisitor<LocalAddressVisitor>
// if the offset overflows then location is not representable, must escape
// - UNKNOWN => UNKNOWN
//
bool Field(Value& val, GenTreeField* field, FieldSeqStore* fieldSeqStore)
bool Field(Value& val, GenTreeField* field, Compiler* compiler)
{

assert(!IsLocation() && !IsAddress());

if (val.IsLocation())
Expand All @@ -246,14 +247,80 @@ class LocalAddressVisitor final : public GenTreeVisitor<LocalAddressVisitor>
m_lclNum = val.m_lclNum;
m_offset = newOffset.Value();

bool haveCorrectFieldForVN;
if (field->gtFldMayOverlap)
{
m_fieldSeq = FieldSeqStore::NotAField();
haveCorrectFieldForVN = false;
}
else
{
LclVarDsc* varDsc = compiler->lvaGetDesc(m_lclNum);
if (!varTypeIsStruct(varDsc))
{
haveCorrectFieldForVN = false;
}
else if (FieldSeqStore::IsPseudoField(field->gtFldHnd))
{
assert(compiler->compObjectStackAllocation());
// We use PseudoFields when accessing stack allocated classes.
haveCorrectFieldForVN = false;
}
else if (val.m_fieldSeq == nullptr)
{

CORINFO_CLASS_HANDLE clsHnd = varDsc->GetStructHnd();
// If the answer is no we are probably accessing a canon type with a non-canon fldHnd,
// currently it could happen in crossgen2 scenario where VM distinguishes class<canon>._field
// from class<not-canon-ref-type>._field.
haveCorrectFieldForVN =
compiler->info.compCompHnd->doesFieldBelongToClass(field->gtFldHnd, clsHnd);
}
else
{
FieldSeqNode* lastSeqNode = val.m_fieldSeq->GetTail();
assert(lastSeqNode != nullptr);
if (lastSeqNode->IsPseudoField() || lastSeqNode == FieldSeqStore::NotAField())
{
haveCorrectFieldForVN = false;
}
else
{
CORINFO_FIELD_HANDLE lastFieldBeforeTheCurrent = lastSeqNode->GetFieldHandle();

CORINFO_CLASS_HANDLE clsHnd;
CorInfoType fieldCorType =
compiler->info.compCompHnd->getFieldType(lastFieldBeforeTheCurrent, &clsHnd);
if (fieldCorType != CORINFO_TYPE_VALUECLASS)
{
// For example, System.IntPtr:ToInt64, when inlined, creates trees like
// * FIELD long _value
// \--* ADDR byref
// \--* FIELD long Information
// \--* ADDR byref
// \--* LCL_VAR struct<Interop+NtDll+IO_STATUS_BLOCK, 16> V08 tmp7
haveCorrectFieldForVN = false;
}
else
{

haveCorrectFieldForVN =
compiler->info.compCompHnd->doesFieldBelongToClass(field->gtFldHnd, clsHnd);
noway_assert(haveCorrectFieldForVN);
Copy link
Contributor

Choose a reason for hiding this comment

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

The new method doesFieldBelongToClass has to return true here otherwise we will terminate the program.

The only place that doesFieldBelongToClass can return false is above at line 276.

Copy link
Contributor

Choose a reason for hiding this comment

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

we won't terminate the program, we will terminate the current method compilation and recompile in minopts.

}
}
}
}

if (haveCorrectFieldForVN)
{
FieldSeqStore* fieldSeqStore = compiler->GetFieldSeqStore();
m_fieldSeq = fieldSeqStore->Append(val.m_fieldSeq, fieldSeqStore->CreateSingleton(field->gtFldHnd));
}
else
{
m_fieldSeq = FieldSeqStore::NotAField();
JITDUMP("Setting NotAField for [%06u],\n", compiler->dspTreeID(field));
}
}

INDEBUG(val.Consume();)
Expand Down Expand Up @@ -463,7 +530,7 @@ class LocalAddressVisitor final : public GenTreeVisitor<LocalAddressVisitor>
assert(TopValue(1).Node() == node);
assert(TopValue(0).Node() == node->AsField()->gtFldObj);

if (!TopValue(1).Field(TopValue(0), node->AsField(), m_compiler->GetFieldSeqStore()))
if (!TopValue(1).Field(TopValue(0), node->AsField(), m_compiler))
{
// Either the address comes from a location value (e.g. FIELD(IND(...)))
// or the field offset has overflowed.
Expand Down
Loading