Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Fix for 17398. #17501

Merged
merged 1 commit into from
Apr 12, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 31 additions & 11 deletions src/vm/eetwain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2404,14 +2404,16 @@ unsigned scanArgRegTable(PTR_CBYTE table,
Returns size of things pushed on the stack for ESP frames

Arguments:
table - The pointer table
curOffs - The current code offset
info - Incoming arg used to determine if there's a frame, and to save results
table - The pointer table
curOffsRegs - The current code offset that should be used for reporting registers
curOffsArgs - The current code offset that should be used for reporting args

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, and/or below where you assert that one is less than the other, you should add a comment describing why these are (or may be) different (or refer to the comment where this is called from EnumGcRefs().

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a comment before the assert that references the comment in EnumGCRefs.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it would be better that instead of two offset arguments to have just one offset and a flag requiring the register offset to be adjusted (e.g. inactiveFrame, callInProgress etc.). That would avoid that need for asserts such as "one is less than the other".

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I started with that version but decided this is better. This method is called from several places and some of them don't really care about the registers and don't have the active/inactive frame info. It seemed better to just pass the same curOffs from those callers.

info - Incoming arg used to determine if there's a frame, and to save results
*/

static
unsigned scanArgRegTableI(PTR_CBYTE table,
unsigned curOffs,
unsigned scanArgRegTableI(PTR_CBYTE table,
unsigned curOffsRegs,
unsigned curOffsArgs,
hdrInfo * info)
{
CONTRACTL {
Expand All @@ -2433,6 +2435,10 @@ unsigned scanArgRegTableI(PTR_CBYTE table,
bool isThis = false;
bool iptr = false;

// The comment before the call to scanArgRegTableI in EnumGCRefs
// describes why curOffsRegs can be smaller than curOffsArgs.
_ASSERTE(curOffsRegs <= curOffsArgs);

#if VERIFY_GC_TABLES
_ASSERTE(*castto(table, unsigned short *)++ == 0xBABE);
#endif
Expand Down Expand Up @@ -2506,7 +2512,7 @@ unsigned scanArgRegTableI(PTR_CBYTE table,

/* Have we reached the instruction we're looking for? */

while (ptrOffs <= curOffs)
while (ptrOffs <= curOffsArgs)
{
unsigned val;

Expand Down Expand Up @@ -2543,10 +2549,14 @@ unsigned scanArgRegTableI(PTR_CBYTE table,
regNum reg;

ptrOffs += (val ) & 0x7;
if (ptrOffs > curOffs) {
if (ptrOffs > curOffsArgs) {
iptr = isThis = false;
goto REPORT_REFS;
}
else if (ptrOffs > curOffsRegs) {
iptr = isThis = false;
continue;
}

reg = (regNum)((val >> 3) & 0x7);
regMask = 1 << reg; // EAX,ECX,EDX,EBX,---,EBP,ESI,EDI
Expand Down Expand Up @@ -2606,7 +2616,7 @@ unsigned scanArgRegTableI(PTR_CBYTE table,
/* A small argument encoding */

ptrOffs += (val & 0x07);
if (ptrOffs > curOffs) {
if (ptrOffs > curOffsArgs) {
iptr = isThis = false;
goto REPORT_REFS;
}
Expand Down Expand Up @@ -2736,7 +2746,7 @@ unsigned scanArgRegTableI(PTR_CBYTE table,
/* non-ptr arg push */
_ASSERTE(!hasPartialArgInfo);
ptrOffs += (val & 0x07);
if (ptrOffs > curOffs) {
if (ptrOffs > curOffsArgs) {
iptr = isThis = false;
goto REPORT_REFS;
}
Expand Down Expand Up @@ -2858,6 +2868,7 @@ unsigned GetPushedArgSize(hdrInfo * info, PTR_CBYTE table, DWORD curOffs)
if (info->interruptible)
{
sz = scanArgRegTableI(skipToArgReg(*info, table),
curOffs,
curOffs,
info);
}
Expand Down Expand Up @@ -4436,7 +4447,15 @@ bool EECodeManager::EnumGcRefs( PREGDISPLAY pContext,

if (info.interruptible)
{
pushedSize = scanArgRegTableI(skipToArgReg(info, table), curOffs, &info);
// If we are not on the active stack frame, we need to report gc registers
// that are live before the call. The reason is that the liveness of gc registers
// may change across a call to a method that does not return. In this case the instruction
// after the call may be a jump target and a register that didn't have a live gc pointer
// before the call may have a live gc pointer after the jump. To make sure we report the
// registers that have live gc pointers before the call we subtract 1 from curOffs.
unsigned curOffsRegs = (flags & ActiveStackFrame) != 0 ? curOffs : curOffs - 1;

pushedSize = scanArgRegTableI(skipToArgReg(info, table), curOffsRegs, curOffs, &info);

RegMask regs = info.regMaskResult;
RegMask iregs = info.iregMaskResult;
Expand Down Expand Up @@ -5342,7 +5361,7 @@ OBJECTREF EECodeManager::GetInstance( PREGDISPLAY pContext,

if (info.interruptible)
{
stackDepth = scanArgRegTableI(skipToArgReg(info, table), (unsigned)relOffset, &info);
stackDepth = scanArgRegTableI(skipToArgReg(info, table), relOffset, relOffset, &info);
}
else
{
Expand Down Expand Up @@ -6046,6 +6065,7 @@ TADDR EECodeManager::GetAmbientSP(PREGDISPLAY pContext,
if (stateBuf->hdrInfoBody.interruptible)
{
baseSP += scanArgRegTableI(skipToArgReg(stateBuf->hdrInfoBody, table),
dwRelOffset,
dwRelOffset,
&stateBuf->hdrInfoBody);
}
Expand Down
84 changes: 84 additions & 0 deletions tests/src/Regressions/coreclr/GitHub_17398/test17398.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Runtime.CompilerServices;

// Repro case for CoreCLR 17398

class X
{
static int v;

string s;

public override string ToString() => s;

[MethodImpl(MethodImplOptions.NoInlining)]
X(int x)
{
s = "String" + x;
}

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

public static void T0(object o, int x)
{
GC.Collect(2);
throw new Exception(o.ToString());
}

[MethodImpl(MethodImplOptions.NoInlining)]
public static void Test()
{
object x1 = new X(1);
object x2 = new X(2);

if (v == 1)
{
// Generate enough pressure here to
// kill ESI so in linear flow it is dead
// at the call to T0
int w = v;
int x = v;
int y = v;
int z = v;

// Unbounded loop here forces fully interruptible GC
for (int i = 0; i < v; i++)
{
w++;
}

T0(x2, w + x + y + z);
}

// Encourage x1 to be in callee save (ESI)
F();

if (v == 2)
{
T0(x1, 0);
}
}

public static int Main()
{
v = 1;
int r = 0;

try
{
Test();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
r = 100;
}

return r;
}
}
41 changes: 41 additions & 0 deletions tests/src/Regressions/coreclr/GitHub_17398/test17398.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{E55A6F8B-B9E3-45CE-88F4-22AE70F606CB}</ProjectGuid>
<OutputType>Exe</OutputType>
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<CLRTestKind>BuildAndRun</CLRTestKind>
</PropertyGroup>
<!-- Default configurations to help VS understand the configurations -->
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
</PropertyGroup>
<ItemGroup>
<CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
<Visible>False</Visible>
</CodeAnalysisDependentAssemblyPaths>
</ItemGroup>
<PropertyGroup>
<DebugType></DebugType>
<Optimize>True</Optimize>
</PropertyGroup>
<ItemGroup>
<Compile Include="test17398.cs" />
</ItemGroup>
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../../Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj" />
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
<PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' ">
</PropertyGroup>
</Project>