Fix several devirtualization issues
authorAndy Ayers <andya@microsoft.com>
Tue, 21 Mar 2017 20:17:28 +0000 (13:17 -0700)
committerAndy Ayers <andya@microsoft.com>
Wed, 22 Mar 2017 00:56:38 +0000 (17:56 -0700)
When doing interface devirtualization, if the object type is canonical,
ensure that the owner type is too. The jit may present a mixed set
when inlining a shared method into a non-shared method. Ideally the
jit would also be able to present exact object types in such cases but
currently it cannot guarantee this. Closes #10311.

Adjust contracts to address some contract violations seen in desktop
testing. Make the helper non-static and fold in some of the info that
was passed from the caller to bring the desktop and CoreCLR implementations
closer.

Disallow interface devirt if the method is final but the class is not
exact or final, since derived classes can still override final methods
when implementing interfaces.

Don't try and devirtualize interface calls from com objects.

Add some related test cases.

src/jit/importer.cpp
src/vm/jitinterface.cpp
src/vm/jitinterface.h
tests/src/JIT/opt/Devirtualization/GitHub_10311.cs [new file with mode: 0644]
tests/src/JIT/opt/Devirtualization/GitHub_10311.csproj [new file with mode: 0644]
tests/src/JIT/opt/Devirtualization/generic.cs [new file with mode: 0644]
tests/src/JIT/opt/Devirtualization/generic.csproj [new file with mode: 0644]
tests/src/JIT/opt/Devirtualization/overload.cs [new file with mode: 0644]
tests/src/JIT/opt/Devirtualization/overload.csproj [new file with mode: 0644]
tests/src/JIT/opt/Devirtualization/override.il [new file with mode: 0644]
tests/src/JIT/opt/Devirtualization/override.ilproj [new file with mode: 0644]

index 6b9c457..f009c49 100644 (file)
@@ -18521,7 +18521,14 @@ void Compiler::impDevirtualizeCall(GenTreeCall*            call,
         // the time of jitting, objClass has no subclasses that
         // override this method), then perhaps we'd be willing to
         // make a bet...?
-        JITDUMP("    Class NOT final or exact, no devirtualization\n");
+        JITDUMP("    Class not final or exact, method not final, no devirtualization\n");
+        return;
+    }
+
+    // For interface calls we must have an exact type or final class.
+    if (isInterface && !isExact && !objClassIsFinal)
+    {
+        JITDUMP("    Class not final or exact for interface, no devirtualization\n");
         return;
     }
 
@@ -18604,4 +18611,4 @@ void Compiler::impDevirtualizeCall(GenTreeCall*            call,
                baseMethodName, derivedClassName, derivedMethodName, note);
     }
 #endif // defined(DEBUG)
-}
\ No newline at end of file
+}
index 4b9f2f8..1f8ebfc 100644 (file)
@@ -8714,12 +8714,16 @@ void CEEInfo::getMethodVTableOffset (CORINFO_METHOD_HANDLE methodHnd,
 }
 
 /*********************************************************************/
