Skip to content

Commit 1ea9cf4

Browse files
Fix missing zero-offset sequences and add checking (#64805)
* Add checking for zero-offset FldSeq addition * Add a test * Fix ADDR(LCL_VAR) Zero [FldSeq] * Always add NotAField field sequences * NotAField printing improvements
1 parent 8b5e4cc commit 1ea9cf4

6 files changed

Lines changed: 237 additions & 19 deletions

File tree

src/coreclr/jit/compiler.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3625,6 +3625,7 @@ class Compiler
36253625
void gtGetArgMsg(GenTreeCall* call, GenTree* arg, unsigned argNum, char* bufp, unsigned bufLength);
36263626
void gtGetLateArgMsg(GenTreeCall* call, GenTree* arg, int argNum, char* bufp, unsigned bufLength);
36273627
void gtDispArgList(GenTreeCall* call, GenTree* lastCallOperand, IndentStack* indentStack);
3628+
void gtDispAnyFieldSeq(FieldSeqNode* fieldSeq);
36283629
void gtDispFieldSeq(FieldSeqNode* pfsn);
36293630

36303631
void gtDispRange(LIR::ReadOnlyRange const& range);
@@ -5574,6 +5575,7 @@ class Compiler
55745575

55755576
#ifdef DEBUG
55765577
void fgDebugCheckExceptionSets();
5578+
void fgDebugCheckValueNumberedTree(GenTree* tree);
55775579
#endif
55785580

55795581
// These are the current value number for the memory implicit variables while

src/coreclr/jit/gentree.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9178,7 +9178,7 @@ void Compiler::gtDispZeroFieldSeq(GenTree* tree)
91789178
if (map->Lookup(tree, &fldSeq))
91799179
{
91809180
printf(" Zero");
9181-
gtDispFieldSeq(fldSeq);
9181+
gtDispAnyFieldSeq(fldSeq);
91829182
}
91839183
}
91849184
}
@@ -10295,9 +10295,28 @@ void Compiler::gtDispConst(GenTree* tree)
1029510295
}
1029610296
}
1029710297

