Skip to content

Commit 337999d

Browse files
authored
JIT: Handle some "field offset computation" patterns (#81998)
Both during local morph and during VN. Fix #40021 Saves 3 KB on BasicMinimalApi after #84095 (there's 1111 __GetFieldHelper functions). About 0.04%. There's still a null check kept for each offset computation, which we cannot really get rid of, but NAOT could maybe emit the IL such that there is a dominating null check so that only one is emitted. Example: Base: ``` .managed:0000000140347CC0 loc_140347CC0: ; CODE XREF: S_P_CoreLib_System_Collections_Generic_KeyValuePair_2_System_Net_Security_System_Net_Security_SslSessionsCache_SslCredKey__System___Canon_____GetFieldHelper+23↑j .managed:0000000140347CC0 ; DATA XREF: .rdata:__readonlydata_S_P_CoreLib_System_Collections_Generic_KeyValuePair_2_System_Net_Security_System_Net_Security_SslSessionsCache_SslCredKey__System___Canon_____GetFieldHelper↓o .managed:0000000140347CC0 lea rax, ??_7Boxed_System_Net_Security_System_Net_Security_SslSessionsCache_SslCredKey@@6b@ ; jumptable 0000000140347CB3 case 0 .managed:0000000140347CC7 mov [r9], rax .managed:0000000140347CCA cmp [rcx], cl .managed:0000000140347CCC lea rax, [rcx+8] .managed:0000000140347CD0 sub rax, rcx .managed:0000000140347CD3 add rsp, 8 .managed:0000000140347CD7 retn ``` Diff: ``` .managed:0000000140347AA0 loc_140347AA0: ; CODE XREF: S_P_CoreLib_System_Collections_Generic_KeyValuePair_2_System_Net_Security_System_Net_Security_SslSessionsCache_SslCredKey__System___Canon_____GetFieldHelper+23↑j .managed:0000000140347AA0 ; DATA XREF: .rdata:__readonlydata_S_P_CoreLib_System_Collections_Generic_KeyValuePair_2_System_Net_Security_System_Net_Security_SslSessionsCache_SslCredKey__System___Canon_____GetFieldHelper↓o .managed:0000000140347AA0 lea rax, ??_7Boxed_System_Net_Security_System_Net_Security_SslSessionsCache_SslCredKey@@6b@ ; jumptable 0000000140347A93 case 0 .managed:0000000140347AA7 mov [r9], rax .managed:0000000140347AAA cmp [rcx], cl .managed:0000000140347AAC mov eax, 8 .managed:0000000140347AB1 add rsp, 8 .managed:0000000140347AB5 retn ``` Local morph changes handle the pattern for local structs -- VN changes handle the pattern for classes (and more complicated struct cases, like storing them in locals, which there are a few examples of in #40021).
1 parent e0c94f8 commit 337999d

File tree

2 files changed

+64
-3
lines changed

2 files changed

+64
-3
lines changed

src/coreclr/jit/lclmorph.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,34 @@ class LocalAddressVisitor final : public GenTreeVisitor<LocalAddressVisitor>
650650
PopValue();
651651
break;
652652

653+
case GT_SUB:
654+
{
655+
Value& rhs = TopValue(0);
656+
Value& lhs = TopValue(1);
657+
if (m_compiler->opts.OptimizationEnabled() && lhs.IsAddress() && rhs.IsAddress() &&
658+
(lhs.LclNum() == rhs.LclNum()) && (rhs.Offset() <= lhs.Offset()) &&
659+
FitsIn<int>(lhs.Offset() - rhs.Offset()))
660+
{
661+
// TODO-Bug: Due to inlining we may end up with incorrectly typed SUB trees here.
662+
assert(node->TypeIs(TYP_I_IMPL, TYP_BYREF));
663+
664+
ssize_t result = (ssize_t)(lhs.Offset() - rhs.Offset());
665+
node->BashToConst(result, TYP_I_IMPL);
666+
INDEBUG(lhs.Consume());
667+
INDEBUG(rhs.Consume());
668+
PopValue();
669+
PopValue();
670+
m_stmtModified = true;
671+
break;
672+
}
673+
674+
EscapeValue(TopValue(0), node);
675+
PopValue();
676+
EscapeValue(TopValue(0), node);
677+
PopValue();
678+
break;
679+
}
680+
653681
case GT_FIELD_ADDR:
654682
if (node->AsField()->IsInstance())
655683
{

src/coreclr/jit/valuenum.cpp

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4450,7 +4450,7 @@ ValueNum ValueNumStore::EvalUsingMathIdentity(var_types typ, VNFunc func, ValueN
44504450
// (x - 0) == x
44514451
// (x - x) == 0
44524452
// This identity does not apply for floating point (when x == -0.0).
4453-
auto identityForSubtraction = [=]() -> ValueNum {
4453+
auto identityForSubtraction = [=](bool ovf) -> ValueNum {
44544454
if (!varTypeIsFloating(typ))
44554455
{
44564456
ValueNum ZeroVN = VNZeroForType(typ);
@@ -4462,6 +4462,39 @@ ValueNum ValueNumStore::EvalUsingMathIdentity(var_types typ, VNFunc func, ValueN
44624462
{
44634463
return ZeroVN;
44644464
}
4465+
4466+
if (!ovf)
4467+
{
4468+
// (x + a) - x == a
4469+
// (a + x) - x == a
4470+
VNFuncApp add;
4471+
if (GetVNFunc(arg0VN, &add) && (add.m_func == (VNFunc)GT_ADD))
4472+
{
4473+
if (add.m_args[0] == arg1VN)
4474+
return add.m_args[1];
4475+
if (add.m_args[1] == arg1VN)
4476+
return add.m_args[0];
4477+
4478+
// (x + a) - (x + b) == a - b
4479+
// (a + x) - (x + b) == a - b
4480+
// (x + a) - (b + x) == a - b
4481+
// (a + x) - (b + x) == a - b
4482+
VNFuncApp add2;
4483+
if (GetVNFunc(arg1VN, &add2) && (add2.m_func == (VNFunc)GT_ADD))
4484+
{
4485+
for (int a = 0; a < 2; a++)
4486+
{
4487+
for (int b = 0; b < 2; b++)
4488+
{
4489+
if (add.m_args[a] == add2.m_args[b])
4490+
{
4491+
return VNForFunc(typ, (VNFunc)GT_SUB, add.m_args[1 - a], add2.m_args[1 - b]);
4492+
}
4493+
}
4494+
}
4495+
}
4496+
}
4497+
}
44654498
}
44664499

44674500
return NoVN;
@@ -4515,7 +4548,7 @@ ValueNum ValueNumStore::EvalUsingMathIdentity(var_types typ, VNFunc func, ValueN
45154548
break;
45164549

45174550
case GT_SUB:
4518-
resultVN = identityForSubtraction();
4551+
resultVN = identityForSubtraction(/* ovf */ false);
45194552
break;
45204553

45214554
case GT_MUL:
@@ -4830,7 +4863,7 @@ ValueNum ValueNumStore::EvalUsingMathIdentity(var_types typ, VNFunc func, ValueN
48304863

48314864
case VNF_SUB_OVF:
48324865
case VNF_SUB_UN_OVF:
4833-
resultVN = identityForSubtraction();
4866+
resultVN = identityForSubtraction(/* ovf */ true);
48344867
break;
48354868

48364869
case VNF_MUL_OVF:

0 commit comments

Comments
 (0)