Skip to content

Commit 9b76c28

Browse files
authored
JIT: Fix tailcall-to-loop improper locals zeroing (#81083)
The zeroing that the tailcall-to-loop optimization does was zeroing the promoted copies for implicit byrefs even when promotion of them was undone. This was introducing unexpected references to the promoted fields. Fix #81081
1 parent 1145e01 commit 9b76c28

3 files changed

Lines changed: 82 additions & 23 deletions

File tree

src/coreclr/jit/morph.cpp

Lines changed: 47 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7866,39 +7866,63 @@ void Compiler::fgMorphRecursiveFastTailCallIntoLoop(BasicBlock* block, GenTreeCa
78667866
// Liveness phase will remove unnecessary initializations.
78677867
if (info.compInitMem || compSuppressedZeroInit)
78687868
{
7869-
unsigned varNum;
7870-
LclVarDsc* varDsc;
7871-
for (varNum = 0, varDsc = lvaTable; varNum < lvaCount; varNum++, varDsc++)
7869+
for (unsigned varNum = 0; varNum < lvaCount; varNum++)
78727870
{
78737871
#if FEATURE_FIXED_OUT_ARGS
78747872
if (varNum == lvaOutgoingArgSpaceVar)
78757873
{
78767874
continue;
78777875
}
78787876
#endif // FEATURE_FIXED_OUT_ARGS
7879-
if (!varDsc->lvIsParam)
7877+
7878+
LclVarDsc* varDsc = lvaGetDesc(varNum);
7879+
7880+
if (varDsc->lvIsParam)
78807881
{
7881-
var_types lclType = varDsc->TypeGet();
7882-
bool isUserLocal = (varNum < info.compLocalsCount);
7883-
bool structWithGCFields = ((lclType == TYP_STRUCT) && varDsc->GetLayout()->HasGCPtr());
7884-
bool hadSuppressedInit = varDsc->lvSuppressedZeroInit;
7885-
if ((info.compInitMem && (isUserLocal || structWithGCFields)) || hadSuppressedInit)
7882+
continue;
7883+
}
7884+
7885+
#if FEATURE_IMPLICIT_BYREFS
7886+
if (varDsc->lvPromoted)
7887+
{
7888+
LclVarDsc* firstField = lvaGetDesc(varDsc->lvFieldLclStart);
7889+
if (firstField->lvParentLcl != varNum)
78867890
{
7887-
GenTree* lcl = gtNewLclvNode(varNum, lclType);
7888-
GenTree* init = nullptr;
7889-
if (varTypeIsStruct(lclType))
7890-
{
7891-
init = gtNewBlkOpNode(lcl, gtNewIconNode(0));
7892-
init = fgMorphInitBlock(init);
7893-
}
7894-
else
7895-
{
7896-
GenTree* zero = gtNewZeroConNode(lclType);
7897-
init = gtNewAssignNode(lcl, zero);
7898-
}
7899-
Statement* initStmt = gtNewStmt(init, callDI);
7900-
fgInsertStmtBefore(block, lastStmt, initStmt);
7891+
// Local copy for implicit byref promotion that was undone. Do
7892+
// not introduce new references to it, all uses have been
7893+
// morphed to access the parameter.
7894+
CLANG_FORMAT_COMMENT_ANCHOR;
7895+
7896+
#ifdef DEBUG
7897+
LclVarDsc* param = lvaGetDesc(firstField->lvParentLcl);
7898+
assert(param->lvIsImplicitByRef && !param->lvPromoted);
7899+
assert(param->lvFieldLclStart == varNum);
7900+
#endif
7901+
continue;
7902+
}
7903+
}
7904+
#endif
7905+
7906+
var_types lclType = varDsc->TypeGet();
7907+
bool isUserLocal = (varNum < info.compLocalsCount);
7908+
bool structWithGCFields = ((lclType == TYP_STRUCT) && varDsc->GetLayout()->HasGCPtr());
7909+
bool hadSuppressedInit = varDsc->lvSuppressedZeroInit;
7910+
if ((info.compInitMem && (isUserLocal || structWithGCFields)) || hadSuppressedInit)
7911+
{
7912+
GenTree* lcl = gtNewLclvNode(varNum, lclType);
7913+
GenTree* init = nullptr;
7914+
if (varTypeIsStruct(lclType))
7915+
{
7916+
init = gtNewBlkOpNode(lcl, gtNewIconNode(0));
7917+
init = fgMorphInitBlock(init);
7918+
}
7919+
else
7920+
{
7921+
GenTree* zero = gtNewZeroConNode(lclType);
7922+
init = gtNewAssignNode(lcl, zero);
79017923
}
7924+
Statement* initStmt = gtNewStmt(init, callDI);
7925+
fgInsertStmtBefore(block, lastStmt, initStmt);
79027926
}
79037927
}
79047928
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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+
class Runtime_81081
5+
{
6+
static int Main()
7+
{
8+
Test(1234, default);
9+
return 100;
10+
}
11+
12+
static int Test(int count, S16 s)
13+
{
14+
object o = "1234";
15+
if (count == 0 || o.GetHashCode() == 1234)
16+
return 42;
17+
18+
return Test(count - 1, s);
19+
}
20+
21+
struct S16
22+
{
23+
public object A, B;
24+
}
25+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>Exe</OutputType>
4+
<Optimize>True</Optimize>
5+
</PropertyGroup>
6+
<ItemGroup>
7+
<Compile Include="$(MSBuildProjectName).cs" />
8+
<CLRTestEnvironmentVariable Include="DOTNET_JitNoInline" Value="1" />
9+
</ItemGroup>
10+
</Project>

0 commit comments

Comments
 (0)