10298+
//------------------------------------------------------------------------
10299+
// gtDispFieldSeq: "gtDispFieldSeq" that also prints "<NotAField>".
10300+
//
10301+
// Useful for printing zero-offset field sequences.
10302+
//
10303+
void Compiler::gtDispAnyFieldSeq(FieldSeqNode* fieldSeq)
10304+
{
10305+
if (fieldSeq == FieldSeqStore::NotAField())
10306+
{
10307+
printf(" Fseq<NotAField>");
10308+
return;
10309+
}
10310+
10311+
gtDispFieldSeq(fieldSeq);
10312+
}
10313+
10314+
//------------------------------------------------------------------------
10315+
// gtDispFieldSeq: Print out the fields in this field sequence.
10316+
//
1029810317
void Compiler::gtDispFieldSeq(FieldSeqNode* pfsn)
1029910318
{
10300-
if (pfsn == FieldSeqStore::NotAField() || (pfsn == nullptr))
10319+
if ((pfsn == nullptr) || (pfsn == FieldSeqStore::NotAField()))
1030110320
{
1030210321
return;
1030310322
}

src/coreclr/jit/morph.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13724,8 +13724,7 @@ GenTree* Compiler::fgOptimizeAddition(GenTreeOp* add)
1372413724
// TODO-Bug: this code will lose the GC-ness of a tree like "native int + byref(0)".
1372513725
if (op2->IsIntegralConst(0) && ((add->TypeGet() == op1->TypeGet()) || !op1->TypeIs(TYP_REF)))
1372613726
{
13727-
if (op2->IsCnsIntOrI() && (op2->AsIntCon()->gtFieldSeq != nullptr) &&
13728-
(op2->AsIntCon()->gtFieldSeq != FieldSeqStore::NotAField()))
13727+
if (op2->IsCnsIntOrI() && varTypeIsI(op1))
1372913728
{
1373013729
fgAddFieldSeqForZeroOffset(op1, op2->AsIntCon()->gtFieldSeq);
1373113730
}
@@ -17935,7 +17934,7 @@ void Compiler::fgAddFieldSeqForZeroOffset(GenTree* addr, FieldSeqNode* fieldSeqZ
1793517934
if (verbose)
1793617935
{
1793717936
printf("\nfgAddFieldSeqForZeroOffset for");
17938-
gtDispFieldSeq(fieldSeqZero);
17937+
gtDispAnyFieldSeq(fieldSeqZero);
1793917938

1794017939
printf("\naddr (Before)\n");
1794117940
gtDispNode(addr, nullptr, nullptr, false);

src/coreclr/jit/valuenum.cpp

Lines changed: 98 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4167,32 +4167,33 @@ ValueNum ValueNumStore::VNForFieldSeq(FieldSeqNode* fieldSeq)
41674167
{
41684168
return VNForNull();
41694169
}
4170-
else if (fieldSeq == FieldSeqStore::NotAField())
4170+
4171+
ValueNum fieldSeqVN;
4172+
if (fieldSeq == FieldSeqStore::NotAField())
41714173
{
41724174
// We always allocate a new, unique VN in this call.
41734175
Chunk* c = GetAllocChunk(TYP_REF, CEA_NotAField);
41744176
unsigned offsetWithinChunk = c->AllocVN();
4175-
ValueNum result = c->m_baseVN + offsetWithinChunk;
4176-
return result;
4177+
fieldSeqVN = c->m_baseVN + offsetWithinChunk;
41774178
}
41784179
else
41794180
{
41804181
ssize_t fieldHndVal = ssize_t(fieldSeq->m_fieldHnd);
41814182
ValueNum fieldHndVN = VNForHandle(fieldHndVal, GTF_ICON_FIELD_HDL);
41824183
ValueNum seqNextVN = VNForFieldSeq(fieldSeq->m_next);
4183-
ValueNum fieldSeqVN = VNForFunc(TYP_REF, VNF_FieldSeq, fieldHndVN, seqNextVN);
4184+
fieldSeqVN = VNForFunc(TYP_REF, VNF_FieldSeq, fieldHndVN, seqNextVN);
4185+
}
41844186

41854187
#ifdef DEBUG
4186-
if (m_pComp->verbose)
4187-
{
4188-
printf(" FieldSeq");
4189-
vnDump(m_pComp, fieldSeqVN);
4190-
printf(" is " FMT_VN "\n", fieldSeqVN);
4191-
}
4188+
if (m_pComp->verbose)
4189+
{
4190+
printf(" FieldSeq");
4191+
vnDump(m_pComp, fieldSeqVN);
4192+
printf(" is " FMT_VN "\n", fieldSeqVN);
4193+
}
41924194
#endif
41934195

4194-
return fieldSeqVN;
4195-
}
4196+
return fieldSeqVN;
41964197
}
41974198

41984199
FieldSeqNode* ValueNumStore::FieldSeqVNToFieldSeq(ValueNum vn)
@@ -5961,6 +5962,7 @@ void ValueNumStore::vnDump(Compiler* comp, ValueNum vn, bool isPtr)
59615962
switch (funcApp.m_func)
59625963
{
59635964
case VNF_FieldSeq:
5965+
case VNF_NotAField:
59645966
vnDumpFieldSeq(comp, &funcApp, true);
59655967
break;
59665968
case VNF_MapSelect:
@@ -6068,6 +6070,12 @@ void ValueNumStore::vnDumpExcSeq(Compiler* comp, VNFuncApp* excSeq, bool isHead)
60686070

60696071
void ValueNumStore::vnDumpFieldSeq(Compiler* comp, VNFuncApp* fieldSeq, bool isHead)
60706072
{
6073+
if (fieldSeq->m_func == VNF_NotAField)
6074+
{
6075+
printf("<NotAField>");
6076+
return;
6077+
}
6078+
60716079
assert(fieldSeq->m_func == VNF_FieldSeq); // Precondition.
60726080
// First arg is the field handle VN.
60736081
assert(IsVNConstant(fieldSeq->m_args[0]) && TypeOfVN(fieldSeq->m_args[0]) == TYP_I_IMPL);
@@ -8592,12 +8600,21 @@ void Compiler::fgValueNumberTree(GenTree* tree)
85928600
newVN = vnStore->VNForExpr(compCurBB, TYP_BYREF);
85938601
}
85948602
}
8603+
85958604
if (newVN == ValueNumStore::NoVN)
85968605
{
8606+
// We may have a zero-offset field sequence on this ADDR.
8607+
FieldSeqNode* zeroOffsetFieldSeq = nullptr;
8608+
if (GetZeroOffsetFieldMap()->Lookup(tree, &zeroOffsetFieldSeq))
8609+
{
8610+
fieldSeq = GetFieldSeqStore()->Append(fieldSeq, zeroOffsetFieldSeq);
8611+
}
8612+
85978613
newVN = vnStore->VNForFunc(TYP_BYREF, VNF_PtrToLoc,
85988614
vnStore->VNForIntCon(arg->AsLclVarCommon()->GetLclNum()),
85998615
vnStore->VNForFieldSeq(fieldSeq));
86008616
}
8617+
86018618
tree->gtVNPair.SetBoth(newVN);
86028619
}
86038620
else if ((arg->gtOper == GT_IND) || arg->OperIsBlk())
@@ -8608,8 +8625,7 @@ void Compiler::fgValueNumberTree(GenTree* tree)
86088625

86098626
ValueNumPair addrVNP = ValueNumPair();
86108627
FieldSeqNode* zeroOffsetFieldSeq = nullptr;
8611-
if (GetZeroOffsetFieldMap()->Lookup(tree, &zeroOffsetFieldSeq) &&
8612-
(zeroOffsetFieldSeq != FieldSeqStore::NotAField()))
8628+
if (GetZeroOffsetFieldMap()->Lookup(tree, &zeroOffsetFieldSeq))
86138629
{
86148630
ValueNum addrExtended = vnStore->ExtendPtrVN(arg->AsIndir()->Addr(), zeroOffsetFieldSeq);
86158631
if (addrExtended != ValueNumStore::NoVN)
@@ -9270,6 +9286,8 @@ void Compiler::fgValueNumberTree(GenTree* tree)
92709286
printf("\n");
92719287
}
92729288
}
9289+
9290+
fgDebugCheckValueNumberedTree(tree);
92739291
#endif // DEBUG
92749292
}
92759293

