Skip to content

Commit d9d6e82

Browse files
Forbid creating layout-less TYP_STRUCT LCL_FLD nodes (#70170)
* Fold ADD(ADDR(LCL_VAR), CONST) => ADDR(LCL_FLD) * Add an assert for layout-less LCL_FLD * Query the handle in "gtGetStructHandleIfPresent" * Add layout checks to "Compare" and "gtHashValue"
1 parent 7227526 commit d9d6e82

File tree

4 files changed

+60
-94
lines changed

4 files changed

+60
-94
lines changed

src/coreclr/jit/gentree.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2396,7 +2396,8 @@ bool GenTree::Compare(GenTree* op1, GenTree* op2, bool swapOK)
23962396

23972397
case GT_LCL_FLD:
23982398
if ((op1->AsLclFld()->GetLclNum() != op2->AsLclFld()->GetLclNum()) ||
2399-
(op1->AsLclFld()->GetLclOffs() != op2->AsLclFld()->GetLclOffs()))
2399+
(op1->AsLclFld()->GetLclOffs() != op2->AsLclFld()->GetLclOffs()) ||
2400+
(op1->AsLclFld()->GetLayout() != op2->AsLclFld()->GetLayout()))
24002401
{
24012402
break;
24022403
}
@@ -2792,6 +2793,7 @@ unsigned Compiler::gtHashValue(GenTree* tree)
27922793
break;
27932794
case GT_LCL_FLD:
27942795
hash = genTreeHashAdd(hash, tree->AsLclFld()->GetLclNum());
2796+
hash = genTreeHashAdd(hash, tree->AsLclFld()->GetLayout());
27952797
add = tree->AsLclFld()->GetLclOffs();
27962798
break;
27972799

