summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/jit/flowgraph.cpp35
-rw-r--r--src/jit/importer.cpp29
-rw-r--r--src/jit/inline.h1
-rw-r--r--tests/src/JIT/opt/Devirtualization/arraypool.cs41
-rw-r--r--tests/src/JIT/opt/Devirtualization/arraypool.csproj38
-rw-r--r--tests/src/JIT/opt/Devirtualization/late1.cs45
-rw-r--r--tests/src/JIT/opt/Devirtualization/late1.csproj38
7 files changed, 208 insertions, 19 deletions
diff --git a/src/jit/flowgraph.cpp b/src/jit/flowgraph.cpp
index 7c5e264220..b2846ed7cf 100644
--- a/src/jit/flowgraph.cpp
+++ b/src/jit/flowgraph.cpp
@@ -5853,19 +5853,34 @@ void Compiler::fgFindBasicBlocks()
// or if the inlinee has GC ref locals.
if ((info.compRetNativeType != TYP_VOID) && ((retBlocks > 1) || impInlineInfo->HasGcRefLocals()))
{
- // The lifetime of this var might expand multiple BBs. So it is a long lifetime compiler temp.
- lvaInlineeReturnSpillTemp = lvaGrabTemp(false DEBUGARG("Inline return value spill temp"));
- lvaTable[lvaInlineeReturnSpillTemp].lvType = info.compRetNativeType;
+ // If we've spilled the ret expr to a temp we can reuse the temp
+ // as the inlinee return spill temp.
+ //
+ // Todo: see if it is even better to always use this existing temp
+ // for return values, even if we otherwise wouldn't need a return spill temp...
+ lvaInlineeReturnSpillTemp = impInlineInfo->inlineCandidateInfo->preexistingSpillTemp;
- // If the method returns a ref class, set the class of the spill temp
- // to the method's return value. We may update this later if it turns
- // out we can prove the method returns a more specific type.
- if (info.compRetType == TYP_REF)
+ if (lvaInlineeReturnSpillTemp != BAD_VAR_NUM)
+ {
+ // This temp should already have the type of the return value.
+ JITDUMP("\nInliner: re-using pre-existing spill temp V%02u\n", lvaInlineeReturnSpillTemp);
+ }
+ else
{
- CORINFO_CLASS_HANDLE retClassHnd = impInlineInfo->inlineCandidateInfo->methInfo.args.retTypeClass;
- if (retClassHnd != nullptr)
+ // The lifetime of this var might expand multiple BBs. So it is a long lifetime compiler temp.
+ lvaInlineeReturnSpillTemp = lvaGrabTemp(false DEBUGARG("Inline return value spill temp"));
+ lvaTable[lvaInlineeReturnSpillTemp].lvType = info.compRetNativeType;
+
+ // If the method returns a ref class, set the class of the spill temp
+ // to the method's return value. We may update this later if it turns
+ // out we can prove the method returns a more specific type.
+ if (info.compRetType == TYP_REF)
{
- lvaSetClass(lvaInlineeReturnSpillTemp, retClassHnd);
+ CORINFO_CLASS_HANDLE retClassHnd = impInlineInfo->inlineCandidateInfo->methInfo.args.retTypeClass;
+ if (retClassHnd != nullptr)
+ {
+ lvaSetClass(lvaInlineeReturnSpillTemp, retClassHnd);
+ }
}
}
}
diff --git a/src/jit/importer.cpp b/src/jit/importer.cpp
index a8a1d016be..1037c9d7db 100644
--- a/src/jit/importer.cpp
+++ b/src/jit/importer.cpp
@@ -2219,6 +2219,16 @@ bool Compiler::impSpillStackEntry(unsigned level,
{
CORINFO_CLASS_HANDLE stkHnd = verCurrentState.esStack[level].seTypeInfo.GetClassHandle();
lvaSetClass(tnum, tree, stkHnd);
+
+ // If we're assigning a GT_RET_EXPR, note the temp over on the call,
+ // so the inliner can use it in case it needs a return spill temp.
+ if (tree->OperGet() == GT_RET_EXPR)
+ {
+ JITDUMP("\n*** see V%02u = GT_RET_EXPR, noting temp\n", tnum);
+ GenTree* call = tree->gtRetExpr.gtInlineCandidate;
+ InlineCandidateInfo* ici = call->gtCall.gtInlineCandidateInfo;
+ ici->preexistingSpillTemp = tnum;
+ }
}
// The tree type may be modified by impAssignTempGen, so use the type of the lclVar.
@@ -18047,15 +18057,16 @@ void Compiler::impCheckCanInline(GenTree* call,
InlineCandidateInfo* pInfo;
pInfo = new (pParam->pThis, CMK_Inlining) InlineCandidateInfo;
- pInfo->dwRestrictions = dwRestrictions;
- pInfo->methInfo = methInfo;
- pInfo->methAttr = pParam->methAttr;
- pInfo->clsHandle = clsHandle;
- pInfo->clsAttr = clsAttr;
- pInfo->fncRetType = fncRetType;
- pInfo->exactContextHnd = pParam->exactContextHnd;
- pInfo->ilCallerHandle = pParam->pThis->info.compMethodHnd;
- pInfo->initClassResult = initClassResult;
+ pInfo->dwRestrictions = dwRestrictions;
+ pInfo->methInfo = methInfo;
+ pInfo->methAttr = pParam->methAttr;
+ pInfo->clsHandle = clsHandle;
+ pInfo->clsAttr = clsAttr;
+ pInfo->fncRetType = fncRetType;
+ pInfo->exactContextHnd = pParam->exactContextHnd;
+ pInfo->ilCallerHandle = pParam->pThis->info.compMethodHnd;
+ pInfo->initClassResult = initClassResult;
+ pInfo->preexistingSpillTemp = BAD_VAR_NUM;
*(pParam->ppInlineCandidateInfo) = pInfo;
diff --git a/src/jit/inline.h b/src/jit/inline.h
index 0503b074dd..551341e2e6 100644
--- a/src/jit/inline.h
+++ b/src/jit/inline.h
@@ -509,6 +509,7 @@ struct InlineCandidateInfo
CORINFO_CONTEXT_HANDLE exactContextHnd;
bool exactContextNeedsRuntimeLookup;
CorInfoInitClassResult initClassResult;
+ unsigned preexistingSpillTemp;
};
// InlArgInfo describes inline candidate argument properties.
diff --git a/tests/src/JIT/opt/Devirtualization/arraypool.cs b/tests/src/JIT/opt/Devirtualization/arraypool.cs
new file mode 100644
index 0000000000..8d9b35993c
--- /dev/null
+++ b/tests/src/JIT/opt/Devirtualization/arraypool.cs
@@ -0,0 +1,41 @@
+// 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.Buffers;
+using System.Runtime.CompilerServices;
+
+class X
+{
+ static int N;
+ static int J;
+ static int K;
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static void S() { J = N / 2; K = J; }
+
+ // We expect calls to Rent and Return to be
+ // devirtualized.
+ public static int Main()
+ {
+ N = 100;
+ byte[] buffer = ArrayPool<byte>.Shared.Rent(N);
+ int r = -1;
+
+ try {
+ S();
+ buffer[J] = 100;
+ r = (int) buffer[K];
+ }
+ finally
+ {
+ if (buffer != null)
+ {
+ ArrayPool<byte>.Shared.Return(buffer);
+ }
+ }
+
+ return r;
+ }
+}
diff --git a/tests/src/JIT/opt/Devirtualization/arraypool.csproj b/tests/src/JIT/opt/Devirtualization/arraypool.csproj
new file mode 100644
index 0000000000..d5105da346
--- /dev/null
+++ b/tests/src/JIT/opt/Devirtualization/arraypool.csproj
@@ -0,0 +1,38 @@
+<?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>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <FileAlignment>512</FileAlignment>
+ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT .0\UITestExtensionPackages</ReferencePath>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+ <NuGetPackageImportStamp>7a9bfb7d</NuGetPackageImportStamp>
+ </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>None</DebugType>
+ <Optimize>True</Optimize>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="arraypool.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> \ No newline at end of file
diff --git a/tests/src/JIT/opt/Devirtualization/late1.cs b/tests/src/JIT/opt/Devirtualization/late1.cs
new file mode 100644
index 0000000000..69508d109e
--- /dev/null
+++ b/tests/src/JIT/opt/Devirtualization/late1.cs
@@ -0,0 +1,45 @@
+// 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;
+
+public class B
+{
+ public virtual int F(int x) { return x + 33; }
+}
+
+public class D : B
+{
+ public override int F(int x) { return x + 44; }
+}
+
+public class Q
+{
+ static int V(int x)
+ {
+ return x;
+ }
+
+ // calls to B will use a return spill temp since
+ // B has multiple return sites.
+ //
+ // Jit will initially type this temp as 'B' but then
+ // sharpen type type to 'B exact' or 'D exact' if the
+ // 'b' is a constant at the call site.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static B Choose(bool b)
+ {
+ return b ? new B() : new D();
+ }
+
+ // The calls to F should be devirtualized late
+ public static int Main(string[] args)
+ {
+ int v0 = Choose(false).F(V(67));
+ B b = Choose(true);
+ int v1 = b.F(V(56));
+ return v0 + v1 - 100;
+ }
+}
diff --git a/tests/src/JIT/opt/Devirtualization/late1.csproj b/tests/src/JIT/opt/Devirtualization/late1.csproj
new file mode 100644
index 0000000000..66a4233689
--- /dev/null
+++ b/tests/src/JIT/opt/Devirtualization/late1.csproj
@@ -0,0 +1,38 @@
+<?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>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <FileAlignment>512</FileAlignment>
+ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT .0\UITestExtensionPackages</ReferencePath>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+ <NuGetPackageImportStamp>7a9bfb7d</NuGetPackageImportStamp>
+ </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>None</DebugType>
+ <Optimize>True</Optimize>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="late1.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> \ No newline at end of file