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

Fix missing zero-offset sequences and add checking #64805

Merged
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
2 changes: 2 additions & 0 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -3625,6 +3625,7 @@ class Compiler
void gtGetArgMsg(GenTreeCall* call, GenTree* arg, unsigned argNum, char* bufp, unsigned bufLength);
void gtGetLateArgMsg(GenTreeCall* call, GenTree* arg, int argNum, char* bufp, unsigned bufLength);
void gtDispArgList(GenTreeCall* call, GenTree* lastCallOperand, IndentStack* indentStack);
void gtDispAnyFieldSeq(FieldSeqNode* fieldSeq);
void gtDispFieldSeq(FieldSeqNode* pfsn);

void gtDispRange(LIR::ReadOnlyRange const& range);
Expand Down Expand Up @@ -5573,6 +5574,7 @@ class Compiler

#ifdef DEBUG
void fgDebugCheckExceptionSets();
void fgDebugCheckValueNumberedTree(GenTree* tree);
#endif

// These are the current value number for the memory implicit variables while
Expand Down
23 changes: 21 additions & 2 deletions src/coreclr/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9178,7 +9178,7 @@ void Compiler::gtDispZeroFieldSeq(GenTree* tree)
if (map->Lookup(tree, &fldSeq))
{
printf(" Zero");
gtDispFieldSeq(fldSeq);
gtDispAnyFieldSeq(fldSeq);
}
}
}
Expand Down Expand Up @@ -10295,9 +10295,28 @@ void Compiler::gtDispConst(GenTree* tree)
}
}

//------------------------------------------------------------------------
// gtDispFieldSeq: "gtDispFieldSeq" that also prints "<NotAField>".
//
// Useful for printing zero-offset field sequences.
//
void Compiler::gtDispAnyFieldSeq(FieldSeqNode* fieldSeq)
{
if (fieldSeq == FieldSeqStore::NotAField())
{
printf(" Fseq<NotAField>");
return;
}

gtDispFieldSeq(fieldSeq);
}

