Skip to content

Commit

Permalink
JIT: small OSR locals must be normalize on load (#84000)
Browse files Browse the repository at this point in the history
When I changed the importation strategy for OSR in #83910 it
exposed a latent issue -- small OSR locals must normalized on load if
they were exposed at Tier0.

Fixes #83959.
Fixes #83960.
  • Loading branch information
AndyAyersMS authored Mar 28, 2023
1 parent dc75cd8 commit fd157a5
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 11 deletions.
8 changes: 6 additions & 2 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,8 @@ class LclVarDsc
unsigned char lvIsOSRLocal : 1; // Root method local in an OSR method. Any stack home will be on the Tier0 frame.
// Initial value will be defined by Tier0. Requires special handing in prolog.

unsigned char lvIsOSRExposedLocal : 1; // OSR local that was address exposed in Tier0

private:
unsigned char lvIsNeverNegative : 1; // The local is known to be never negative

Expand Down Expand Up @@ -1099,14 +1101,16 @@ class LclVarDsc
{
return varTypeIsSmall(TypeGet()) &&
// lvIsStructField is treated the same as the aliased local, see fgDoNormalizeOnStore.
(lvIsParam || m_addrExposed || lvIsStructField);
// OSR exposed locals were normalize on load in the Tier0 frame so must be so for OSR too.
(lvIsParam || m_addrExposed || lvIsStructField || lvIsOSRExposedLocal);
}

bool lvNormalizeOnStore() const
{
return varTypeIsSmall(TypeGet()) &&
// lvIsStructField is treated the same as the aliased local, see fgDoNormalizeOnStore.
!(lvIsParam || m_addrExposed || lvIsStructField);
// OSR exposed locals were normalize on load in the Tier0 frame so must be so for OSR too.
!(lvIsParam || m_addrExposed || lvIsStructField || lvIsOSRExposedLocal);
}

void incRefCnts(weight_t weight, Compiler* pComp, RefCountState state = RCS_NORMAL, bool propagate = true);
Expand Down
23 changes: 15 additions & 8 deletions src/coreclr/jit/lclvars.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,12 @@ void Compiler::lvaInitTypeRef()
{
LclVarDsc* const varDsc = lvaGetDesc(lclNum);
varDsc->lvIsOSRLocal = true;

if (info.compPatchpointInfo->IsExposed(lclNum))
{
JITDUMP("-- V%02u is OSR exposed\n", lclNum);
varDsc->lvIsOSRExposedLocal = true;
}
}
}

Expand Down Expand Up @@ -2435,14 +2441,15 @@ void Compiler::StructPromotionHelper::PromoteStructVar(unsigned lclNum)
// refresh the cached varDsc for lclNum.
varDsc = compiler->lvaGetDesc(lclNum);

LclVarDsc* fieldVarDsc = compiler->lvaGetDesc(varNum);
fieldVarDsc->lvType = pFieldInfo->fldType;
fieldVarDsc->lvIsStructField = true;
fieldVarDsc->lvFldOffset = pFieldInfo->fldOffset;
fieldVarDsc->lvFldOrdinal = pFieldInfo->fldOrdinal;
fieldVarDsc->lvParentLcl = lclNum;
fieldVarDsc->lvIsParam = varDsc->lvIsParam;
fieldVarDsc->lvIsOSRLocal = varDsc->lvIsOSRLocal;
LclVarDsc* fieldVarDsc = compiler->lvaGetDesc(varNum);
fieldVarDsc->lvType = pFieldInfo->fldType;
fieldVarDsc->lvIsStructField = true;
fieldVarDsc->lvFldOffset = pFieldInfo->fldOffset;
fieldVarDsc->lvFldOrdinal = pFieldInfo->fldOrdinal;
fieldVarDsc->lvParentLcl = lclNum;
fieldVarDsc->lvIsParam = varDsc->lvIsParam;
fieldVarDsc->lvIsOSRLocal = varDsc->lvIsOSRLocal;
fieldVarDsc->lvIsOSRExposedLocal = varDsc->lvIsOSRExposedLocal;

// This new local may be the first time we've seen a long typed local.
if (fieldVarDsc->lvType == TYP_LONG)
Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/jit/morph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15069,7 +15069,8 @@ PhaseStatus Compiler::fgRetypeImplicitByRefArgs()
if (fieldVarDsc->lvIsOSRLocal)
{
assert(opts.IsOSR());
fieldVarDsc->lvIsOSRLocal = false;
fieldVarDsc->lvIsOSRLocal = false;
fieldVarDsc->lvIsOSRExposedLocal = false;
}
}

Expand Down
52 changes: 52 additions & 0 deletions src/tests/JIT/opt/OSR/normalizeonload.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Runtime.CompilerServices;

// Ensure small OSR locals are marked as normalize on load

class Runtime_83959
{
static bool B(out byte b)
{
b = 1;
return true;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static void F() {}

[MethodImpl(MethodImplOptions.NoInlining)]
[SkipLocalsInit]
static void WithOSR(int n, out char c)
{
c = (char) 0;
B(out byte b);
for (int i = 0; i < n; i++)
{
F();
}
// This load of `b` must be a single byte
c = (char) b;
c += (char) 99;
}

// Ensure stack is filled with nonzero data
static void FillStack(int n)
{
Span<int> s = stackalloc int[n];
for (int i = 0; i < n; i++)
{
s[i] = -1;
}
}

public static int Main()
{
char c = (char) 0;
FillStack(100);
WithOSR(50000, out c);
return (int) c;
}
}
15 changes: 15 additions & 0 deletions src/tests/JIT/opt/OSR/normalizeonload.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<DebugType />
<Optimize>True</Optimize>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildProjectName).cs" />

<CLRTestEnvironmentVariable Include="DOTNET_TieredCompilation" Value="1" />
<CLRTestEnvironmentVariable Include="DOTNET_TC_QuickJitForLoops" Value="1" />
<CLRTestEnvironmentVariable Include="DOTNET_TC_OnStackReplacement" Value="1" />
</ItemGroup>
</Project>

0 comments on commit fd157a5

Please sign in to comment.