diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index b3e771c4a11ede..85ec2b3c1b9467 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -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 @@ -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); diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 192b26ca6e1fd4..38620131777271 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -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; + } } } @@ -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) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 64006e890d8595..72f57d2bdc1735 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -15069,7 +15069,8 @@ PhaseStatus Compiler::fgRetypeImplicitByRefArgs() if (fieldVarDsc->lvIsOSRLocal) { assert(opts.IsOSR()); - fieldVarDsc->lvIsOSRLocal = false; + fieldVarDsc->lvIsOSRLocal = false; + fieldVarDsc->lvIsOSRExposedLocal = false; } } diff --git a/src/tests/JIT/opt/OSR/normalizeonload.cs b/src/tests/JIT/opt/OSR/normalizeonload.cs new file mode 100644 index 00000000000000..31b6a12d5a1186 --- /dev/null +++ b/src/tests/JIT/opt/OSR/normalizeonload.cs @@ -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 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; + } +} diff --git a/src/tests/JIT/opt/OSR/normalizeonload.csproj b/src/tests/JIT/opt/OSR/normalizeonload.csproj new file mode 100644 index 00000000000000..b3bc14e098a820 --- /dev/null +++ b/src/tests/JIT/opt/OSR/normalizeonload.csproj @@ -0,0 +1,15 @@ + + + Exe + + True + True + + + + + + + + +