Skip to content

Commit ade2459

Browse files
authored
JIT: Add ordering side effects in fgMorphExpandInstanceField (#92998)
In #92710 I removed an ordering side effect set on a LCL_VAR node in fgMorphExpandInstanceField, since the side effect is meaningless on a local. However, I did not realize that setting the ordering effect was important because propagating it to parent nodes was actually necessary. This PR explicitly sets the ordering effect on the ADD<byref> nodes created, since forming these and reporting them to GC is illegal if the base is invalid. Additionally, it sets the ordering effect on the consuming indirection; if that indirection made use of the fact that the FIELD_ADDR is non-null to become non-faulting, then it has an ordering dependency once we expand the FIELD_ADDR to a control-flow based null-check. This is not actually necessary since we always set end up with an ADD<byref> that propagates it, but that seems more like a coincidence than anything else. Fix #92990
1 parent 686bd7a commit ade2459

File tree

2 files changed

+35
-12
lines changed

2 files changed

+35
-12
lines changed

src/coreclr/jit/compiler.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5917,8 +5917,8 @@ class Compiler
59175917
// small; hence the other fields of MorphAddrContext.
59185918
struct MorphAddrContext
59195919
{
5920-
size_t m_totalOffset = 0; // Sum of offsets between the top-level indirection and here (current context).
5921-
bool m_used = false; // Whether this context was used to elide a null check.
5920+
GenTreeIndir* m_user = nullptr; // Indirection using this address.
5921+
size_t m_totalOffset = 0; // Sum of offsets between the top-level indirection and here (current context).
59225922
};
59235923

59245924
#ifdef FEATURE_SIMD

src/coreclr/jit/morph.cpp

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5088,9 +5088,24 @@ GenTree* Compiler::fgMorphExpandInstanceField(GenTree* tree, MorphAddrContext* m
50885088
// will implicitly null-check the produced address.
50895089
addExplicitNullCheck = (mac == nullptr) || fgIsBigOffset(mac->m_totalOffset + fieldOffset);
50905090

5091-
if (!addExplicitNullCheck)
5091+
// The transformation here turns a value dependency (FIELD_ADDR being a
5092+
// known non-null operand) into a control-flow dependency (introducing
5093+
// explicit COMMA(NULLCHECK, ...)). This effectively "disconnects" the
5094+
// null check from the parent of the FIELD_ADDR node. For the cases
5095+
// where we made use of non-nullness we need to make the dependency
5096+
// explicit now.
5097+
if (addExplicitNullCheck)
50925098
{
5093-
mac->m_used = true;
5099+
if (mac != nullptr)
5100+
{
5101+
mac->m_user->SetHasOrderingSideEffect();
5102+
}
5103+
}
5104+
else
5105+
{
5106+
// We can elide the null check only by letting it happen as part of
5107+
// the consuming indirection, so it is no longer non-faulting.
5108+
mac->m_user->gtFlags &= ~GTF_IND_NONFAULTING;
50945109
}
50955110
}
50965111

@@ -5153,6 +5168,13 @@ GenTree* Compiler::fgMorphExpandInstanceField(GenTree* tree, MorphAddrContext* m
51535168
}
51545169

51555170
addr = gtNewOperNode(GT_ADD, (objRefType == TYP_I_IMPL) ? TYP_I_IMPL : TYP_BYREF, addr, offsetNode);
5171+
5172+
// We cannot form and GC report an invalid byref, so this must preserve
5173+
// its ordering with the null check.
5174+
if (addExplicitNullCheck && addr->TypeIs(TYP_BYREF))
5175+
{
5176+
addr->SetHasOrderingSideEffect();
5177+
}
51565178
}
51575179
#endif
51585180

@@ -5168,6 +5190,13 @@ GenTree* Compiler::fgMorphExpandInstanceField(GenTree* tree, MorphAddrContext* m
51685190
{
51695191
addr = gtNewOperNode(GT_ADD, (objRefType == TYP_I_IMPL) ? TYP_I_IMPL : TYP_BYREF, addr,
51705192
gtNewIconNode(fieldOffset, fieldSeq));
5193+
5194+
// We cannot form and GC report an invalid byref, so this must preserve
5195+
// its ordering with the null check.
5196+
if (addExplicitNullCheck && addr->TypeIs(TYP_BYREF))
5197+
{
5198+
addr->SetHasOrderingSideEffect();
5199+
}
51715200
}
51725201

51735202
if (addExplicitNullCheck)
@@ -8937,7 +8966,8 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA
89378966
if (tree->OperIsIndir()) // TODO-CQ: add more operators here (e. g. atomics).
89388967
{
89398968
// Communicate to FIELD_ADDR morphing that the parent is an indirection.
8940-
mac = &indMac;
8969+
indMac.m_user = tree->AsIndir();
8970+
mac = &indMac;
89418971
}
89428972
// For additions, if we already have a context, keep track of whether all offsets added
89438973
// to the address are constant, and their sum does not overflow.
@@ -8961,13 +8991,6 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA
89618991

89628992
tree->AsOp()->gtOp1 = op1 = fgMorphTree(op1, mac);
89638993

8964-
if ((mac != nullptr) && mac->m_used && tree->OperIsIndir())
8965-
{
8966-
// This context was used to elide an explicit null check on a FIELD_ADDR, with the expectation that
8967-
// this indirection will now be responsible for null-checking (implicitly).
8968-
tree->gtFlags &= ~GTF_IND_NONFAULTING;
8969-
}
8970-
89718994
// If we are exiting the "then" part of a Qmark-Colon we must
89728995
// save the state of the current copy assignment table
89738996
// so that we can merge this state with the "else" part exit

0 commit comments

Comments
 (0)