-static CORINFO_METHOD_HANDLE resolveVirtualMethodHelper(MethodDesc* callerMethod,
-                                                        CORINFO_METHOD_HANDLE baseMethod,
-                                                        CORINFO_CLASS_HANDLE derivedClass,
-                                                        CORINFO_CONTEXT_HANDLE ownerType)
+CORINFO_METHOD_HANDLE CEEInfo::resolveVirtualMethodHelper(CORINFO_METHOD_HANDLE baseMethod,
+                                                          CORINFO_CLASS_HANDLE derivedClass,
+                                                          CORINFO_CONTEXT_HANDLE ownerType)
 {
-    STANDARD_VM_CONTRACT;
+    CONTRACTL {
+        SO_TOLERANT;
+        THROWS;
+        GC_TRIGGERS;
+        MODE_PREEMPTIVE;
+    } CONTRACTL_END;
 
     MethodDesc* pBaseMD = GetMethod(baseMethod);
     MethodTable* pBaseMT = pBaseMD->GetMethodTable();
@@ -8747,6 +8751,15 @@ static CORINFO_METHOD_HANDLE resolveVirtualMethodHelper(MethodDesc* callerMethod
 
     if (pBaseMT->IsInterface())
     {
+
+#ifdef FEATURE_COMINTEROP
+        // Don't try and devirtualize com interface calls.
+        if (pDerivedMT->IsComObjectType())
+        {
+            return nullptr;
+        }
+#endif // FEATURE_COMINTEROP
+
         // Interface call devirtualization.
         //
         // We must ensure that pDerivedMT actually implements the
@@ -8760,7 +8773,17 @@ static CORINFO_METHOD_HANDLE resolveVirtualMethodHelper(MethodDesc* callerMethod
         // safely devirtualize.
         if (ownerType != nullptr)
         {
-            pDevirtMD = pDerivedMT->GetMethodDescForInterfaceMethod(GetTypeFromContext(ownerType), pBaseMD);
+            TypeHandle OwnerClsHnd = GetTypeFromContext(ownerType);
+            MethodTable* pOwnerMT = OwnerClsHnd.GetMethodTable();
+
+            // If the derived class is a shared class, make sure the
+            // owner class is too.
+            if (pDerivedMT->IsSharedByGenericInstantiations())
+            {
+                pOwnerMT = pOwnerMT->GetCanonicalMethodTable();
+            }
+
+            pDevirtMD = pDerivedMT->GetMethodDescForInterfaceMethod(TypeHandle(pOwnerMT), pBaseMD);
         }
         else if (!pBaseMD->HasClassOrMethodInstantiation())
         {
@@ -8810,6 +8833,7 @@ static CORINFO_METHOD_HANDLE resolveVirtualMethodHelper(MethodDesc* callerMethod
     // bubble information and if so, disallow it.
     if (IsReadyToRunCompilation())
     {
+        MethodDesc* callerMethod = m_pMethodBeingCompiled;
         Assembly* pCallerAssembly = callerMethod->GetModule()->GetAssembly();
         bool allowDevirt =
             IsInSameVersionBubble(pCallerAssembly , pDevirtMD->GetModule()->GetAssembly())
@@ -8829,13 +8853,18 @@ CORINFO_METHOD_HANDLE CEEInfo::resolveVirtualMethod(CORINFO_METHOD_HANDLE method
                                                     CORINFO_CLASS_HANDLE derivedClass,
                                                     CORINFO_CONTEXT_HANDLE ownerType)
 {
-    STANDARD_VM_CONTRACT;
+    CONTRACTL {
+        SO_TOLERANT;
+        THROWS;
+        GC_TRIGGERS;
+        MODE_PREEMPTIVE;
+    } CONTRACTL_END;
 
     CORINFO_METHOD_HANDLE result = nullptr;
 
     JIT_TO_EE_TRANSITION();
 
-    result = resolveVirtualMethodHelper(m_pMethodBeingCompiled, methodHnd, derivedClass, ownerType);
+    result = resolveVirtualMethodHelper(methodHnd, derivedClass, ownerType);
 
     EE_TO_JIT_TRANSITION();
 
index a432b59..14a4f70 100644 (file)
@@ -735,6 +735,12 @@ public:
         CORINFO_CONTEXT_HANDLE ownerType
         );
 
+    CORINFO_METHOD_HANDLE resolveVirtualMethodHelper(
+        CORINFO_METHOD_HANDLE virtualMethod,
+        CORINFO_CLASS_HANDLE implementingClass,
+        CORINFO_CONTEXT_HANDLE ownerType
+        );
+
     CorInfoIntrinsics getIntrinsicID(CORINFO_METHOD_HANDLE method,
                                      bool * pMustExpand = NULL);
 
diff --git a/tests/src/JIT/opt/Devirtualization/GitHub_10311.cs b/tests/src/JIT/opt/Devirtualization/GitHub_10311.cs
new file mode 100644 (file)
index 0000000..3eb5431
--- /dev/null
@@ -0,0 +1,80 @@
+// 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.Collections;
+using System.Collections.Generic;
+
+public class MyCollection<T> : ICollection<T>
+{
+    private List<T> _items = new List<T>();
+
+    public MyCollection()
+    {
+    }
+
+    public MyCollection(params T[] values)
+    {
+        _items.AddRange(values);
+    }
+
+    public void Add(T item)
+    {
+        _items.Add(item);
+    }
+
+    public void Clear()
+    {
+        _items.Clear();
+    }
+
+    public bool Contains(T item)
+    {
+        return _items.Contains(item);
+    }
+
+    public void CopyTo(T[] array, int arrayIndex)
+    {
+        _items.CopyTo(array, arrayIndex);
+    }
+
+    public int Count
+    {
+        get { return _items.Count; }
+    }
+
+    public bool IsReadOnly
+    {
+        get { return ((ICollection<T>)_items).IsReadOnly; }
+    }
+
+    public bool Remove(T item)
+    {
+        return _items.Remove(item);
+    }
+
+    public IEnumerator<T> GetEnumerator()
+    {
+        return ((ICollection<T>)_items).GetEnumerator();
+    }
+
+    IEnumerator IEnumerable.GetEnumerator()
+    {
+        return ((IEnumerable)_items).GetEnumerator();
+    }
+}
+
+class Bug
+{
+    public static int Main()
+    {
+        int v = 0;
+        MyCollection<string> x = new MyCollection<string>("a1", "a2");
+        foreach (string item in x)
+        {
+            v += item[0];
+        }
+        return v - 94;
+    }
+}
diff --git a/tests/src/JIT/opt/Devirtualization/GitHub_10311.csproj b/tests/src/JIT/opt/Devirtualization/GitHub_10311.csproj
new file mode 100644 (file)
index 0000000..4469702
--- /dev/null
@@ -0,0 +1,37 @@
+<?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_10311.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>
diff --git a/tests/src/JIT/opt/Devirtualization/generic.cs b/tests/src/JIT/opt/Devirtualization/generic.cs
new file mode 100644 (file)
index 0000000..63d53ac
--- /dev/null
@@ -0,0 +1,90 @@
+// 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;
+
+interface Ix
+{
+    int F();
+    int G();
+}
+
+public class B<T> : Ix
+{
+    int Ix.F() 
+    { 
+        if (typeof(T) == typeof(string))
+        {
+            return 3; 
+        }
+        else
+        {
+            return 5;
+        }
+    }
+    
+    public virtual int G()
+    {
+        if (typeof(T) == typeof(object))
+        {
+            return 7; 
+        }
+        else
+        {
+            return 11;
+        }
+
+    }
+}
+
+public class D : B<string>, Ix
+{
+    int Ix.F() { return 13; }
+}
+
+class E : D
+{
+    public sealed override int G() { return 17; }
+}
+
+// K overrides E.G for interface purposes, even though it is sealed
+class K : E, Ix
+{
+    int Ix.G() { return 19; }
+}
+
+sealed class J : E, Ix
+{
+    int Ix.F() { return 21; }
+}
+
+public class Z
+{
+    static int IxF(Ix x) { return x.F(); }
+    static int IxG(Ix x) { return x.G(); }
+
+    public static int Main(string[] args)
+    {
+        E e = new E();
+        K k = new K();
+        J j = new J();
+        E q = k;
+
+        int callsBFs = IxF(new B<string>());
+        int callsBFo = IxF(new B<object>());
+        int callsBGo = IxG(new B<object>());
+        int callsBGs = IxG(new B<string>()) + IxG(new D());
+        int callsDF  = IxF(new D()) + IxF(e) + IxF(k) + IxF(q);
+        int callsEG  = IxG(e) + IxG(j);
+        int callsKG  = IxG(k) + IxG(q);
+        int callsJF  = IxF(j);
+
+        int expected = 3 + 5 + 7 + 2 * 11 + 4 * 13 + 2 * 17 + 2 * 19 + 21;
+        int val = callsBFs + callsBFo + callsDF + callsBGs + callsBGo + callsEG + callsKG + callsJF;
+
+        return val - expected + 100;
+    }
+}
+
+
diff --git a/tests/src/JIT/opt/Devirtualization/generic.csproj b/tests/src/JIT/opt/Devirtualization/generic.csproj
new file mode 100644 (file)
index 0000000..2910303
--- /dev/null
@@ -0,0 +1,37 @@
+<?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>PdbOnly</DebugType>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="generic.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>
diff --git a/tests/src/JIT/opt/Devirtualization/overload.cs b/tests/src/JIT/opt/Devirtualization/overload.cs
new file mode 100644 (file)
index 0000000..a27dbc1
--- /dev/null
@@ -0,0 +1,27 @@
+// 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;
+
+interface Io<T,U> where T:class where U:class
+{
+    T FromU(U u);
+    T FromS(string s);
+}
+
+public class Z : Io<string, string>
+{
+    string Io<string, string>.FromU(string s) { return "U"; }
+    string Io<string, string>.FromS(string s) { return "S"; }
+
+    public static int Main(string[] args)
+    {
+        string fromU = ((Io<string, string>) new Z()).FromU("u");
+        string fromS = ((Io<string, string>) new Z()).FromS("s");
+
+        return fromU[0] + fromS[0] - 68;
+    }
+}
+
+
diff --git a/tests/src/JIT/opt/Devirtualization/overload.csproj b/tests/src/JIT/opt/Devirtualization/overload.csproj
new file mode 100644 (file)
index 0000000..6d5208e
--- /dev/null
@@ -0,0 +1,37 @@
+<?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>PdbOnly</DebugType>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="overload.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>
diff --git a/tests/src/JIT/opt/Devirtualization/override.il b/tests/src/JIT/opt/Devirtualization/override.il
new file mode 100644 (file)
index 0000000..5f87411
--- /dev/null
@@ -0,0 +1,158 @@
+// 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.
+
+.assembly extern System.Console { }
+// MyBar is an abstract class which declares an abstract method MyBar::DoBar().
+// MyBar also DoSelfBar() that explicitly overrides DoBar().
+// BarImpl extends MyBar and also overrides DoBar().
+// Expect that b.DoBar() will invoke BarImpl::DoBar() if called virtually, whether b is a BarImpl reference
+// or a MyBar reference.
+
+.assembly extern mscorlib{}
+.assembly self_override1{}
+
+.class public abstract auto ansi beforefieldinit MyBar
+       extends [mscorlib]System.Object
+{
+  .method public hidebysig newslot abstract virtual
+          instance int32  DoBar() cil managed
+  {
+  } // end of method MyBar::DoBar
+
+  .method public hidebysig virtual instance int32
+          DoSelfBar() cil managed
+  {
+    .override MyBar::DoBar
+    .maxstack  1
+    .locals init (int32 V_0)
+    IL_0000:  ldstr      "In MyBar.DoSelfBar"
+    IL_0005:  call       void [System.Console]System.Console::WriteLine(string)
+    IL_000a:  ldc.i4.1
+    IL_000b:  stloc.0
+    IL_000c:  br.s       IL_000e
+
+    IL_000e:  ldloc.0
+    IL_000f:  ret
+  } // end of method BarImpl::DoBar
+
+
+  .method family 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 MyBar::.ctor
+
+} // end of class MyBar
+
+.class public auto ansi beforefieldinit BarImpl
+       extends MyBar
+{
+  .method public hidebysig virtual instance int32
+          DoBar() cil managed
+  {
+    // Code size       16 (0x10)
+    .maxstack  1
+    .locals init (int32 V_0)
+    IL_0000:  ldstr      "In BarImpl.DoBar"
+    IL_0005:  call       void [System.Console]System.Console::WriteLine(string)
+    IL_000a:  ldc.i4.2
+    IL_000b:  stloc.0
+    IL_000c:  br.s       IL_000e
+
+    IL_000e:  ldloc.0
+    IL_000f:  ret
+  } // end of method BarImpl::DoBar
+
+  .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 MyBar::.ctor()
+    IL_0006:  ret
+  } // end of method BarImpl::.ctor
+
+} // end of class BarImpl
+
+.class public auto ansi beforefieldinit CMain
+       extends [mscorlib]System.Object
+{
+  .method public hidebysig static int32  Main(string[] args) cil managed
+  {
+    .entrypoint
+    // Code size       66 (0x42)
+    .maxstack  2
+    .locals init (bool V_0,
+             class BarImpl V_1,
+             class MyBar V_2,
+             int32 V_3)
+
+
+    // Invoke b.DoBar() virtually where b is a BarImpl reference.
+    IL_0000:  ldc.i4.1
+    IL_0001:  stloc.0
+    IL_0002:  newobj     instance void BarImpl::.ctor()
+    IL_0007:  stloc.1
+    IL_0008:  ldc.i4.2
+    IL_0009:  ldloc.1
+    IL_000a:  callvirt   instance int32 MyBar::DoBar()
+    IL_000f:  beq.s      IL_001d
+
+    IL_0011:  ldstr      "FAIL: expected BarImpl.DoBar to execute, but anoth"
+    + "er method was executed instead."
+    IL_0016:  call       void [System.Console]System.Console::WriteLine(string)
+    IL_001b:  ldc.i4.0
+    IL_001c:  stloc.0
+
+    // Invoke b.DoBar() virtually where b is a MyBar reference.
+    IL_001d:  ldloc.1
+    IL_001e:  stloc.2
+    IL_001f:  ldc.i4.2
+    IL_0020:  ldloc.2
+    IL_0021:  callvirt   instance int32 MyBar::DoBar()
+    IL_0026:  beq.s      IL_0034
+    IL_0028:  ldstr      "FAIL: expected BarImpl.DoBar to execute, but ano"
+    + "ther method was executed instead."
+    IL_002d:  call       void [System.Console]System.Console::WriteLine(string)
+    IL_0032:  ldc.i4.0
+    IL_0033:  stloc.0
+
+
+    // return a status
+    IL_0034:  ldloc.0
+    IL_0035:  brtrue.s   IL_003b
+
+    IL_0037:  ldc.i4.s   101
+    stloc.3
+    ldstr "FAIL"
+    call void [System.Console]System.Console::WriteLine(string)
+    IL_0039:  br.s       IL_0040
+
+    IL_003b:  ldc.i4.s   100
+    IL_003d:  stloc.3
+    ldstr "PASS"
+    call void [System.Console]System.Console::WriteLine(string)
+    IL_003e:  br.s       IL_0040
+
+    IL_0040:  ldloc.3
+    IL_0041:  ret
+  } // end of method CMain::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 CMain::.ctor
+
+} // end of class CMain
+
diff --git a/tests/src/JIT/opt/Devirtualization/override.ilproj b/tests/src/JIT/opt/Devirtualization/override.ilproj
new file mode 100644 (file)
index 0000000..0bfc25f
--- /dev/null
@@ -0,0 +1,42 @@
+<?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>
+    <DefineConstants>$(DefineConstants);STATIC</DefineConstants>
+ </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="override.il" />
+  </ItemGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <PropertyGroup>
+    <ProjectJson>$(JitPackagesConfigFileDirectory)benchmark\project.json</ProjectJson>
+    <ProjectLockJson>$(JitPackagesConfigFileDirectory)benchmark\project.lock.json</ProjectLockJson>
+  </PropertyGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+  <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' ">
+  </PropertyGroup>
+</Project>
\ No newline at end of file