@@ -17200,7 +17202,11 @@ CORINFO_CLASS_HANDLE Compiler::gtGetStructHandleIfPresent(GenTree* tree)
1720017202
}
1720117203
#endif
1720217204
}
17205+
else
1720317206
#endif
17207+
{
17208+
structHnd = tree->AsLclFld()->GetLayout()->GetClassHandle();
17209+
}
1720417210
break;
1720517211
case GT_LCL_VAR:
1720617212
{
@@ -23128,7 +23134,7 @@ unsigned GenTreeHWIntrinsic::GetResultOpNumForFMA(GenTree* use, GenTree* op1, Ge
2312823134

2312923135
unsigned GenTreeLclFld::GetSize() const
2313023136
{
23131-
return genTypeSize(TypeGet());
23137+
return TypeIs(TYP_STRUCT) ? GetLayout()->GetSize() : genTypeSize(TypeGet());
2313223138
}
2313323139

2313423140
#ifdef TARGET_ARM

src/coreclr/jit/gentree.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3681,9 +3681,10 @@ struct GenTreeLclFld : public GenTreeLclVarCommon
36813681

36823682
public:
36833683
GenTreeLclFld(genTreeOps oper, var_types type, unsigned lclNum, unsigned lclOffs, ClassLayout* layout = nullptr)
3684-
: GenTreeLclVarCommon(oper, type, lclNum), m_lclOffs(static_cast<uint16_t>(lclOffs)), m_layout(layout)
3684+
: GenTreeLclVarCommon(oper, type, lclNum), m_lclOffs(static_cast<uint16_t>(lclOffs))
36853685
{
36863686
assert(lclOffs <= UINT16_MAX);
3687+
SetLayout(layout);
36873688
}
36883689

36893690
uint16_t GetLclOffs() const
@@ -3699,6 +3700,7 @@ struct GenTreeLclFld : public GenTreeLclVarCommon
36993700

37003701
ClassLayout* GetLayout() const
37013702
{
3703+
assert(!TypeIs(TYP_STRUCT) || (m_layout != nullptr));
37023704
return m_layout;
37033705
}
37043706

src/coreclr/jit/morph.cpp

Lines changed: 48 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -11194,7 +11194,6 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
1119411194
*/
1119511195

1119611196
GenTree* temp;
11197-
size_t ival1;
1119811197
GenTree* lclVarTree;
1119911198
GenTree* effectiveOp1;
1120011199
FieldSeqNode* fieldSeq = nullptr;
@@ -11571,15 +11570,14 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
1157111570

1157211571
bool foldAndReturnTemp = false;
1157311572
temp = nullptr;
11574-
ival1 = 0;
1157511573

1157611574
// Don't remove a volatile GT_IND, even if the address points to a local variable.
11577-
if ((tree->gtFlags & GTF_IND_VOLATILE) == 0)
11575+
//
11576+
if (!tree->AsIndir()->IsVolatile())
1157811577
{
1157911578
/* Try to Fold *(&X) into X */
1158011579
if (op1->gtOper == GT_ADDR)
1158111580
{
11582-
// Can not remove a GT_ADDR if it is currently a CSE candidate.
1158311581
if (gtIsActiveCSE_Candidate(op1))
1158411582
{
1158511583
break;
@@ -11679,98 +11677,34 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
1167911677
}
1168011678
}
1168111679
#endif // TARGET_ARM
11682-
11683-
/* Try to change *(&lcl + cns) into lcl[cns] to prevent materialization of &lcl */
11684-
11685-
if (op1->AsOp()->gtOp1->OperGet() == GT_ADDR && op1->AsOp()->gtOp2->OperGet() == GT_CNS_INT &&
11686-
opts.OptimizationEnabled())
11687-
{
11688-
// No overflow arithmetic with pointers
11689-
noway_assert(!op1->gtOverflow());
11690-
11691-
temp = op1->AsOp()->gtOp1->AsOp()->gtOp1;
11692-
if (!temp->OperIsLocal())
11693-
{
11694-
temp = nullptr;
11695-
break;
11696-
}
11697-
11698-
// Can not remove the GT_ADDR if it is currently a CSE candidate.
11699-
if (gtIsActiveCSE_Candidate(op1->AsOp()->gtOp1))
11700-
{
11701-
break;
11702-
}
11703-
11704-
ival1 = op1->AsOp()->gtOp2->AsIntCon()->gtIconVal;
11705-
fieldSeq = op1->AsOp()->gtOp2->AsIntCon()->gtFieldSeq;
11706-
11707-
if (ival1 == 0 && typ == temp->TypeGet() && temp->TypeGet() != TYP_STRUCT)
11708-
{
11709-
noway_assert(!varTypeIsGC(temp->TypeGet()));
11710-
foldAndReturnTemp = true;
11711-
}
11712-
else
11713-
{
11714-
// The emitter can't handle large offsets
11715-
if (ival1 != (unsigned short)ival1)
11716-
{
11717-
break;
11718-
}
11719-
11720-
// The emitter can get confused by invalid offsets
11721-
if (ival1 >= Compiler::lvaLclSize(temp->AsLclVarCommon()->GetLclNum()))
11722-
{
11723-
break;
11724-
}
11725-
}
11726-
// Now we can fold this into a GT_LCL_FLD below
11727-
// where we check (temp != nullptr)
11728-
}
1172911680
}
1173011681
}
1173111682

11732-
// At this point we may have a lclVar or lclFld that might be foldable with a bit of extra massaging:
11733-
// - We may have a load of a local where the load has a different type than the local
11734-
// - We may have a load of a local plus an offset
11735-
//
11736-
// In these cases, we will change the lclVar or lclFld into a lclFld of the appropriate type and
11737-
// offset if doing so is legal. The only cases in which this transformation is illegal are if the load
11738-
// begins before the local or if the load extends beyond the end of the local (i.e. if the load is
11739-
// out-of-bounds w.r.t. the local).
11683+
// At this point we may have a lclVar or lclFld of some mismatched type. In this case, we will change
11684+
// the lclVar or lclFld into a lclFld of the appropriate type if doing so is legal. The only cases in
11685+
// which this transformation is illegal is when we have a STRUCT indirection, as we do not have the
11686+
// necessary layout information, or if the load would extend beyond the local.
1174011687
if ((temp != nullptr) && !foldAndReturnTemp)
1174111688
{
11742-
assert(temp->OperIsLocal());
11689+
assert(temp->OperIs(GT_LCL_VAR, GT_LCL_FLD));
1174311690

11744-
unsigned lclNum = temp->AsLclVarCommon()->GetLclNum();
11691+
unsigned lclNum = temp->AsLclVarCommon()->GetLclNum();
11692+
unsigned lclOffs = temp->AsLclVarCommon()->GetLclOffs();
1174511693

1174611694
// Make sure we do not enregister this lclVar.
1174711695
lvaSetVarDoNotEnregister(lclNum DEBUGARG(DoNotEnregisterReason::LocalField));
1174811696

11749-
// If the size of the load is greater than the size of the lclVar, we cannot fold this access into
11750-
// a lclFld: the access represented by an lclFld node must begin at or after the start of the
11751-
// lclVar and must not extend beyond the end of the lclVar.
11752-
if ((ival1 >= 0) && ((ival1 + genTypeSize(typ)) <= lvaLclExactSize(lclNum)))
11697+
if ((typ != TYP_STRUCT) && ((lclOffs + genTypeSize(typ)) <= lvaLclExactSize(lclNum)))
1175311698
{
11754-
GenTreeLclFld* lclFld;
11755-
11756-
// We will turn a GT_LCL_VAR into a GT_LCL_FLD with an gtLclOffs of 'ival'
11757-
// or if we already have a GT_LCL_FLD we will adjust the gtLclOffs by adding 'ival'
11758-
// Then we change the type of the GT_LCL_FLD to match the orginal GT_IND type.
11699+
// We will change the type of the node to match the orginal GT_IND type.
1175911700
//
11760-
if (temp->OperGet() == GT_LCL_FLD)
11761-
{
11762-
lclFld = temp->AsLclFld();
11763-
lclFld->SetLclOffs(lclFld->GetLclOffs() + static_cast<unsigned>(ival1));
11764-
}
11765-
else // We have a GT_LCL_VAR.
11701+
temp->gtType = typ;
11702+
11703+
if (temp->OperIs(GT_LCL_VAR))
1176611704
{
11767-
assert(temp->OperGet() == GT_LCL_VAR);
11768-
temp->ChangeOper(GT_LCL_FLD); // Note that this makes the gtFieldSeq "NotAField".
11769-
lclFld = temp->AsLclFld();
11770-
lclFld->SetLclOffs(static_cast<unsigned>(ival1));
11705+
temp->ChangeOper(GT_LCL_FLD);
1177111706
}
1177211707

11773-
temp->gtType = tree->gtType;
1177411708
foldAndReturnTemp = true;
1177511709
}
1177611710
}
@@ -11779,20 +11713,15 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
1177911713
{
1178011714
assert(temp != nullptr);
1178111715
assert(temp->TypeGet() == typ);
11782-
assert((op1->OperGet() == GT_ADD) || (op1->OperGet() == GT_ADDR));
11716+
assert(op1->OperIs(GT_ADDR));
1178311717

1178411718
// Copy the value of GTF_DONT_CSE from the original tree to `temp`: it can be set for
1178511719
// 'temp' because a GT_ADDR always marks it for its operand.
1178611720
temp->gtFlags &= ~GTF_DONT_CSE;
1178711721
temp->gtFlags |= (tree->gtFlags & GTF_DONT_CSE);
1178811722
temp->SetVNsFromNode(tree);
1178911723

11790-
if (op1->OperGet() == GT_ADD)
11791-
{
11792-
DEBUG_DESTROY_NODE(op1->AsOp()->gtOp1); // GT_ADDR
11793-
DEBUG_DESTROY_NODE(op1->AsOp()->gtOp2); // GT_CNS_INT
11794-
}
11795-
DEBUG_DESTROY_NODE(op1); // GT_ADD or GT_ADDR
11724+
DEBUG_DESTROY_NODE(op1); // GT_ADDR
1179611725
DEBUG_DESTROY_NODE(tree); // GT_IND
1179711726

1179811727
// If the result of the fold is a local var, we may need to perform further adjustments e.g. for
@@ -12794,9 +12723,39 @@ GenTree* Compiler::fgOptimizeAddition(GenTreeOp* add)
1279412723
return op1;
1279512724
}
1279612725

12797-
// Note that these transformations are legal for floating-point ADDs as well.
1279812726
if (opts.OptimizationEnabled())
1279912727
{
12728+
// Reduce local addresses: "ADD(ADDR(LCL_VAR), OFFSET)" => "ADDR(LCL_FLD OFFSET)".
12729+
// TODO-ADDR: do "ADD(LCL_FLD/VAR_ADDR, OFFSET)" => "LCL_FLD_ADDR" instead.
12730+
//
12731+
if (op1->OperIs(GT_ADDR) && op2->IsCnsIntOrI() && op1->gtGetOp1()->OperIsLocalRead())
12732+
{
12733+
GenTreeUnOp* addrNode = op1->AsUnOp();
12734+
GenTreeLclVarCommon* lclNode = addrNode->gtGetOp1()->AsLclVarCommon();
12735+
GenTreeIntCon* offsetNode = op2->AsIntCon();
12736+
if (FitsIn<uint16_t>(offsetNode->IconValue()))
12737+
{
12738+
unsigned offset = lclNode->GetLclOffs() + static_cast<uint16_t>(offsetNode->IconValue());
12739+
12740+
// Note: the emitter does not expect out-of-bounds access for LCL_FLD_ADDR.
12741+
if (FitsIn<uint16_t>(offset) && (offset < lvaLclExactSize(lclNode->GetLclNum())))
12742+
{
12743+
// Types of location nodes under ADDRs do not matter. We arbitrarily choose TYP_UBYTE.
12744+
lclNode->ChangeType(TYP_UBYTE);
12745+
lclNode->SetOper(GT_LCL_FLD);
12746+
lclNode->AsLclFld()->SetLclOffs(offset);
12747+
lvaSetVarDoNotEnregister(lclNode->GetLclNum() DEBUGARG(DoNotEnregisterReason::LocalField));
12748+
12749+
addrNode->SetVNsFromNode(add);
12750+
12751+
DEBUG_DESTROY_NODE(offsetNode);
12752+
DEBUG_DESTROY_NODE(add);
12753+
12754+
return addrNode;
12755+
}
12756+
}
12757+
}
12758+
1280012759
// - a + b = > b - a
1280112760
// ADD((NEG(a), b) => SUB(b, a)
1280212761

src/coreclr/jit/valuenum.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8490,8 +8490,7 @@ void Compiler::fgValueNumberTree(GenTree* tree)
84908490
{
84918491
unsigned lclNum = lclFld->GetLclNum();
84928492

8493-
// TODO-ADDR: delete the "GetSize" check once location nodes are no more.
8494-
if (!lvaInSsa(lclFld->GetLclNum()) || !lclFld->HasSsaName() || (lclFld->GetSize() == 0))
8493+
if (!lvaInSsa(lclFld->GetLclNum()) || !lclFld->HasSsaName())
84958494
{
84968495
lclFld->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, lclFld->TypeGet()));
84978496
}

0 commit comments

Comments
 (0)