if (varTypeIsStruct(exprToBox))
{
+ // Workaround for GitHub issue 53549.
+ //
+ // If the struct being boxed is returned via hidden buffer and comes from an inline/gdv candidate,
+ // the IR we produce after importation is out of order:
+ //
+ // call (&(box-temp + 8), ....)
+ // box-temp = newobj
+ // ret-val from call (void)
+ // ... box-temp (on stack)
+ //
+ // For inline candidates this bad ordering gets fixed up during inlining, but for GDV candidates
+ // the GDV expansion is such that the newobj follows the call as in the above.
+ //
+ // This is nontrivial to fix in GDV, so in these (rare) cases we simply disable GDV.
+ //
+ if (exprToBox->OperIs(GT_RET_EXPR))
+ {
+ GenTreeCall* const call = exprToBox->AsRetExpr()->gtInlineCandidate->AsCall();
+
+ if (call->IsGuardedDevirtualizationCandidate() && call->HasRetBufArg())
+ {
+ JITDUMP("Disabling GDV for [%06u] because of in-box struct return\n");
+ call->ClearGuardedDevirtualizationCandidate();
+ if (call->IsVirtualStub())
+ {
+ JITDUMP("Restoring stub addr %p from guarded devirt candidate info\n",
+ dspPtr(call->gtGuardedDevirtualizationCandidateInfo->stubAddr));
+ call->gtStubCallStubAddr = call->gtGuardedDevirtualizationCandidateInfo->stubAddr;
+ }
+ }
+ }
+
assert(info.compCompHnd->getClassSize(pResolvedToken->hClass) == info.compCompHnd->getClassSize(operCls));
op1 = impAssignStructPtr(op1, exprToBox, operCls, (unsigned)CHECK_SPILL_ALL);
}
--- /dev/null
+// 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;
+using System.Threading;
+
+interface I
+{
+ public Decimal F();
+}
+
+class Runtime_53549 : I
+{
+ Decimal z;
+
+ public Decimal F() => z;
+
+ public static bool G(object o)
+ {
+ return ((decimal) o).Equals(100M);
+ }
+
+ // This method will have bad codegen if
+ // we allow GDV on i.F().
+ //
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static int H(I i)
+ {
+ return G(i.F()) ? 100 : -1;
+ }
+
+ public static int Main()
+ {
+ Runtime_53549 x = new Runtime_53549();
+ x.z = 100M;
+
+ for (int i = 0; i < 100; i++)
+ {
+ _ = H(x);
+ Thread.Sleep(15);
+ }
+
+ return H(x);
+ }
+}
+
+
+
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ </PropertyGroup>
+ <PropertyGroup>
+ <DebugType>None</DebugType>
+ <Optimize>True</Optimize>
+ </PropertyGroup>
+ <PropertyGroup>
+ <CLRTestBatchPreCommands><![CDATA[
+$(CLRTestBatchPreCommands)
+set COMPlus_TieredPGO=1
+set COMPlus_TC_QuickJitForLoops=1
+]]></CLRTestBatchPreCommands>
+ <BashCLRTestPreCommands><![CDATA[
+$(BashCLRTestPreCommands)
+export COMPlus_TieredPGO=1
+export COMPlus_TC_QuickJitForLoops=1
+]]></BashCLRTestPreCommands>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="$(MSBuildProjectName).cs" />
+ </ItemGroup>
+</Project>