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

JIT: streamline temp usage for returns #20640

Merged
merged 1 commit into from
Oct 29, 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
35 changes: 25 additions & 10 deletions src/jit/flowgraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
}
Expand Down
29 changes: 20 additions & 9 deletions src/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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;

Expand Down
1 change: 1 addition & 0 deletions src/jit/inline.h
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,7 @@ struct InlineCandidateInfo
CORINFO_CONTEXT_HANDLE exactContextHnd;
bool exactContextNeedsRuntimeLookup;
CorInfoInitClassResult initClassResult;
unsigned preexistingSpillTemp;
};

// InlArgInfo describes inline candidate argument properties.
Expand Down
41 changes: 41 additions & 0 deletions tests/src/JIT/opt/Devirtualization/arraypool.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
38 changes: 38 additions & 0 deletions tests/src/JIT/opt/Devirtualization/arraypool.csproj
Original file line number Diff line number Diff line change
@@ -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>
45 changes: 45 additions & 0 deletions tests/src/JIT/opt/Devirtualization/late1.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
38 changes: 38 additions & 0 deletions tests/src/JIT/opt/Devirtualization/late1.csproj
Original file line number Diff line number Diff line change
@@ -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>