Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
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
3 changes: 2 additions & 1 deletion src/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -3329,7 +3329,8 @@ class Compiler
unsigned* methodFlags,
CORINFO_CONTEXT_HANDLE* contextHandle,
CORINFO_CONTEXT_HANDLE* exactContextHandle,
bool isLateDevirtualization);
bool isLateDevirtualization,
bool isExplicitTailCall);

//=========================================================================
// PROTECTED
Expand Down
4 changes: 3 additions & 1 deletion src/jit/flowgraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22510,7 +22510,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)
Expand Down
29 changes: 25 additions & 4 deletions src/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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))
{
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -8742,7 +8744,6 @@ var_types Compiler::impImportCall(OPCODE opcode,

if (info.compCompHnd->canTailCall(info.compMethodHnd, methHnd, exactCalleeHnd, explicitTailCall))
{
canTailCall = true;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assignment was redundant (see line 8736 above)

if (explicitTailCall)
{
// In case of explicit tail calls, mark it so that it is not considered
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 =
Expand Down Expand Up @@ -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
{
Expand Down
6 changes: 4 additions & 2 deletions src/jit/indirectcalltransformer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -676,14 +676,16 @@ class IndirectCallTransformer

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
Expand Down
106 changes: 106 additions & 0 deletions tests/src/JIT/Regression/JitBlue/DevDiv_754566/DevDiv_754566.il
Original file line number Diff line number Diff line change
@@ -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
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>{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>
31 changes: 31 additions & 0 deletions tests/src/JIT/Regression/JitBlue/DevDiv_754566/test.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}