forked from dotnet/coreclr
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
On x86, structs are passed by value on the stack. We copy structs to the stack in various ways, but one way is to first subtract the size of the struct and then use a "rep movsb" instruction. If the struct we are passing is sufficiently large, this can cause us to miss the stack guard page. So, introduce stack probes for these struct copies. It turns out the stack pointer after prolog probing can be sitting near the very end of the guard page (one `STACK_ALIGN` slot before the end, which allows a "call" instruction which pushes its return address to touch the guard page with the return address push). We don't want to probe with every argument push, though. So change the prolog probing to insert an "extra" touch at the final SP location if the previous touch was "too far" away, leaving at least some buffer zone for un-probed SP adjustments. I chose this to be the size of the largest SIMD register, which also can get copied to the argument stack with a "SUB;MOV" sequence. Added several test case variations showing different large stack probe situations. Fixes #23796
- Loading branch information
1 parent
a6b1e97
commit 98b6a5b
Showing
11 changed files
with
594 additions
and
78 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
// 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. | ||
|
||
// Passing a very large struct by value on the stack, on arm32 and x86, | ||
// can cause it to be copied from a temp to the outgoing space without | ||
// probing the stack. | ||
|
||
using System; | ||
using System.Runtime.InteropServices; | ||
using System.Runtime.CompilerServices; | ||
|
||
namespace BigFrames | ||
{ | ||
|
||
[StructLayout(LayoutKind.Explicit)] | ||
public struct LargeStruct | ||
{ | ||
[FieldOffset(0)] | ||
public int i1; | ||
[FieldOffset(65512)] | ||
public int i2; | ||
} | ||
|
||
public class Test | ||
{ | ||
public static int iret = 1; | ||
|
||
[MethodImplAttribute(MethodImplOptions.NoInlining)] | ||
public static void TestWrite(int i1, int i2, int i3, int i4, LargeStruct s) | ||
{ | ||
Console.Write("Enter TestWrite: "); | ||
Console.WriteLine(i1 + i2 + i3 + i4 + s.i2); | ||
iret = 100; | ||
} | ||
|
||
[MethodImplAttribute(MethodImplOptions.NoInlining)] | ||
public static void Test1() | ||
{ | ||
Console.WriteLine("Enter Test1"); | ||
LargeStruct s = new LargeStruct(); | ||
s.i2 = 5; | ||
TestWrite(1, 2, 3, 4, s); // 4 int reg args, then struct stack arg | ||
} | ||
|
||
[MethodImplAttribute(MethodImplOptions.NoInlining)] | ||
public static void Escape(ref LargeStruct s) | ||
{ | ||
} | ||
|
||
// A lot of time the stack when we are called has a bunch of committed pages | ||
// before the guard page. So eat up a bunch of stack before doing our test, | ||
// where we want to be near the guard page. | ||
[MethodImplAttribute(MethodImplOptions.NoInlining)] | ||
public static void EatStackThenTest1(int level = 0) | ||
{ | ||
LargeStruct s = new LargeStruct(); | ||
s.i2 = level; | ||
Escape(ref s); | ||
|
||
if (level < 20) | ||
{ | ||
EatStackThenTest1(level + 1); | ||
} | ||
else | ||
{ | ||
Test1(); | ||
} | ||
} | ||
|
||
public static int Main() | ||
{ | ||
Test1(); // force JIT of this | ||
|
||
EatStackThenTest1(); // If that didn't fail, eat stack then try again. | ||
|
||
if (iret == 100) | ||
{ | ||
Console.WriteLine("TEST PASSED"); | ||
} | ||
else | ||
{ | ||
Console.WriteLine("TEST FAILED"); | ||
} | ||
return iret; | ||
} | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
tests/src/JIT/Methodical/largeframes/skip3/skippage3.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
<?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> | ||
<AssemblyName>$(MSBuildProjectName)</AssemblyName> | ||
<SchemaVersion>2.0</SchemaVersion> | ||
<ProjectGuid>{43F24741-6FD9-4593-92FA-D3252B540A92}</ProjectGuid> | ||
<OutputType>Exe</OutputType> | ||
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> | ||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir> | ||
<CLRTestPriority>1</CLRTestPriority> | ||
</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>PdbOnly</DebugType> | ||
<Optimize>True</Optimize> | ||
</PropertyGroup> | ||
<ItemGroup> | ||
<Compile Include="skippage3.cs" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> | ||
</ItemGroup> | ||
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" /> | ||
<PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "></PropertyGroup> | ||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// 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. | ||
|
||
// Passing a very large struct by value on the stack, on arm32 and x86, | ||
// can cause it to be copied from a temp to the outgoing space without | ||
// probing the stack. | ||
|
||
using System; | ||
using System.Runtime.InteropServices; | ||
using System.Runtime.CompilerServices; | ||
|
||
namespace BigFrames | ||
{ | ||
|
||
[StructLayout(LayoutKind.Explicit)] | ||
public struct LargeStructWithRef | ||
{ | ||
[FieldOffset(0)] | ||
public int i1; | ||
[FieldOffset(65500)] | ||
public Object o1; | ||
} | ||
|
||
public class Test | ||
{ | ||
public static int iret = 1; | ||
|
||
[MethodImplAttribute(MethodImplOptions.NoInlining)] | ||
public static void TestWrite(LargeStructWithRef s) | ||
{ | ||
Console.Write("Enter TestWrite: "); | ||
Console.WriteLine(s.o1.GetHashCode()); | ||
iret = 100; | ||
} | ||
|
||
[MethodImplAttribute(MethodImplOptions.NoInlining)] | ||
public static void Test1() | ||
{ | ||
Console.WriteLine("Enter Test1"); | ||
LargeStructWithRef s = new LargeStructWithRef(); | ||
s.o1 = new Object(); | ||
TestWrite(s); | ||
} | ||
|
||
public static int Main() | ||
{ | ||
Test1(); | ||
|
||
if (iret == 100) | ||
{ | ||
Console.WriteLine("TEST PASSED"); | ||
} | ||
else | ||
{ | ||
Console.WriteLine("TEST FAILED"); | ||
} | ||
return iret; | ||
} | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
tests/src/JIT/Methodical/largeframes/skip4/skippage4.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
<?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> | ||
<AssemblyName>$(MSBuildProjectName)</AssemblyName> | ||
<SchemaVersion>2.0</SchemaVersion> | ||
<ProjectGuid>{43F24741-6FD9-4593-92FA-D3252B540A92}</ProjectGuid> | ||
<OutputType>Exe</OutputType> | ||
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> | ||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir> | ||
<CLRTestPriority>1</CLRTestPriority> | ||
</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>PdbOnly</DebugType> | ||
<Optimize>True</Optimize> | ||
</PropertyGroup> | ||
<ItemGroup> | ||
<Compile Include="skippage4.cs" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> | ||
</ItemGroup> | ||
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" /> | ||
<PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "></PropertyGroup> | ||
</Project> |
62 changes: 62 additions & 0 deletions
62
tests/src/JIT/Methodical/largeframes/skip4/skippage4_save.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
// 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. | ||
|
||
// Passing a very large struct by value on the stack, on arm32 and x86, | ||
// can cause it to be copied from a temp to the outgoing space without | ||
// probing the stack. | ||
|
||
using System; | ||
using System.Runtime.InteropServices; | ||
using System.Runtime.CompilerServices; | ||
|
||
namespace BigFrames | ||
{ | ||
|
||
[StructLayout(LayoutKind.Explicit)] | ||
public struct Struct65500ref | ||
{ | ||
[FieldOffset(0)] | ||
public int i1; | ||
[FieldOffset(65496)] | ||
public Object o1; | ||
} | ||
|
||
public class Test | ||
{ | ||
public static int iret = 1; | ||
|
||
[MethodImplAttribute(MethodImplOptions.NoInlining)] | ||
public static void TestWrite(int i1, int i2, int i3, int i4, Struct65500ref s) | ||
{ | ||
Console.Write("Enter TestWrite: "); | ||
Console.WriteLine(i1 + i2 + i3 + i4 + s.o1.GetHashCode()); | ||
iret = 100; | ||
// Test1(); // recurse | ||
} | ||
|
||
[MethodImplAttribute(MethodImplOptions.NoInlining)] | ||
public static void Test1() | ||
{ | ||
Console.WriteLine("Enter Test1"); | ||
Struct65500ref s = new Struct65500ref(); | ||
s.o1 = new Object(); | ||
TestWrite(1, 2, 3, 4, s); // 4 int reg args, then struct stack arg | ||
} | ||
|
||
public static int Main() | ||
{ | ||
Test1(); | ||
|
||
if (iret == 100) | ||
{ | ||
Console.WriteLine("TEST PASSED"); | ||
} | ||
else | ||
{ | ||
Console.WriteLine("TEST FAILED"); | ||
} | ||
return iret; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// 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; | ||
using System.Runtime.InteropServices; | ||
|
||
class Program | ||
{ | ||
[StructLayout(LayoutKind.Sequential)] | ||
unsafe struct S | ||
{ | ||
fixed byte x[65500]; | ||
} | ||
|
||
class C | ||
{ | ||
public S s; | ||
} | ||
|
||
static int Main() => Test(new C()); | ||
|
||
[MethodImpl(MethodImplOptions.NoInlining)] | ||
static void Call(int r0, int r1, int r2, int r3, int r4, int r5, int r6, S s) | ||
{ | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.NoInlining)] | ||
static int Test(C c) | ||
{ | ||
Call(0, 1, 2, 3, 4, 5, 42, c.s); | ||
Console.WriteLine("TEST PASSED"); | ||
return 100; // If we don't crash, we pass | ||
} | ||
} |
Oops, something went wrong.