//------------------------------------------------------------------------
// gtDispFieldSeq: Print out the fields in this field sequence.
//
void Compiler::gtDispFieldSeq(FieldSeqNode* pfsn)
{
if (pfsn == FieldSeqStore::NotAField() || (pfsn == nullptr))
if ((pfsn == nullptr) || (pfsn == FieldSeqStore::NotAField()))
{
return;
}
Expand Down
5 changes: 2 additions & 3 deletions src/coreclr/jit/morph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13641,8 +13641,7 @@ GenTree* Compiler::fgOptimizeAddition(GenTreeOp* add)
// TODO-Bug: this code will lose the GC-ness of a tree like "native int + byref(0)".
if (op2->IsIntegralConst(0) && ((add->TypeGet() == op1->TypeGet()) || !op1->TypeIs(TYP_REF)))
{
if (op2->IsCnsIntOrI() && (op2->AsIntCon()->gtFieldSeq != nullptr) &&
(op2->AsIntCon()->gtFieldSeq != FieldSeqStore::NotAField()))
if (op2->IsCnsIntOrI() && varTypeIsI(op1))
{
fgAddFieldSeqForZeroOffset(op1, op2->AsIntCon()->gtFieldSeq);
}
Expand Down Expand Up @@ -17845,7 +17844,7 @@ void Compiler::fgAddFieldSeqForZeroOffset(GenTree* addr, FieldSeqNode* fieldSeqZ
if (verbose)
{
printf("\nfgAddFieldSeqForZeroOffset for");
gtDispFieldSeq(fieldSeqZero);
gtDispAnyFieldSeq(fieldSeqZero);

printf("\naddr (Before)\n");
gtDispNode(addr, nullptr, nullptr, false);
Expand Down
112 changes: 98 additions & 14 deletions src/coreclr/jit/valuenum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4167,32 +4167,33 @@ ValueNum ValueNumStore::VNForFieldSeq(FieldSeqNode* fieldSeq)
{
return VNForNull();
}
else if (fieldSeq == FieldSeqStore::NotAField())

ValueNum fieldSeqVN;
if (fieldSeq == FieldSeqStore::NotAField())
{
// We always allocate a new, unique VN in this call.
Chunk* c = GetAllocChunk(TYP_REF, CEA_NotAField);
unsigned offsetWithinChunk = c->AllocVN();
ValueNum result = c->m_baseVN + offsetWithinChunk;
return result;
fieldSeqVN = c->m_baseVN + offsetWithinChunk;
}
else
{
ssize_t fieldHndVal = ssize_t(fieldSeq->m_fieldHnd);
ValueNum fieldHndVN = VNForHandle(fieldHndVal, GTF_ICON_FIELD_HDL);
ValueNum seqNextVN = VNForFieldSeq(fieldSeq->m_next);
ValueNum fieldSeqVN = VNForFunc(TYP_REF, VNF_FieldSeq, fieldHndVN, seqNextVN);
fieldSeqVN = VNForFunc(TYP_REF, VNF_FieldSeq, fieldHndVN, seqNextVN);
}

#ifdef DEBUG
if (m_pComp->verbose)
{
printf(" FieldSeq");
vnDump(m_pComp, fieldSeqVN);
printf(" is " FMT_VN "\n", fieldSeqVN);
}
if (m_pComp->verbose)
{
printf(" FieldSeq");
vnDump(m_pComp, fieldSeqVN);
printf(" is " FMT_VN "\n", fieldSeqVN);
}
#endif

return fieldSeqVN;
}
return fieldSeqVN;
}

FieldSeqNode* ValueNumStore::FieldSeqVNToFieldSeq(ValueNum vn)
Expand Down Expand Up @@ -5961,6 +5962,7 @@ void ValueNumStore::vnDump(Compiler* comp, ValueNum vn, bool isPtr)
switch (funcApp.m_func)
{
case VNF_FieldSeq:
case VNF_NotAField:
vnDumpFieldSeq(comp, &funcApp, true);
break;
case VNF_MapSelect:
Expand Down Expand Up @@ -6068,6 +6070,12 @@ void ValueNumStore::vnDumpExcSeq(Compiler* comp, VNFuncApp* excSeq, bool isHead)

void ValueNumStore::vnDumpFieldSeq(Compiler* comp, VNFuncApp* fieldSeq, bool isHead)
{
if (fieldSeq->m_func == VNF_NotAField)
{
printf("<NotAField>");
return;
}

assert(fieldSeq->m_func == VNF_FieldSeq); // Precondition.
// First arg is the field handle VN.
assert(IsVNConstant(fieldSeq->m_args[0]) && TypeOfVN(fieldSeq->m_args[0]) == TYP_I_IMPL);
Expand Down Expand Up @@ -8592,12 +8600,21 @@ void Compiler::fgValueNumberTree(GenTree* tree)
newVN = vnStore->VNForExpr(compCurBB, TYP_BYREF);
}
}

if (newVN == ValueNumStore::NoVN)
{
// We may have a zero-offset field sequence on this ADDR.
FieldSeqNode* zeroOffsetFieldSeq = nullptr;
if (GetZeroOffsetFieldMap()->Lookup(tree, &zeroOffsetFieldSeq))
{
fieldSeq = GetFieldSeqStore()->Append(fieldSeq, zeroOffsetFieldSeq);
}

newVN = vnStore->VNForFunc(TYP_BYREF, VNF_PtrToLoc,
vnStore->VNForIntCon(arg->AsLclVarCommon()->GetLclNum()),
vnStore->VNForFieldSeq(fieldSeq));
}

tree->gtVNPair.SetBoth(newVN);
}
else if ((arg->gtOper == GT_IND) || arg->OperIsBlk())
Expand All @@ -8608,8 +8625,7 @@ void Compiler::fgValueNumberTree(GenTree* tree)

ValueNumPair addrVNP = ValueNumPair();
FieldSeqNode* zeroOffsetFieldSeq = nullptr;
if (GetZeroOffsetFieldMap()->Lookup(tree, &zeroOffsetFieldSeq) &&
(zeroOffsetFieldSeq != FieldSeqStore::NotAField()))
if (GetZeroOffsetFieldMap()->Lookup(tree, &zeroOffsetFieldSeq))
{
ValueNum addrExtended = vnStore->ExtendPtrVN(arg->AsIndir()->Addr(), zeroOffsetFieldSeq);
if (addrExtended != ValueNumStore::NoVN)
Expand Down Expand Up @@ -9270,6 +9286,8 @@ void Compiler::fgValueNumberTree(GenTree* tree)
printf("\n");
}
}

fgDebugCheckValueNumberedTree(tree);
#endif // DEBUG
}

Expand Down Expand Up @@ -10965,6 +10983,72 @@ void Compiler::fgDebugCheckExceptionSets()
}
}