@@ -10965,6 +10983,72 @@ void Compiler::fgDebugCheckExceptionSets()
1096510983
}
1096610984
}
1096710985

10986+
//------------------------------------------------------------------------
10987+
// fgDebugCheckValueNumberedTree: Verify proper numbering for "tree".
10988+
//
10989+
// Currently only checks that we have not forgotten to add a zero-offset
10990+
// field sequence to "tree"'s value number.
10991+
//
10992+
// Arguments:
10993+
// tree - The tree, that has just been numbered, to check
10994+
//
10995+
void Compiler::fgDebugCheckValueNumberedTree(GenTree* tree)
10996+
{
10997+
FieldSeqNode* zeroOffsetFldSeq;
10998+
if (GetZeroOffsetFieldMap()->Lookup(tree, &zeroOffsetFldSeq))
10999+
{
11000+
// Empty field sequences should never be recorded in the map.
11001+
assert(zeroOffsetFldSeq != nullptr);
11002+
11003+
ValueNum vns[] = {tree->GetVN(VNK_Liberal), tree->GetVN(VNK_Conservative)};
11004+
for (ValueNum vn : vns)
11005+
{
11006+
VNFuncApp vnFunc;
11007+
if (vnStore->GetVNFunc(vn, &vnFunc))
11008+
{
11009+
FieldSeqNode* fullFldSeq;
11010+
switch (vnFunc.m_func)
11011+
{
11012+
case VNF_PtrToLoc:
11013+
case VNF_PtrToStatic:
11014+
fullFldSeq = vnStore->FieldSeqVNToFieldSeq(vnFunc.m_args[1]);
11015+
break;
11016+
11017+
case VNF_PtrToArrElem:
11018+
fullFldSeq = vnStore->FieldSeqVNToFieldSeq(vnFunc.m_args[3]);
11019+
break;
11020+
11021+
default:
11022+
continue;
11023+
}
11024+
11025+
// Verify that the "fullFldSeq" we have just collected is of the
11026+
// form "[outer fields, zeroOffsetFldSeq]", or is "NotAField".
11027+
if (fullFldSeq == FieldSeqStore::NotAField())
11028+
{
11029+
continue;
11030+
}
11031+
11032+
// This check relies on the canonicality of field sequences.
11033+
FieldSeqNode* fldSeq = fullFldSeq;
11034+
bool zeroOffsetFldSeqFound = false;
11035+
while (fldSeq != nullptr)
11036+
{
11037+
if (fldSeq == zeroOffsetFldSeq)
11038+
{
11039+
zeroOffsetFldSeqFound = true;
11040+
break;
11041+
}
11042+
11043+
fldSeq = fldSeq->m_next;
11044+
}
11045+
11046+
assert(zeroOffsetFldSeqFound);
11047+
}
11048+
}
11049+
}
11050+
}
11051+
1096811052
// This method asserts that SSA name constraints specified are satisfied.
1096911053
// Until we figure out otherwise, all VN's are assumed to be liberal.
1097011054
// TODO-Cleanup: new JitTestLabels for lib vs cons vs both VN classes?
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Runtime.InteropServices;
6+
using System.Runtime.CompilerServices;
7+
8+
class ZeroOffsetFieldSeqs
9+
{
10+
private static UnionStruct s_union;
11+
12+
public static int Main()
13+
{
14+
if (ProblemWithArrayUnions(new UnionStruct[] { default }))
15+
{
16+
return 101;
17+
}
18+
19+
if (ProblemWithStaticUnions())
20+
{
21+
return 102;
22+
}
23+
24+
if (AnotherProblemWithArrayUnions(new UnionStruct[] { default }))
25+
{
26+
return 103;
27+
}
28+
29+
return 100;
30+
}
31+
32+
[MethodImpl(MethodImplOptions.NoInlining)]
33+
private static bool ProblemWithArrayUnions(UnionStruct[] a)
34+
{
35+
if (a[0].UnionOne.UnionOneFldTwo == 0)
36+
{
37+
a[0].UnionTwo.UnionTwoFldTwo = 1;
38+
if (a[0].UnionOne.UnionOneFldTwo == 0)
39+
{
40+
return true;
41+
}
42+
}
43+
44+
return false;
45+
}
46+
47+
[MethodImpl(MethodImplOptions.NoInlining)]
48+
private static bool ProblemWithStaticUnions()
49+
{
50+
if (s_union.UnionOne.UnionOneFldTwo == 0)
51+
{
52+
s_union.UnionTwo.UnionTwoFldTwo = 1;
53+
if (s_union.UnionOne.UnionOneFldTwo == 0)
54+
{
55+
return true;
56+
}
57+
}
58+
59+
return false;
60+
}
61+
62+
[MethodImpl(MethodImplOptions.NoInlining)]
63+
private static bool AnotherProblemWithArrayUnions(UnionStruct[] a)
64+
{
65+
ref var p1 = ref a[0];
66+
ref var p1a = ref Unsafe.Add(ref p1, 0).UnionOne;
67+
ref var p1b = ref Unsafe.Add(ref p1, 0).UnionTwo;
68+
69+
if (p1a.UnionOneFldTwo == 0)
70+
{
71+
p1b.UnionTwoFldTwo = 1;
72+
if (p1a.UnionOneFldTwo == 0)
73+
{
74+
return true;
75+
}
76+
}
77+
78+
return false;
79+
}
80+
}
81+
82+
[StructLayout(LayoutKind.Explicit)]
83+
struct UnionStruct
84+
{
85+
[FieldOffset(0)]
86+
public UnionPartOne UnionOne;
87+
[FieldOffset(0)]
88+
public UnionPartTwo UnionTwo;
89+
}
90+
91+
struct UnionPartOne
92+
{
93+
public long UnionOneFldOne;
94+
public long UnionOneFldTwo;
95+
}
96+
97+
struct UnionPartTwo
98+
{
99+
public long UnionTwoFldOne;
100+
public long UnionTwoFldTwo;
101+
}
102+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>Exe</OutputType>
4+
</PropertyGroup>
5+
<PropertyGroup>
6+
<DebugType>None</DebugType>
7+
<Optimize>True</Optimize>
8+
</PropertyGroup>
9+
<ItemGroup>
10+
<Compile Include="$(MSBuildProjectName).cs" />
11+
</ItemGroup>
12+
</Project>

0 commit comments

Comments
 (0)