summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/jit/compiler.h3
-rw-r--r--src/jit/flowgraph.cpp4
-rw-r--r--src/jit/importer.cpp29
-rw-r--r--src/jit/indirectcalltransformer.cpp6
-rw-r--r--tests/src/JIT/Regression/JitBlue/DevDiv_754566/DevDiv_754566.il106
-rw-r--r--tests/src/JIT/Regression/JitBlue/DevDiv_754566/DevDiv_754566.ilprog35
-rw-r--r--tests/src/JIT/Regression/JitBlue/DevDiv_754566/test.cs31
7 files changed, 206 insertions, 8 deletions
diff --git a/src/jit/compiler.h b/src/jit/compiler.h
index 21a426e3f1..6c93434dea 100644
--- a/src/jit/compiler.h
+++ b/src/jit/compiler.h
@@ -3329,7 +3329,8 @@ public:
unsigned* methodFlags,
CORINFO_CONTEXT_HANDLE* contextHandle,
CORINFO_CONTEXT_HANDLE* exactContextHandle,
- bool isLateDevirtualization);
+ bool isLateDevirtualization,
+ bool isExplicitTailCall);
//=========================================================================
// PROTECTED
diff --git a/src/jit/flowgraph.cpp b/src/jit/flowgraph.cpp
index eedb1aa58f..86f18ca404 100644
--- a/src/jit/flowgraph.cpp
+++ b/src/jit/flowgraph.cpp
@@ -22552,7 +22552,9 @@ Compiler::fgWalkResult Compiler::fgLateDevirtualization(GenTree** pTree, fgWalkD
unsigned methodFlags = 0;
CORINFO_CONTEXT_HANDLE context = nullptr;
const bool isLateDevirtualization = true;
- comp->impDevirtualizeCall(call, &method, &methodFlags, &context, nullptr, isLateDevirtualization);
+ bool explicitTailCall = (call->gtCall.gtCallMoreFlags & GTF_CALL_M_EXPLICIT_TAILCALL) != 0;
+ comp->impDevirtualizeCall(call, &method, &methodFlags, &context, nullptr, isLateDevirtualization,
+ explicitTailCall);
}
}
else if (tree->OperGet() == GT_ASG)
diff --git a/src/jit/importer.cpp b/src/jit/importer.cpp
index 47ed202e1b..27ea06abaf 100644
--- a/src/jit/importer.cpp
+++ b/src/jit/importer.cpp
@@ -5276,7 +5276,7 @@ BOOL Compiler::verIsSDArray(typeInfo ti)
typeInfo Compiler::verGetArrayElemType(typeInfo arrayObjectType)
{
- assert(!arrayObjectType.IsNullObjRef()); // you need to check for null explictly since that is a success case
+ assert(!arrayObjectType.IsNullObjRef()); // you need to check for null explicitly since that is a success case
if (!verIsSDArray(arrayObjectType))
{
@@ -8590,9 +8590,11 @@ var_types Compiler::impImportCall(OPCODE opcode,
assert(obj->gtType == TYP_REF);
// See if we can devirtualize.
+
+ bool explicitTailCall = (tailCall & PREFIX_TAILCALL_EXPLICIT) != 0;
const bool isLateDevirtualization = false;
impDevirtualizeCall(call->AsCall(), &callInfo->hMethod, &callInfo->methodFlags, &callInfo->contextHandle,
- &exactContextHnd, isLateDevirtualization);
+ &exactContextHnd, isLateDevirtualization, explicitTailCall);
}
if (impIsThis(obj))
@@ -8742,7 +8744,6 @@ DONE:
if (info.compCompHnd->canTailCall(info.compMethodHnd, methHnd, exactCalleeHnd, explicitTailCall))
{
- canTailCall = true;
if (explicitTailCall)
{
// In case of explicit tail calls, mark it so that it is not considered
@@ -20174,6 +20175,7 @@ bool Compiler::IsMathIntrinsic(GenTree* tree)
// contextHandle -- [IN/OUT] context handle for the call. Updated iff call devirtualized.
// exactContextHnd -- [OUT] updated context handle iff call devirtualized
// isLateDevirtualization -- if devirtualization is happening after importation
+// isExplicitTailCalll -- [IN] true if we plan on using an explicit tail call
//
// Notes:
// Virtual calls in IL will always "invoke" the base class method.
@@ -20207,7 +20209,8 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
unsigned* methodFlags,
CORINFO_CONTEXT_HANDLE* contextHandle,
CORINFO_CONTEXT_HANDLE* exactContextHandle,
- bool isLateDevirtualization)
+ bool isLateDevirtualization,
+ bool isExplicitTailCall)
{
assert(call != nullptr);
assert(method != nullptr);
@@ -20558,6 +20561,12 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
{
JITDUMP("Now have direct call to boxed entry point, looking for unboxed entry point\n");
+ if (isExplicitTailCall)
+ {
+ JITDUMP("Call is an explicit tail call, we cannot perform an unbox\n");
+ return;
+ }
+
// Note for some shared methods the unboxed entry point requires an extra parameter.
bool requiresInstMethodTableArg = false;
CORINFO_METHOD_HANDLE unboxedEntryMethod =
@@ -20640,6 +20649,18 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
call->gtCallMethHnd = unboxedEntryMethod;
call->gtCallMoreFlags |= GTF_CALL_M_UNBOXED;
derivedMethod = unboxedEntryMethod;
+
+#if FEATURE_TAILCALL_OPT
+ if (call->IsImplicitTailCall())
+ {
+ JITDUMP("Clearing the implicit tail call flag\n");
+
+ // If set, we clear the implicit tail call flag
+ // as we just introduced a new address taken local variable
+ //
+ call->gtCallMoreFlags &= ~GTF_CALL_M_IMPLICIT_TAILCALL;
+ }
+#endif // FEATURE_TAILCALL_OPT
}
else
{
diff --git a/src/jit/indirectcalltransformer.cpp b/src/jit/indirectcalltransformer.cpp
index 85d6d227cb..e0782b5b73 100644
--- a/src/jit/indirectcalltransformer.cpp
+++ b/src/jit/indirectcalltransformer.cpp
@@ -676,14 +676,16 @@ private:
JITDUMP("Direct call [%06u] in block BB%02u\n", compiler->dspTreeID(call), thenBlock->bbNum);
- // Then invoke impDevirtualizeCall do actually
+ // Then invoke impDevirtualizeCall to actually
// transform the call for us. It should succeed.... as we have
// now provided an exact typed this.
CORINFO_METHOD_HANDLE methodHnd = inlineInfo->methInfo.ftn;
unsigned methodFlags = inlineInfo->methAttr;
CORINFO_CONTEXT_HANDLE context = inlineInfo->exactContextHnd;
const bool isLateDevirtualization = true;
- compiler->impDevirtualizeCall(call, &methodHnd, &methodFlags, &context, nullptr, isLateDevirtualization);
+ bool explicitTailCall = (call->gtCall.gtCallMoreFlags & GTF_CALL_M_EXPLICIT_TAILCALL) != 0;
+ compiler->impDevirtualizeCall(call, &methodHnd, &methodFlags, &context, nullptr, isLateDevirtualization,
+ explicitTailCall);
// Presumably devirt might fail? If so we should try and avoid
// making this a guarded devirt candidate instead of ending
diff --git a/tests/src/JIT/Regression/JitBlue/DevDiv_754566/DevDiv_754566.il b/tests/src/JIT/Regression/JitBlue/DevDiv_754566/DevDiv_754566.il
new file mode 100644
index 0000000000..21290d24da
--- /dev/null
+++ b/tests/src/JIT/Regression/JitBlue/DevDiv_754566/DevDiv_754566.il
@@ -0,0 +1,106 @@
+// 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.
+
+
+// Metadata version: v4.0.30319
+.assembly extern mscorlib
+{
+ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
+ .ver 4:0:0:0
+}
+.assembly test
+{
+ .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
+ .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
+ 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows.
+ .hash algorithm 0x00008004
+ .ver 0:0:0:0
+}
+.module test.exe
+// MVID: {A80A87C4-1DDB-4F93-AB31-444266FDFA55}
+.imagebase 0x00400000
+.file alignment 0x00000200
+.stackreserve 0x00100000
+.subsystem 0x0003 // WINDOWS_CUI
+.corflags 0x00000001 // ILONLY
+// Image base: 0x0000024A58020000
+
+
+// =============== CLASS MEMBERS DECLARATION ===================
+
+.class private auto ansi beforefieldinit Program
+ extends [mscorlib]System.Object
+{
+ .method public hidebysig instance string
+ Test(int32 val) cil managed noinlining
+ {
+ // This testcase ensures that we don't perform devirtualization
+ // via an unboxing optimization, for the callvirt below.
+
+ // Code size 12 (0xc)
+ .maxstack 8
+ IL_0000: ldarg.1
+ IL_0001: box [mscorlib]System.Int32
+ tail.
+ IL_0006: callvirt instance string [mscorlib]System.Object::ToString()
+ IL_000b: ret
+ } // end of method Program::Test
+
+ .method private hidebysig static int32
+ Main(string[] args) cil managed
+ {
+ .entrypoint
+ // Code size 73 (0x49)
+ .maxstack 2
+ .locals init (int32 V_0,
+ class Program V_1,
+ string V_2)
+ IL_0000: ldc.i4.m1
+ IL_0001: stloc.0
+ IL_0002: newobj instance void Program::.ctor()
+ IL_0007: stloc.1
+ IL_0008: ldloc.1
+ IL_0009: ldc.i4.s 42
+ IL_000b: callvirt instance string Program::Test(int32)
+ IL_0010: stloc.2
+ IL_0011: ldloc.2
+ IL_0012: ldstr "42"
+ IL_0017: call bool [mscorlib]System.String::op_Equality(string,
+ string)
+ IL_001c: brfalse.s IL_002d
+
+ IL_001e: ldstr "=== PASSED ==="
+ IL_0023: call void [mscorlib]System.Console::WriteLine(string)
+ IL_0028: ldc.i4.s 100
+ IL_002a: stloc.0
+ IL_002b: br.s IL_0047
+
+ IL_002d: ldstr "result shoudl be 42, is= "
+ IL_0032: ldloc.2
+ IL_0033: call string [mscorlib]System.String::Concat(string,
+ string)
+ IL_0038: call void [mscorlib]System.Console::WriteLine(string)
+ IL_003d: ldstr "+++ FAILED +++"
+ IL_0042: call void [mscorlib]System.Console::WriteLine(string)
+ IL_0047: ldloc.0
+ IL_0048: ret
+ } // end of method Program::Main
+
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor() cil managed
+ {
+ // Code size 7 (0x7)
+ .maxstack 8
+ IL_0000: ldarg.0
+ IL_0001: call instance void [mscorlib]System.Object::.ctor()
+ IL_0006: ret
+ } // end of method Program::.ctor
+
+} // end of class Program
+
+
+// =============================================================
+
+// *********** DISASSEMBLY COMPLETE ***********************
+// WARNING: Created Win32 resource file test2.res
diff --git a/tests/src/JIT/Regression/JitBlue/DevDiv_754566/DevDiv_754566.ilprog b/tests/src/JIT/Regression/JitBlue/DevDiv_754566/DevDiv_754566.ilprog
new file mode 100644
index 0000000000..d30b4aeb0c
--- /dev/null
+++ b/tests/src/JIT/Regression/JitBlue/DevDiv_754566/DevDiv_754566.ilprog
@@ -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>{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>
+ <CLRTestPriority>0</CLRTestPriority>
+ </PropertyGroup>
+ <!-- Default configurations to help VS understand the configurations -->
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "></PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "></PropertyGroup>
+ <PropertyGroup>
+ <DebugType>PdbOnly</DebugType>
+ <Optimize>True</Optimize>
+ </PropertyGroup>
+ <ItemGroup>
+ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+ <Visible>False</Visible>
+ </CodeAnalysisDependentAssemblyPaths>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="DevDiv_754566.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> \ No newline at end of file
diff --git a/tests/src/JIT/Regression/JitBlue/DevDiv_754566/test.cs b/tests/src/JIT/Regression/JitBlue/DevDiv_754566/test.cs
new file mode 100644
index 0000000000..a07bb7bd3c
--- /dev/null
+++ b/tests/src/JIT/Regression/JitBlue/DevDiv_754566/test.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Runtime.CompilerServices;
+
+class Program
+{
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public String Test(int val)
+ {
+ return ((Object) val).ToString();
+ }
+
+ static int Main(string[] args)
+ {
+ int exitStatus = -1;
+
+ Program p = new Program();
+
+ String result = p.Test(42);
+ if (result == "42")
+ {
+ Console.WriteLine("=== PASSED ===");
+ exitStatus = 100;
+ }
+ else
+ {
+ Console.WriteLine("result shoudl be 42, is= " + result);
+ Console.WriteLine("+++ FAILED +++");
+ }
+ return exitStatus;
+ }
+}