//------------------------------------------------------------------------
// fgDebugCheckValueNumberedTree: Verify proper numbering for "tree".
//
// Currently only checks that we have not forgotten to add a zero-offset
// field sequence to "tree"'s value number.
//
// Arguments:
// tree - The tree, that has just been numbered, to check
//
void Compiler::fgDebugCheckValueNumberedTree(GenTree* tree)
{
FieldSeqNode* zeroOffsetFldSeq;
if (GetZeroOffsetFieldMap()->Lookup(tree, &zeroOffsetFldSeq))
{
// Empty field sequences should never be recorded in the map.
assert(zeroOffsetFldSeq != nullptr);

ValueNum vns[] = {tree->GetVN(VNK_Liberal), tree->GetVN(VNK_Conservative)};
for (ValueNum vn : vns)
{
VNFuncApp vnFunc;
if (vnStore->GetVNFunc(vn, &vnFunc))
{
FieldSeqNode* fullFldSeq;
switch (vnFunc.m_func)
{
case VNF_PtrToLoc:
case VNF_PtrToStatic:
fullFldSeq = vnStore->FieldSeqVNToFieldSeq(vnFunc.m_args[1]);
break;

case VNF_PtrToArrElem:
fullFldSeq = vnStore->FieldSeqVNToFieldSeq(vnFunc.m_args[3]);
break;

default:
continue;
}

// Verify that the "fullFldSeq" we have just collected is of the
// form "[outer fields, zeroOffsetFldSeq]", or is "NotAField".
if (fullFldSeq == FieldSeqStore::NotAField())
{
continue;
}

// This check relies on the canonicality of field sequences.
FieldSeqNode* fldSeq = fullFldSeq;
bool zeroOffsetFldSeqFound = false;
while (fldSeq != nullptr)
{
if (fldSeq == zeroOffsetFldSeq)
{
zeroOffsetFldSeqFound = true;
break;
}

fldSeq = fldSeq->m_next;
}

assert(zeroOffsetFldSeqFound);
}
}
}
}

// This method asserts that SSA name constraints specified are satisfied.
// Until we figure out otherwise, all VN's are assumed to be liberal.
// TODO-Cleanup: new JitTestLabels for lib vs cons vs both VN classes?
Expand Down
102 changes: 102 additions & 0 deletions src/tests/JIT/opt/ValueNumbering/ZeroOffsetFieldSeqs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;

class ZeroOffsetFieldSeqs
{
private static UnionStruct s_union;

public static int Main()
{
if (ProblemWithArrayUnions(new UnionStruct[] { default }))
{
return 101;
}

if (ProblemWithStaticUnions())
{
return 102;
}

if (AnotherProblemWithArrayUnions(new UnionStruct[] { default }))
{
return 103;
}

return 100;
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static bool ProblemWithArrayUnions(UnionStruct[] a)
{
if (a[0].UnionOne.UnionOneFldTwo == 0)
{
a[0].UnionTwo.UnionTwoFldTwo = 1;
if (a[0].UnionOne.UnionOneFldTwo == 0)
{
return true;
}
}

return false;
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static bool ProblemWithStaticUnions()
{
if (s_union.UnionOne.UnionOneFldTwo == 0)
{
s_union.UnionTwo.UnionTwoFldTwo = 1;
if (s_union.UnionOne.UnionOneFldTwo == 0)
{
return true;
}
}

return false;
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static bool AnotherProblemWithArrayUnions(UnionStruct[] a)
{
ref var p1 = ref a[0];
ref var p1a = ref Unsafe.Add(ref p1, 0).UnionOne;
ref var p1b = ref Unsafe.Add(ref p1, 0).UnionTwo;

if (p1a.UnionOneFldTwo == 0)
{
p1b.UnionTwoFldTwo = 1;
if (p1a.UnionOneFldTwo == 0)
{
return true;
}
}

return false;
}
}

[StructLayout(LayoutKind.Explicit)]
struct UnionStruct
{
[FieldOffset(0)]
public UnionPartOne UnionOne;
[FieldOffset(0)]
public UnionPartTwo UnionTwo;
}

struct UnionPartOne
{
public long UnionOneFldOne;
public long UnionOneFldTwo;
}

struct UnionPartTwo
{
public long UnionTwoFldOne;
public long UnionTwoFldTwo;
}

12 changes: 12 additions & 0 deletions src/tests/JIT/opt/ValueNumbering/ZeroOffsetFieldSeqs.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
</PropertyGroup>
<PropertyGroup>
<DebugType>None</DebugType>
<Optimize>True</Optimize>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildProjectName).cs" />
</ItemGroup>
</Project>