Lower fast tail call can replace local variables (holding Caller stack arguments) with new temps in order to set up Callee stack arguments correctly. This involves patching corresponding LCL_VAR and LCL_VAR_ADDR nodes and replacing them with the location of a new temp.
This was not done for control expression which continued pointing to the old location and could contain a Callee argument.
LowerArgsForCall(call);
// note that everything generated from this point on runs AFTER the outgoing args are placed
- GenTree* result = nullptr;
+ GenTree* controlExpr = nullptr;
// for x86, this is where we record ESP for checking later to make sure stack is balanced
// an indirect call.
if (call->IsDelegateInvoke())
{
- result = LowerDelegateInvoke(call);
+ controlExpr = LowerDelegateInvoke(call);
}
else
{
switch (call->gtFlags & GTF_CALL_VIRT_KIND_MASK)
{
case GTF_CALL_VIRT_STUB:
- result = LowerVirtualStubCall(call);
+ controlExpr = LowerVirtualStubCall(call);
break;
case GTF_CALL_VIRT_VTABLE:
// stub dispatching is off or this is not a virtual call (could be a tailcall)
- result = LowerVirtualVtableCall(call);
+ controlExpr = LowerVirtualVtableCall(call);
break;
case GTF_CALL_NONVIRT:
if (call->IsUnmanaged())
{
- result = LowerNonvirtPinvokeCall(call);
+ controlExpr = LowerNonvirtPinvokeCall(call);
}
else if (call->gtCallType == CT_INDIRECT)
{
- result = LowerIndirectNonvirtCall(call);
+ controlExpr = LowerIndirectNonvirtCall(call);
}
else
{
- result = LowerDirectCall(call);
+ controlExpr = LowerDirectCall(call);
}
break;
if (call->IsTailCallViaHelper())
{
// Either controlExpr or gtCallAddr must contain real call target.
- if (result == nullptr)
+ if (controlExpr == nullptr)
{
assert(call->gtCallType == CT_INDIRECT);
assert(call->gtCallAddr != nullptr);
- result = call->gtCallAddr;
+ controlExpr = call->gtCallAddr;
}
- result = LowerTailCallViaHelper(call, result);
- }
- else if (call->IsFastTailCall())
- {
- LowerFastTailCall(call);
+ controlExpr = LowerTailCallViaHelper(call, controlExpr);
}
- if (result != nullptr)
+ if (controlExpr != nullptr)
{
- LIR::Range resultRange = LIR::SeqTree(comp, result);
+ LIR::Range controlExprRange = LIR::SeqTree(comp, controlExpr);
JITDUMP("results of lowering call:\n");
- DISPRANGE(resultRange);
+ DISPRANGE(controlExprRange);
GenTree* insertionPoint = call;
if (!call->IsTailCallViaHelper())
}
}
- ContainCheckRange(resultRange);
- BlockRange().InsertBefore(insertionPoint, std::move(resultRange));
+ ContainCheckRange(controlExprRange);
+ BlockRange().InsertBefore(insertionPoint, std::move(controlExprRange));
- call->gtControlExpr = result;
+ call->gtControlExpr = controlExpr;
+ }
+ if (call->IsFastTailCall())
+ {
+ // Lower fast tail call can introduce new temps to set up args correctly for Callee.
+ // This involves patching LCL_VAR and LCL_VAR_ADDR nodes holding Caller stack args
+ // and replacing them with a new temp. Control expr also can contain nodes that need
+ // to be patched.
+ // Therefore lower fast tail call must be done after controlExpr is inserted into LIR.
+ // There is one side effect which is flipping the order of PME and control expression
+ // since LowerFastTailCall calls InsertPInvokeMethodEpilog.
+ LowerFastTailCall(call);
}
if (comp->opts.IsJit64Compat())
--- /dev/null
+// 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.
+
+// GitHub 20625: bug with LowerFastTailCall on platforms where fast tail call is supported.
+// Lower fast tail call can replace local variables (holding Caller stack arguments) with new temps
+// in order to set up Callee stack arguments correctly.
+// This involves patching corresponding LCL_VAR and LCL_VAR_ADDR nodes and replacing them with the location
+// of a new temp. This was not done for control expression which continued pointing to the old location
+// and could contain a Callee argument.
+// In this example, in Der1:M during tail call Caller arg6 points to the same location as Callee arg6
+// (which is null) and causes NullReferenceException.
+
+.assembly extern System.Runtime
+{
+}
+
+.assembly GitHub_20625
+{
+}
+
+.class private abstract auto ansi beforefieldinit Base
+ extends [System.Runtime]System.Object
+{
+ .method public hidebysig newslot abstract virtual
+ instance void M(int32 arg1, int32 arg2, int32 arg3, int32 arg4, int32 arg5, class Base arg6) cil managed
+ {
+ }
+
+ .method family hidebysig specialname rtspecialname
+ instance void .ctor() cil managed
+ {
+ .maxstack 8
+ IL_0000: ldarg.0
+ IL_0001: call instance void [System.Runtime]System.Object::.ctor()
+ IL_0006: ret
+ }
+}
+
+.class private auto ansi beforefieldinit Der1
+ extends Base
+{
+ .method public hidebysig virtual instance void
+ M(int32 arg1, int32 arg2, int32 arg3, int32 arg4, int32 arg5, class Base arg6) cil managed nooptimization
+ {
+ .maxstack 8
+ IL_0000: ldarg.s arg6
+ IL_0002: ldarg.1
+ IL_0003: ldarg.2
+ IL_0004: ldarg.3
+ IL_0005: ldarg.s arg4
+ IL_0007: ldarg.s arg5
+ IL_0009: ldnull
+ IL_000a: tail.
+ callvirt instance void Base::M(int32, int32, int32, int32, int32, class Base)
+ IL_000f: ret
+ }
+
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor() cil managed
+ {
+ .maxstack 8
+ IL_0000: ldarg.0
+ IL_0001: call instance void Base::.ctor()
+ IL_0006: ret
+ }
+
+}
+
+.class private auto ansi beforefieldinit Der2
+ extends Base
+{
+ .method public hidebysig virtual instance void
+ M(int32 arg1, int32 arg2, int32 arg3, int32 arg4, int32 arg5, class Base arg6) cil managed
+ {
+ .maxstack 8
+ IL_0000: ret
+ }
+
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor() cil managed
+ {
+ .maxstack 8
+ IL_0000: ldarg.0
+ IL_0001: call instance void Base::.ctor()
+ IL_0006: ret
+ }
+}
+
+.class private auto ansi beforefieldinit Program
+ extends [System.Runtime]System.Object
+{
+ .method private hidebysig static int32
+ Run(class Base der1, class Base der2) cil managed noinlining
+ {
+ .maxstack 7
+ .locals init (int32 V_0)
+ .try
+ {
+ IL_0000: ldarg.0
+ IL_0001: ldc.i4.2
+ IL_0002: ldc.i4.0
+ IL_0003: ldc.i4.6
+ IL_0004: ldc.i4.2
+ IL_0005: ldc.i4.5
+ IL_0006: ldarg.1
+ IL_0007: callvirt instance void Base::M(int32, int32, int32, int32, int32, class Base)
+ IL_000c: ldc.i4.s 100
+ IL_000e: stloc.0
+ IL_000f: leave.s IL_0016
+
+ }
+ catch [System.Runtime]System.NullReferenceException
+ {
+ IL_0011: pop
+ IL_0012: ldc.i4.1
+ IL_0013: stloc.0
+ IL_0014: leave.s IL_0016
+ }
+ IL_0016: ldloc.0
+ IL_0017: ret
+ }
+
+ .method private hidebysig static int32
+ Main(string[] args) cil managed
+ {
+ .entrypoint
+
+ .maxstack 2
+ .locals init (class Der2 V_0)
+ IL_0000: newobj instance void Der1::.ctor()
+ IL_0005: newobj instance void Der2::.ctor()
+ IL_000a: stloc.0
+ IL_000b: ldloc.0
+ IL_000c: call int32 Program::Run(class Base, class Base)
+ IL_0011: ret
+ }
+
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor() cil managed
+ {
+ .maxstack 8
+ IL_0000: ldarg.0
+ IL_0001: call instance void [System.Runtime]System.Object::.ctor()
+ IL_0006: ret
+ }
+}
--- /dev/null
+<?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>
+ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+ </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="GitHub_20625.il" />
+ </ItemGroup>
+ <ItemGroup>
+ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+ <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' ">
+ </PropertyGroup>
+</Project>