From 5a16ec5ff139834404847dee5d4a26821bbd9fb4 Mon Sep 17 00:00:00 2001 From: Tomas Rylek Date: Thu, 17 Mar 2022 02:12:19 +0100 Subject: [PATCH 01/22] Move the diamondshape test under StaticVirtualMethods --- .../DiamondShape/svm_diamondshape.cs | 258 +++++ .../DiamondShape/svm_diamondshape.il | 952 ++++++++++++++++++ .../DiamondShape/svm_diamondshape_d.ilproj | 11 + .../DiamondShape/svm_diamondshape_r.ilproj | 11 + 4 files changed, 1232 insertions(+) create mode 100644 src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.cs create mode 100644 src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il create mode 100644 src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape_d.ilproj create mode 100644 src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape_r.ilproj diff --git a/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.cs b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.cs new file mode 100644 index 00000000000000..9e0325c737aa64 --- /dev/null +++ b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.cs @@ -0,0 +1,258 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +interface IFoo +{ + int Foo(int a); +} + +class IFoo_Impl +{ + int Foo(int a) + { + return a; + } +} + +interface IFoo2 : IFoo +{ +} + +class IFoo2_Impl : IFoo +{ + int IFoo.Foo(int a) + { + Console.WriteLine("At IFoo2.Foo"); + return a + 1; + } +} + +interface IFooEx : IFoo +{ +} + +class IFooEx_Impl : IFoo +{ + int IFoo.Foo(int a) + { + Console.WriteLine("At IFooEx.Foo"); + return a + 2; + } +} + +class FooClass : IFoo2, IFooEx +{ + // Dummy + public int Foo(int a) + { + return 0; + } +} + +interface I1 +{ + int Func(int a); +} + +interface I2 : I1 +{ + // int I1.Func(int a) { return a + 2; } +} + +interface I3 : I1 +{ + // int I1.Func(int a) { return a + 3; } +} + +interface I4 : I2, I3 +{ + // int I1.Func(int a) { return a + 4; } +} + +class I4Class : I4 +{ + // @REMOVE + int I1.Func(int a) + { + Console.WriteLine("At I4Class.Func"); + return a + 4; + } +} + +interface I5: I1 +{ + // int I1.Func(int a) { return a + 5; } +} + +interface I6: I1 +{ + // int I1.Func(int a) { return a + 6; } +} + +interface I7: I5, I6 +{ + // int I1.Func(int a) { return a + 7; } +} + +interface I8: I4, I7 +{ + // int I1.Func(int a) { return a + 8; } +} + +class I47Class: I4, I7 +{ + // @REMOVE + int I1.Func(int a) + { + Console.WriteLine("At I4Class.Func"); + return a + 8; + } + +} + +class I8Class: I8 +{ + // @REMOVE + int I1.Func(int a) + { + Console.WriteLine("At I4Class.Func"); + return a + 8; + } +} + +interface GI1 +{ + int Func(out Type[] types); +} + +interface GI2 : GI1 +{ + // int GI1.Func(out Type[] types) { Console.WriteLine(typeof(T) + ", "typeof(S) + ", GI1Class"); types = new Type[] { typeof(T), typeof(S) }; return 2; } + +} + +interface GI3 : GI1 +{ + // int GI1.Func(out Type[] types) { Console.WriteLine(typeof(T) + ", "typeof(S) + ", GI1Class"); types = new Type[] { typeof(T), typeof(S) }; return 3; } +} + +interface GI4 : GI2, GI3 +{ + // int GI1.Func(out Type[] types) { Console.WriteLine(typeof(T) + ", "typeof(S) + ", GI1Class"); types = new Type[] { typeof(T), typeof(S) }; return 4; } +} + +class GI23Class: GI2, GI3 +{ + // @REMOVE + int GI1.Func(out Type[] types) { Console.WriteLine(typeof(T) + ", " + typeof(S) + ", GI1Class"); types = new Type[] { typeof(T), typeof(S) }; return 4; } +} + +class GI4Class: GI4 +{ + // @REMOVE + int GI1.Func(out Type[] types) { Console.WriteLine(typeof(T) + ", " + typeof(S) + ", GI1Class"); types = new Type[] { typeof(T), typeof(S) }; return 4; } +} + +class Program +{ + public static void Negative() + { + FooClass fooObj = new FooClass(); + IFoo foo = (IFoo) fooObj; + + Console.WriteLine("Calling IFoo.Foo on Foo - expecting exception."); + try + { + foo.Foo(10); + Test.Assert(false, "Expecting exception on Foo"); + } + catch(Exception ex) + { + Console.WriteLine("Exception caught: " + ex.ToString()); + } + + I47Class i47Class = new I47Class(); + I1 i1 = (I1) i47Class; + Console.WriteLine("Calling I1.Func on I47Class - expecting exception"); + try + { + i1.Func(10); + Test.Assert(false, "Expecting exception on I47Class"); + } + catch(Exception ex) + { + Console.WriteLine("Exception caught: " + ex.ToString()); + } + + var gi23Class = new GI23Class(); + GI1 gi1 = (GI1) gi23Class; + Console.WriteLine("Calling GI1.Func on GI23Class - expecting exception"); + try + { + Type[] types; + gi1.Func(out types); + Test.Assert(false, "Expecting exception on GI23Class"); + } + catch(Exception ex) + { + Console.WriteLine("Exception caught: " + ex.ToString()); + } + } + + public static void Positive() + { + Console.WriteLine("Calling I1.Func on I4Class - expecting I4.Func"); + + I4Class i4Class = new I4Class(); + I1 i1 = (I1) i4Class; + Test.Assert(i1.Func(10) == 14, "Expecting I1.Func to land on I4.Func"); + + Console.WriteLine("Calling I1.Func on I8Class - expecting I8.Func"); + + I8Class i8Class = new I8Class(); + i1 = (I1) i8Class; + Test.Assert(i1.Func(10) == 18, "Expecting I1.Func to land on I8.Func"); + + Console.WriteLine("Calling GI1.Func on GI4Class - expecting GI4.Func"); + + var gi4Class = new GI4Class(); + Type[] types; + var gi1 = (GI1) gi4Class; + Test.Assert(gi1.Func(out types) == 4, "Expecting GI1.Func to land on GII4.Func"); + Test.Assert(types[0] == typeof(object), "T must be object"); + Test.Assert(types[1] == typeof(string), "S must be string"); + } + + public static int Main() + { + Negative(); + Positive(); + return Test.Ret(); + } +} + +class Test +{ + private static bool Pass = true; + + public static int Ret() + { + return Pass? 100 : 101; + } + + public static void Assert(bool cond, string msg) + { + if (cond) + { + Console.WriteLine("PASS"); + } + else + { + Console.WriteLine("FAIL: " + msg); + Pass = false; + } + } +} + diff --git a/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il new file mode 100644 index 00000000000000..bccd832229b0e8 --- /dev/null +++ b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il @@ -0,0 +1,952 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0 +// Copyright (c) Microsoft Corporation. All rights reserved. + + + +// 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 extern xunit.core {} +// TODO: use the contract once this is exposed from contracts +.assembly extern System.Runtime { } + +.assembly svm_diamondshape +{ + .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. + + // --- The following custom attribute is added automatically, do not uncomment ------- + // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) + + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +// MVID: {25B467F3-D284-4DB6-BAD4-C7EAC75CF064} +.imagebase 0x00400000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00000001 // ILONLY +// Image base: 0x010B0000 + + +// =============== CLASS MEMBERS DECLARATION =================== + +.class interface private abstract auto ansi IFoo +{ + .method public hidebysig newslot virtual + instance int32 Foo(int32 a) cil managed + { + // Code size 7 (0x7) + .maxstack 1 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: stloc.0 + IL_0003: br.s IL_0005 + + IL_0005: ldloc.0 + IL_0006: ret + } // end of method IFoo::Foo + +} // end of class IFoo + +.class interface private abstract auto ansi IFoo2 + implements IFoo +{ + .method public hidebysig newslot virtual final + instance int32 Foo(int32 a) cil managed + { + .override IFoo::Foo + // Code size 20 (0x14) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldstr "At IFoo2.Foo" + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: ldarg.1 + IL_000d: ldc.i4.1 + IL_000e: add + IL_000f: stloc.0 + IL_0010: br.s IL_0012 + + IL_0012: ldloc.0 + IL_0013: ret + } // end of method IFoo2::IFoo.Foo + +} // end of class IFoo2 + +.class interface private abstract auto ansi IFooEx + implements IFoo +{ + .method public hidebysig newslot virtual final + instance int32 IFoo.Foo(int32 a) cil managed + { + .override IFoo::Foo + // Code size 20 (0x14) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldstr "At IFooEx.Foo" + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: ldarg.1 + IL_000d: ldc.i4.2 + IL_000e: add + IL_000f: stloc.0 + IL_0010: br.s IL_0012 + + IL_0012: ldloc.0 + IL_0013: ret + } // end of method IFooEx::IFoo.Foo +} // end of class IFooEx + +.class private auto ansi beforefieldinit FooClass + extends [mscorlib]System.Object + implements IFoo2, + IFoo, + IFooEx +{ + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method FooClass::.ctor + +} // end of class FooClass + +.class interface private abstract auto ansi I1 +{ + .method public hidebysig newslot abstract virtual + instance int32 Func(int32 a) cil managed + { + } // end of method I1::Func + +} // end of class I1 + +.class interface private abstract auto ansi I2 + implements I1 +{ + .method private hidebysig newslot virtual final + instance int32 I1.Func(int32 a) cil managed + { + .override I1::Func + // Code size 20 (0x14) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldstr "At I2.Func" + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: ldarg.1 + IL_000d: ldc.i4.2 + IL_000e: add + IL_000f: stloc.0 + IL_0010: br.s IL_0012 + + IL_0012: ldloc.0 + IL_0013: ret + } // end of method I2::I1.Func +} // end of class I2 + +.class interface private abstract auto ansi I3 + implements I1 +{ + .method private hidebysig newslot virtual final + instance int32 I1.Func(int32 a) cil managed + { + .override I1::Func + // Code size 20 (0x14) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldstr "At I3.Func" + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: ldarg.1 + IL_000d: ldc.i4.3 + IL_000e: add + IL_000f: stloc.0 + IL_0010: br.s IL_0012 + + IL_0012: ldloc.0 + IL_0013: ret + } // end of method I3::I1.Func +} // end of class I3 + +.class interface private abstract auto ansi I4 + implements I2, + I1, + I3 +{ + .method private hidebysig newslot virtual final + instance int32 I1.Func(int32 a) cil managed + { + .override I1::Func + // Code size 20 (0x14) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldstr "At I4.Func" + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: ldarg.1 + IL_000d: ldc.i4.4 + IL_000e: add + IL_000f: stloc.0 + IL_0010: br.s IL_0012 + + IL_0012: ldloc.0 + IL_0013: ret + } // end of method I4::I1.Func +} // end of class I4 + +.class private auto ansi beforefieldinit I4Class + extends [mscorlib]System.Object + implements I4, + I2, + I1, + I3 +{ + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method I4Class::.ctor + +} // end of class I4Class + +.class interface private abstract auto ansi I5 + implements I1 +{ + .method private hidebysig newslot virtual final + instance int32 I1.Func(int32 a) cil managed + { + .override I1::Func + // Code size 20 (0x14) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldstr "At I5.Func" + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: ldarg.1 + IL_000d: ldc.i4.5 + IL_000e: add + IL_000f: stloc.0 + IL_0010: br.s IL_0012 + + IL_0012: ldloc.0 + IL_0013: ret + } // end of method I5::I1.Func +} // end of class I5 + +.class interface private abstract auto ansi I6 + implements I1 +{ + .method private hidebysig newslot virtual final + instance int32 I1.Func(int32 a) cil managed + { + .override I1::Func + // Code size 20 (0x14) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldstr "At I6.Func" + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: ldarg.1 + IL_000d: ldc.i4.6 + IL_000e: add + IL_000f: stloc.0 + IL_0010: br.s IL_0012 + + IL_0012: ldloc.0 + IL_0013: ret + } // end of method I6::I1.Func +} // end of class I6 + +.class interface private abstract auto ansi I7 + implements I5, + I1, + I6 +{ + .method private hidebysig newslot virtual final + instance int32 I1.Func(int32 a) cil managed + { + .override I1::Func + // Code size 20 (0x14) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldstr "At I7.Func" + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: ldarg.1 + IL_000d: ldc.i4.7 + IL_000e: add + IL_000f: stloc.0 + IL_0010: br.s IL_0012 + + IL_0012: ldloc.0 + IL_0013: ret + } // end of method I7::I1.Func +} // end of class I7 + +.class interface private abstract auto ansi I8 + implements I4, + I2, + I1, + I3, + I7, + I5, + I6 +{ + .method private hidebysig newslot virtual final + instance int32 I1.Func(int32 a) cil managed + { + .override I1::Func + // Code size 20 (0x14) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldstr "At I8.Func" + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: ldarg.1 + IL_000d: ldc.i4.8 + IL_000e: add + IL_000f: stloc.0 + IL_0010: br.s IL_0012 + + IL_0012: ldloc.0 + IL_0013: ret + } // end of method I8::I1.Func +} // end of class I8 + +.class private auto ansi beforefieldinit I47Class + extends [mscorlib]System.Object + implements I4, + I2, + I1, + I3, + I7, + I5, + I6 +{ + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method I47Class::.ctor + +} // end of class I47Class + +.class private auto ansi beforefieldinit I8Class + extends [mscorlib]System.Object + implements I8, + I4, + I2, + I1, + I3, + I7, + I5, + I6 +{ + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method I8Class::.ctor + +} // end of class I8Class + +.class interface private abstract auto ansi GI1`1 +{ + .method public hidebysig newslot abstract virtual + instance int32 Func([out] class [mscorlib]System.Type[]& types) cil managed + { + } // end of method GI1`1::'GI1.Func' + +} // end of class GI1`1 + +.class interface private abstract auto ansi GI2`1 + implements class GI1`1 +{ +.method private hidebysig newslot virtual final + instance int32 'GI1.Func'([out] class [mscorlib]System.Type[]& types) cil managed + { + .override method instance int32 class GI1`1::Func<[1]>(class [mscorlib]System.Type[]&) + // Code size 100 (0x64) + .maxstack 5 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldc.i4.4 + IL_0002: newarr [mscorlib]System.Object + IL_0007: dup + IL_0008: ldc.i4.0 + IL_0009: ldtoken !T + IL_000e: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_0013: stelem.ref + IL_0014: dup + IL_0015: ldc.i4.1 + IL_0016: ldstr ", " + IL_001b: stelem.ref + IL_001c: dup + IL_001d: ldc.i4.2 + IL_001e: ldtoken !!S + IL_0023: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_0028: stelem.ref + IL_0029: dup + IL_002a: ldc.i4.3 + IL_002b: ldstr ", GI2" + IL_0030: stelem.ref + IL_0031: call string [mscorlib]System.String::Concat(object[]) + IL_0036: call void [mscorlib]System.Console::WriteLine(string) + IL_003b: nop + IL_003c: ldarg.1 + IL_003d: ldc.i4.2 + IL_003e: newarr [mscorlib]System.Type + IL_0043: dup + IL_0044: ldc.i4.0 + IL_0045: ldtoken !T + IL_004a: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_004f: stelem.ref + IL_0050: dup + IL_0051: ldc.i4.1 + IL_0052: ldtoken !!S + IL_0057: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_005c: stelem.ref + IL_005d: stind.ref + IL_005e: ldc.i4.2 + IL_005f: stloc.0 + IL_0060: br.s IL_0062 + + IL_0062: ldloc.0 + IL_0063: ret + } // end of method G2`1::'GI1.Func' +} // end of class GI2`1 + +.class interface private abstract auto ansi GI3`1 + implements class GI1`1 +{ +.method private hidebysig newslot virtual final + instance int32 'GI1.Func'([out] class [mscorlib]System.Type[]& types) cil managed + { + .override method instance int32 class GI1`1::Func<[1]>(class [mscorlib]System.Type[]&) + // Code size 100 (0x64) + .maxstack 5 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldc.i4.4 + IL_0002: newarr [mscorlib]System.Object + IL_0007: dup + IL_0008: ldc.i4.0 + IL_0009: ldtoken !T + IL_000e: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_0013: stelem.ref + IL_0014: dup + IL_0015: ldc.i4.1 + IL_0016: ldstr ", " + IL_001b: stelem.ref + IL_001c: dup + IL_001d: ldc.i4.2 + IL_001e: ldtoken !!S + IL_0023: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_0028: stelem.ref + IL_0029: dup + IL_002a: ldc.i4.3 + IL_002b: ldstr ", GI3" + IL_0030: stelem.ref + IL_0031: call string [mscorlib]System.String::Concat(object[]) + IL_0036: call void [mscorlib]System.Console::WriteLine(string) + IL_003b: nop + IL_003c: ldarg.1 + IL_003d: ldc.i4.2 + IL_003e: newarr [mscorlib]System.Type + IL_0043: dup + IL_0044: ldc.i4.0 + IL_0045: ldtoken !T + IL_004a: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_004f: stelem.ref + IL_0050: dup + IL_0051: ldc.i4.1 + IL_0052: ldtoken !!S + IL_0057: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_005c: stelem.ref + IL_005d: stind.ref + IL_005e: ldc.i4.3 + IL_005f: stloc.0 + IL_0060: br.s IL_0062 + + IL_0062: ldloc.0 + IL_0063: ret + } // end of method GI3`1::'GI1.Func' +} // end of class GI3`1 + +.class interface private abstract auto ansi GI4`1 + implements class GI2`1, + class GI1`1, + class GI3`1 +{ + .method private hidebysig newslot virtual final + instance int32 'GI1.Func'([out] class [mscorlib]System.Type[]& types) cil managed + { + .override method instance int32 class GI1`1::Func<[1]>(class [mscorlib]System.Type[]&) + // Code size 100 (0x64) + .maxstack 5 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldc.i4.4 + IL_0002: newarr [mscorlib]System.Object + IL_0007: dup + IL_0008: ldc.i4.0 + IL_0009: ldtoken !T + IL_000e: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_0013: stelem.ref + IL_0014: dup + IL_0015: ldc.i4.1 + IL_0016: ldstr ", " + IL_001b: stelem.ref + IL_001c: dup + IL_001d: ldc.i4.2 + IL_001e: ldtoken !!S + IL_0023: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_0028: stelem.ref + IL_0029: dup + IL_002a: ldc.i4.3 + IL_002b: ldstr ", GI4" + IL_0030: stelem.ref + IL_0031: call string [mscorlib]System.String::Concat(object[]) + IL_0036: call void [mscorlib]System.Console::WriteLine(string) + IL_003b: nop + IL_003c: ldarg.1 + IL_003d: ldc.i4.2 + IL_003e: newarr [mscorlib]System.Type + IL_0043: dup + IL_0044: ldc.i4.0 + IL_0045: ldtoken !T + IL_004a: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_004f: stelem.ref + IL_0050: dup + IL_0051: ldc.i4.1 + IL_0052: ldtoken !!S + IL_0057: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_005c: stelem.ref + IL_005d: stind.ref + IL_005e: ldc.i4.4 + IL_005f: stloc.0 + IL_0060: br.s IL_0062 + + IL_0062: ldloc.0 + IL_0063: ret + } // end of method GI4`1::'GI1.Func' +} // end of class GI4`1 + +.class private auto ansi beforefieldinit GI23Class`1 + extends [mscorlib]System.Object + implements class GI2`1, + class GI1`1, + class GI3`1 +{ + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method GI23Class`1::.ctor + +} // end of class GI23Class`1 + +.class private auto ansi beforefieldinit GI4Class`1 + extends [mscorlib]System.Object + implements class GI4`1, + class GI2`1, + class GI1`1, + class GI3`1 +{ + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method GI4Class`1::.ctor + +} // end of class GI4Class`1 + +.class private auto ansi beforefieldinit Program + extends [mscorlib]System.Object +{ + .method public hidebysig static void Negative() cil managed + { + // Code size 225 (0xe1) + .maxstack 2 + .locals init (class FooClass V_0, + class IFoo V_1, + class I47Class V_2, + class I1 V_3, + class GI23Class`1 V_4, + class GI1`1 V_5, + class [mscorlib]System.Exception V_6, + class [mscorlib]System.Exception V_7, + class [mscorlib]System.Type[] V_8, + class [mscorlib]System.Exception V_9) + IL_0000: nop + IL_0001: newobj instance void FooClass::.ctor() + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: stloc.1 + IL_0009: ldstr "Calling IFoo.Foo on Foo - expecting exception." + IL_000e: call void [mscorlib]System.Console::WriteLine(string) + IL_0013: nop + .try + { + IL_0014: nop + IL_0015: ldloc.1 + IL_0016: ldc.i4.s 10 + IL_0018: callvirt instance int32 IFoo::Foo(int32) + IL_001d: pop + IL_001e: ldc.i4.0 + IL_001f: ldstr "Expecting exception on Foo" + IL_0024: call void Test::Assert(bool, + string) + IL_0029: nop + IL_002a: nop + IL_002b: leave.s IL_004a + + } // end .try + catch [System.Runtime]System.Runtime.AmbiguousImplementationException + { + IL_002d: stloc.s V_6 + IL_002f: nop + IL_0030: ldstr "Exception caught: " + IL_0035: ldloc.s V_6 + IL_0037: callvirt instance string [mscorlib]System.Object::ToString() + IL_003c: call string [mscorlib]System.String::Concat(string, + string) + IL_0041: call void [mscorlib]System.Console::WriteLine(string) + IL_0046: nop + IL_0047: nop + IL_0048: leave.s IL_004a + + } // end handler + IL_004a: newobj instance void I47Class::.ctor() + IL_004f: stloc.2 + IL_0050: ldloc.2 + IL_0051: stloc.3 + IL_0052: ldstr "Calling I1.Func on I47Class - expecting exception" + IL_0057: call void [mscorlib]System.Console::WriteLine(string) + IL_005c: nop + .try + { + IL_005d: nop + IL_005e: ldloc.3 + IL_005f: ldc.i4.s 10 + IL_0061: callvirt instance int32 I1::Func(int32) + IL_0066: pop + IL_0067: ldc.i4.0 + IL_0068: ldstr "Expecting exception on I47Class" + IL_006d: call void Test::Assert(bool, + string) + IL_0072: nop + IL_0073: nop + IL_0074: leave.s IL_0093 + + } // end .try + catch [System.Runtime]System.Runtime.AmbiguousImplementationException + { + IL_0076: stloc.s V_7 + IL_0078: nop + IL_0079: ldstr "Exception caught: " + IL_007e: ldloc.s V_7 + IL_0080: callvirt instance string [mscorlib]System.Object::ToString() + IL_0085: call string [mscorlib]System.String::Concat(string, + string) + IL_008a: call void [mscorlib]System.Console::WriteLine(string) + IL_008f: nop + IL_0090: nop + IL_0091: leave.s IL_0093 + + } // end handler + IL_0093: newobj instance void class GI23Class`1::.ctor() + IL_0098: stloc.s V_4 + IL_009a: ldloc.s V_4 + IL_009c: stloc.s V_5 + IL_009e: ldstr "Calling GI1.Func on GI23Class - expecting ex" + + "ception" + IL_00a3: call void [mscorlib]System.Console::WriteLine(string) + IL_00a8: nop + .try + { + IL_00a9: nop + IL_00aa: ldloc.s V_5 + IL_00ac: ldloca.s V_8 + IL_00ae: callvirt instance int32 class GI1`1::Func(class [mscorlib]System.Type[]&) + IL_00b3: pop + IL_00b4: ldc.i4.0 + IL_00b5: ldstr "Expecting exception on GI23Class" + IL_00ba: call void Test::Assert(bool, + string) + IL_00bf: nop + IL_00c0: nop + IL_00c1: leave.s IL_00e0 + + } // end .try + catch [System.Runtime]System.Runtime.AmbiguousImplementationException + { + IL_00c3: stloc.s V_9 + IL_00c5: nop + IL_00c6: ldstr "Exception caught: " + IL_00cb: ldloc.s V_9 + IL_00cd: callvirt instance string [mscorlib]System.Object::ToString() + IL_00d2: call string [mscorlib]System.String::Concat(string, + string) + IL_00d7: call void [mscorlib]System.Console::WriteLine(string) + IL_00dc: nop + IL_00dd: nop + IL_00de: leave.s IL_00e0 + + } // end handler + IL_00e0: ret + } // end of method Program::Negative + + .method public hidebysig static void Positive() cil managed + { + // Code size 189 (0xbd) + .maxstack 2 + .locals init (class I4Class V_0, + class I1 V_1, + class I8Class V_2, + class GI4Class`1 V_3, + class [mscorlib]System.Type[] V_4, + class GI1`1 V_5) + IL_0000: nop + IL_0001: ldstr "Calling I1.Func on I4Class - expecting I4.Func" + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: newobj instance void I4Class::.ctor() + IL_0011: stloc.0 + IL_0012: ldloc.0 + IL_0013: stloc.1 + IL_0014: ldloc.1 + IL_0015: ldc.i4.s 10 + IL_0017: callvirt instance int32 I1::Func(int32) + IL_001c: ldc.i4.s 14 + IL_001e: ceq + IL_0020: ldstr "Expecting I1.Func to land on I4.Func" + IL_0025: call void Test::Assert(bool, + string) + IL_002a: nop + IL_002b: ldstr "Calling I1.Func on I8Class - expecting I8.Func" + IL_0030: call void [mscorlib]System.Console::WriteLine(string) + IL_0035: nop + IL_0036: newobj instance void I8Class::.ctor() + IL_003b: stloc.2 + IL_003c: ldloc.2 + IL_003d: stloc.1 + IL_003e: ldloc.1 + IL_003f: ldc.i4.s 10 + IL_0041: callvirt instance int32 I1::Func(int32) + IL_0046: ldc.i4.s 18 + IL_0048: ceq + IL_004a: ldstr "Expecting I1.Func to land on I8.Func" + IL_004f: call void Test::Assert(bool, + string) + IL_0054: nop + IL_0055: ldstr "Calling GI1.Func on GI4Class - expecting G" + + "I4.Func" + IL_005a: call void [mscorlib]System.Console::WriteLine(string) + IL_005f: nop + IL_0060: newobj instance void class GI4Class`1::.ctor() + IL_0065: stloc.3 + IL_0066: ldloc.3 + IL_0067: stloc.s V_5 + IL_0069: ldloc.s V_5 + IL_006b: ldloca.s V_4 + IL_006d: callvirt instance int32 class GI1`1::Func(class [mscorlib]System.Type[]&) + IL_0072: ldc.i4.4 + IL_0073: ceq + IL_0075: ldstr "Expecting GI1.Func to land on GII4.Func" + IL_007a: call void Test::Assert(bool, + string) + IL_007f: nop + IL_0080: ldloc.s V_4 + IL_0082: ldc.i4.0 + IL_0083: ldelem.ref + IL_0084: ldtoken [mscorlib]System.Object + IL_0089: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_008e: call bool [mscorlib]System.Type::op_Equality(class [mscorlib]System.Type, + class [mscorlib]System.Type) + IL_0093: ldstr "T must be object" + IL_0098: call void Test::Assert(bool, + string) + IL_009d: nop + IL_009e: ldloc.s V_4 + IL_00a0: ldc.i4.1 + IL_00a1: ldelem.ref + IL_00a2: ldtoken [mscorlib]System.String + IL_00a7: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_00ac: call bool [mscorlib]System.Type::op_Equality(class [mscorlib]System.Type, + class [mscorlib]System.Type) + IL_00b1: ldstr "S must be string" + IL_00b6: call void Test::Assert(bool, + string) + IL_00bb: nop + IL_00bc: ret + } // end of method Program::Positive + + .method public hidebysig static int32 Main() cil managed + { + .custom instance void [xunit.core]Xunit.FactAttribute::.ctor() = ( + 01 00 00 00 + ) + .entrypoint + // Code size 23 (0x17) + .maxstack 1 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: call void Program::Negative() + IL_0006: nop + IL_0007: call void Program::Positive() + IL_000c: nop + IL_000d: call int32 Test::Ret() + IL_0012: stloc.0 + IL_0013: br.s IL_0015 + + IL_0015: ldloc.0 + IL_0016: ret + } // end of method Program::Main + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method Program::.ctor + +} // end of class Program + +.class private auto ansi beforefieldinit Test + extends [mscorlib]System.Object +{ + .field private static bool Pass + .method public hidebysig static int32 Ret() cil managed + { + // Code size 19 (0x13) + .maxstack 1 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldsfld bool Test::Pass + IL_0006: brtrue.s IL_000c + + IL_0008: ldc.i4.s 101 + IL_000a: br.s IL_000e + + IL_000c: ldc.i4.s 100 + IL_000e: stloc.0 + IL_000f: br.s IL_0011 + + IL_0011: ldloc.0 + IL_0012: ret + } // end of method Test::Ret + + .method public hidebysig static void Assert(bool cond, + string msg) cil managed + { + // Code size 47 (0x2f) + .maxstack 2 + .locals init (bool V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: brfalse.s IL_0015 + + IL_0006: nop + IL_0007: ldstr "PASS" + IL_000c: call void [mscorlib]System.Console::WriteLine(string) + IL_0011: nop + IL_0012: nop + IL_0013: br.s IL_002e + + IL_0015: nop + IL_0016: ldstr "FAIL: " + IL_001b: ldarg.1 + IL_001c: call string [mscorlib]System.String::Concat(string, + string) + IL_0021: call void [mscorlib]System.Console::WriteLine(string) + IL_0026: nop + IL_0027: ldc.i4.0 + IL_0028: stsfld bool Test::Pass + IL_002d: nop + IL_002e: ret + } // end of method Test::Assert + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method Test::.ctor + + .method private hidebysig specialname rtspecialname static + void .cctor() cil managed + { + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldc.i4.1 + IL_0001: stsfld bool Test::Pass + IL_0006: ret + } // end of method Test::.cctor + +} // end of class Test + + +// ============================================================= + +// *********** DISASSEMBLY COMPLETE *********************** +// WARNING: Created Win32 resource file diamondshape.res diff --git a/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape_d.ilproj b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape_d.ilproj new file mode 100644 index 00000000000000..5a90175fe318f9 --- /dev/null +++ b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape_d.ilproj @@ -0,0 +1,11 @@ + + + Exe + + + Full + + + + + diff --git a/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape_r.ilproj b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape_r.ilproj new file mode 100644 index 00000000000000..f036395dbd3f95 --- /dev/null +++ b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape_r.ilproj @@ -0,0 +1,11 @@ + + + Exe + + + + + + + + From 07004ecff3fac913bf5285236c8978fd0639f848 Mon Sep 17 00:00:00 2001 From: Tomas Rylek Date: Thu, 17 Mar 2022 02:15:16 +0100 Subject: [PATCH 02/22] Modify the test to exercise static virtual methods --- .../DiamondShape/svm_diamondshape.cs | 94 ++++---- .../DiamondShape/svm_diamondshape.il | 211 +++++------------- 2 files changed, 108 insertions(+), 197 deletions(-) diff --git a/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.cs b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.cs index 9e0325c737aa64..723600f15f5cb7 100644 --- a/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.cs +++ b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.cs @@ -5,12 +5,12 @@ interface IFoo { - int Foo(int a); + static int Foo(int a); } class IFoo_Impl { - int Foo(int a) + static int Foo(int a) { return a; } @@ -22,11 +22,11 @@ interface IFoo2 : IFoo class IFoo2_Impl : IFoo { - int IFoo.Foo(int a) + static int IFoo.Foo(int a) { Console.WriteLine("At IFoo2.Foo"); return a + 1; - } + } } interface IFooEx : IFoo @@ -35,7 +35,7 @@ interface IFooEx : IFoo class IFooEx_Impl : IFoo { - int IFoo.Foo(int a) + static int IFoo.Foo(int a) { Console.WriteLine("At IFooEx.Foo"); return a + 2; @@ -45,7 +45,7 @@ int IFoo.Foo(int a) class FooClass : IFoo2, IFooEx { // Dummy - public int Foo(int a) + public static int Foo(int a) { return 0; } @@ -53,58 +53,58 @@ public int Foo(int a) interface I1 { - int Func(int a); + static int Func(int a); } interface I2 : I1 { - // int I1.Func(int a) { return a + 2; } + // static int I1.Func(int a) { return a + 2; } } interface I3 : I1 { - // int I1.Func(int a) { return a + 3; } + // static int I1.Func(int a) { return a + 3; } } interface I4 : I2, I3 { - // int I1.Func(int a) { return a + 4; } + // static int I1.Func(int a) { return a + 4; } } class I4Class : I4 { // @REMOVE - int I1.Func(int a) + static int I1.Func(int a) { Console.WriteLine("At I4Class.Func"); return a + 4; - } + } } interface I5: I1 { - // int I1.Func(int a) { return a + 5; } + // static int I1.Func(int a) { return a + 5; } } interface I6: I1 { - // int I1.Func(int a) { return a + 6; } + // static int I1.Func(int a) { return a + 6; } } interface I7: I5, I6 { - // int I1.Func(int a) { return a + 7; } + // static int I1.Func(int a) { return a + 7; } } interface I8: I4, I7 { - // int I1.Func(int a) { return a + 8; } + // static int I1.Func(int a) { return a + 8; } } class I47Class: I4, I7 { // @REMOVE - int I1.Func(int a) + static int I1.Func(int a) { Console.WriteLine("At I4Class.Func"); return a + 8; @@ -115,7 +115,7 @@ int I1.Func(int a) class I8Class: I8 { // @REMOVE - int I1.Func(int a) + static int I1.Func(int a) { Console.WriteLine("At I4Class.Func"); return a + 8; @@ -124,61 +124,74 @@ int I1.Func(int a) interface GI1 { - int Func(out Type[] types); + static int Func(out Type[] types); } interface GI2 : GI1 { - // int GI1.Func(out Type[] types) { Console.WriteLine(typeof(T) + ", "typeof(S) + ", GI1Class"); types = new Type[] { typeof(T), typeof(S) }; return 2; } + // static int GI1.Func(out Type[] types) { Console.WriteLine(typeof(T) + ", "typeof(S) + ", GI1Class"); types = new Type[] { typeof(T), typeof(S) }; return 2; } } interface GI3 : GI1 { - // int GI1.Func(out Type[] types) { Console.WriteLine(typeof(T) + ", "typeof(S) + ", GI1Class"); types = new Type[] { typeof(T), typeof(S) }; return 3; } + // static int GI1.Func(out Type[] types) { Console.WriteLine(typeof(T) + ", "typeof(S) + ", GI1Class"); types = new Type[] { typeof(T), typeof(S) }; return 3; } } interface GI4 : GI2, GI3 { - // int GI1.Func(out Type[] types) { Console.WriteLine(typeof(T) + ", "typeof(S) + ", GI1Class"); types = new Type[] { typeof(T), typeof(S) }; return 4; } + // static int GI1.Func(out Type[] types) { Console.WriteLine(typeof(T) + ", "typeof(S) + ", GI1Class"); types = new Type[] { typeof(T), typeof(S) }; return 4; } } class GI23Class: GI2, GI3 { // @REMOVE - int GI1.Func(out Type[] types) { Console.WriteLine(typeof(T) + ", " + typeof(S) + ", GI1Class"); types = new Type[] { typeof(T), typeof(S) }; return 4; } + static int GI1.Func(out Type[] types) { Console.WriteLine(typeof(T) + ", " + typeof(S) + ", GI1Class"); types = new Type[] { typeof(T), typeof(S) }; return 4; } } class GI4Class: GI4 { // @REMOVE - int GI1.Func(out Type[] types) { Console.WriteLine(typeof(T) + ", " + typeof(S) + ", GI1Class"); types = new Type[] { typeof(T), typeof(S) }; return 4; } + static int GI1.Func(out Type[] types) { Console.WriteLine(typeof(T) + ", " + typeof(S) + ", GI1Class"); types = new Type[] { typeof(T), typeof(S) }; return 4; } } class Program { + private static void CallFoo(int value) + : T is IFoo + { + T.Foo(value); + } + + private static void CallI1Func(int value) + : T is I1 + { + T.Func(value); + } + + private static void CallGI1Func(out Type[] types) + : T is GI1 + { + T.Func(out types); + } + public static void Negative() { - FooClass fooObj = new FooClass(); - IFoo foo = (IFoo) fooObj; - Console.WriteLine("Calling IFoo.Foo on Foo - expecting exception."); try { - foo.Foo(10); - Test.Assert(false, "Expecting exception on Foo"); + CallFoo(10); + Test.Assert(false, "Expecting exception on Foo"); } catch(Exception ex) { Console.WriteLine("Exception caught: " + ex.ToString()); } - I47Class i47Class = new I47Class(); - I1 i1 = (I1) i47Class; Console.WriteLine("Calling I1.Func on I47Class - expecting exception"); try { - i1.Func(10); + CallI1Func(10); Test.Assert(false, "Expecting exception on I47Class"); } catch(Exception ex) @@ -192,7 +205,7 @@ public static void Negative() try { Type[] types; - gi1.Func(out types); + CallGI1Func, GT1, string>(out types); Test.Assert(false, "Expecting exception on GI23Class"); } catch(Exception ex) @@ -205,22 +218,16 @@ public static void Positive() { Console.WriteLine("Calling I1.Func on I4Class - expecting I4.Func"); - I4Class i4Class = new I4Class(); - I1 i1 = (I1) i4Class; - Test.Assert(i1.Func(10) == 14, "Expecting I1.Func to land on I4.Func"); + Test.Assert(CallI1Func(10) == 14, "Expecting I1.Func to land on I4.Func"); Console.WriteLine("Calling I1.Func on I8Class - expecting I8.Func"); - I8Class i8Class = new I8Class(); - i1 = (I1) i8Class; - Test.Assert(i1.Func(10) == 18, "Expecting I1.Func to land on I8.Func"); + Test.Assert(CallI1Func(10) == 18, "Expecting I1.Func to land on I8.Func"); Console.WriteLine("Calling GI1.Func on GI4Class - expecting GI4.Func"); - var gi4Class = new GI4Class(); Type[] types; - var gi1 = (GI1) gi4Class; - Test.Assert(gi1.Func(out types) == 4, "Expecting GI1.Func to land on GII4.Func"); + Test.Assert(CallGI1Func, GI1, string>(out types) == 4, "Expecting GI1.Func to land on GII4.Func"); Test.Assert(types[0] == typeof(object), "T must be object"); Test.Assert(types[1] == typeof(string), "S must be string"); } @@ -254,5 +261,4 @@ public static void Assert(bool cond, string msg) Pass = false; } } -} - +} diff --git a/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il index bccd832229b0e8..85a6446b42f51a 100644 --- a/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il +++ b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il @@ -41,14 +41,14 @@ .class interface private abstract auto ansi IFoo { - .method public hidebysig newslot virtual - instance int32 Foo(int32 a) cil managed + .method public hidebysig newslot virtual + static int32 Foo(int32 a) cil managed { // Code size 7 (0x7) .maxstack 1 .locals init (int32 V_0) IL_0000: nop - IL_0001: ldarg.1 + IL_0001: ldarg.0 IL_0002: stloc.0 IL_0003: br.s IL_0005 @@ -62,9 +62,9 @@ implements IFoo { .method public hidebysig newslot virtual final - instance int32 Foo(int32 a) cil managed + static int32 Foo(int32 a) cil managed { - .override IFoo::Foo + .override method int32 IFoo::Foo(int32) // Code size 20 (0x14) .maxstack 2 .locals init (int32 V_0) @@ -72,7 +72,7 @@ IL_0001: ldstr "At IFoo2.Foo" IL_0006: call void [mscorlib]System.Console::WriteLine(string) IL_000b: nop - IL_000c: ldarg.1 + IL_000c: ldarg.0 IL_000d: ldc.i4.1 IL_000e: add IL_000f: stloc.0 @@ -88,9 +88,9 @@ implements IFoo { .method public hidebysig newslot virtual final - instance int32 IFoo.Foo(int32 a) cil managed + static int32 IFoo.Foo(int32 a) cil managed { - .override IFoo::Foo + .override method int32 IFoo::Foo(int32) // Code size 20 (0x14) .maxstack 2 .locals init (int32 V_0) @@ -98,7 +98,7 @@ IL_0001: ldstr "At IFooEx.Foo" IL_0006: call void [mscorlib]System.Console::WriteLine(string) IL_000b: nop - IL_000c: ldarg.1 + IL_000c: ldarg.0 IL_000d: ldc.i4.2 IL_000e: add IL_000f: stloc.0 @@ -115,23 +115,12 @@ IFoo, IFooEx { - .method public hidebysig specialname rtspecialname - instance void .ctor() cil managed - { - // Code size 8 (0x8) - .maxstack 8 - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Object::.ctor() - IL_0006: nop - IL_0007: ret - } // end of method FooClass::.ctor - } // end of class FooClass .class interface private abstract auto ansi I1 { .method public hidebysig newslot abstract virtual - instance int32 Func(int32 a) cil managed + static int32 Func(int32 a) cil managed { } // end of method I1::Func @@ -141,9 +130,9 @@ implements I1 { .method private hidebysig newslot virtual final - instance int32 I1.Func(int32 a) cil managed + static int32 I1.Func(int32 a) cil managed { - .override I1::Func + .override method int32 I1::Func(int32) // Code size 20 (0x14) .maxstack 2 .locals init (int32 V_0) @@ -151,7 +140,7 @@ IL_0001: ldstr "At I2.Func" IL_0006: call void [mscorlib]System.Console::WriteLine(string) IL_000b: nop - IL_000c: ldarg.1 + IL_000c: ldarg.0 IL_000d: ldc.i4.2 IL_000e: add IL_000f: stloc.0 @@ -159,16 +148,16 @@ IL_0012: ldloc.0 IL_0013: ret - } // end of method I2::I1.Func + } // end of method I2::I1.Func } // end of class I2 .class interface private abstract auto ansi I3 implements I1 { .method private hidebysig newslot virtual final - instance int32 I1.Func(int32 a) cil managed + static int32 I1.Func(int32 a) cil managed { - .override I1::Func + .override method int32 I1::Func(int32) // Code size 20 (0x14) .maxstack 2 .locals init (int32 V_0) @@ -176,7 +165,7 @@ IL_0001: ldstr "At I3.Func" IL_0006: call void [mscorlib]System.Console::WriteLine(string) IL_000b: nop - IL_000c: ldarg.1 + IL_000c: ldarg.0 IL_000d: ldc.i4.3 IL_000e: add IL_000f: stloc.0 @@ -184,7 +173,7 @@ IL_0012: ldloc.0 IL_0013: ret - } // end of method I3::I1.Func + } // end of method I3::I1.Func } // end of class I3 .class interface private abstract auto ansi I4 @@ -193,9 +182,9 @@ I3 { .method private hidebysig newslot virtual final - instance int32 I1.Func(int32 a) cil managed + static int32 I1.Func(int32 a) cil managed { - .override I1::Func + .override method int32 I1::Func(int32) // Code size 20 (0x14) .maxstack 2 .locals init (int32 V_0) @@ -203,7 +192,7 @@ IL_0001: ldstr "At I4.Func" IL_0006: call void [mscorlib]System.Console::WriteLine(string) IL_000b: nop - IL_000c: ldarg.1 + IL_000c: ldarg.0 IL_000d: ldc.i4.4 IL_000e: add IL_000f: stloc.0 @@ -221,26 +210,15 @@ I1, I3 { - .method public hidebysig specialname rtspecialname - instance void .ctor() cil managed - { - // Code size 8 (0x8) - .maxstack 8 - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Object::.ctor() - IL_0006: nop - IL_0007: ret - } // end of method I4Class::.ctor - } // end of class I4Class .class interface private abstract auto ansi I5 implements I1 { .method private hidebysig newslot virtual final - instance int32 I1.Func(int32 a) cil managed + static int32 I1.Func(int32 a) cil managed { - .override I1::Func + .override method int32 I1::Func(int32) // Code size 20 (0x14) .maxstack 2 .locals init (int32 V_0) @@ -248,7 +226,7 @@ IL_0001: ldstr "At I5.Func" IL_0006: call void [mscorlib]System.Console::WriteLine(string) IL_000b: nop - IL_000c: ldarg.1 + IL_000c: ldarg.0 IL_000d: ldc.i4.5 IL_000e: add IL_000f: stloc.0 @@ -263,9 +241,9 @@ implements I1 { .method private hidebysig newslot virtual final - instance int32 I1.Func(int32 a) cil managed + static int32 I1.Func(int32 a) cil managed { - .override I1::Func + .override method int32 I1::Func(int32) // Code size 20 (0x14) .maxstack 2 .locals init (int32 V_0) @@ -273,7 +251,7 @@ IL_0001: ldstr "At I6.Func" IL_0006: call void [mscorlib]System.Console::WriteLine(string) IL_000b: nop - IL_000c: ldarg.1 + IL_000c: ldarg.0 IL_000d: ldc.i4.6 IL_000e: add IL_000f: stloc.0 @@ -290,9 +268,9 @@ I6 { .method private hidebysig newslot virtual final - instance int32 I1.Func(int32 a) cil managed + static int32 I1.Func(int32 a) cil managed { - .override I1::Func + .override method int32 I1::Func(int32) // Code size 20 (0x14) .maxstack 2 .locals init (int32 V_0) @@ -300,7 +278,7 @@ IL_0001: ldstr "At I7.Func" IL_0006: call void [mscorlib]System.Console::WriteLine(string) IL_000b: nop - IL_000c: ldarg.1 + IL_000c: ldarg.0 IL_000d: ldc.i4.7 IL_000e: add IL_000f: stloc.0 @@ -321,9 +299,9 @@ I6 { .method private hidebysig newslot virtual final - instance int32 I1.Func(int32 a) cil managed + static int32 I1.Func(int32 a) cil managed { - .override I1::Func + .override method int32 I1::Func(int32) // Code size 20 (0x14) .maxstack 2 .locals init (int32 V_0) @@ -331,7 +309,7 @@ IL_0001: ldstr "At I8.Func" IL_0006: call void [mscorlib]System.Console::WriteLine(string) IL_000b: nop - IL_000c: ldarg.1 + IL_000c: ldarg.0 IL_000d: ldc.i4.8 IL_000e: add IL_000f: stloc.0 @@ -352,17 +330,6 @@ I5, I6 { - .method public hidebysig specialname rtspecialname - instance void .ctor() cil managed - { - // Code size 8 (0x8) - .maxstack 8 - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Object::.ctor() - IL_0006: nop - IL_0007: ret - } // end of method I47Class::.ctor - } // end of class I47Class .class private auto ansi beforefieldinit I8Class @@ -376,23 +343,12 @@ I5, I6 { - .method public hidebysig specialname rtspecialname - instance void .ctor() cil managed - { - // Code size 8 (0x8) - .maxstack 8 - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Object::.ctor() - IL_0006: nop - IL_0007: ret - } // end of method I8Class::.ctor - } // end of class I8Class .class interface private abstract auto ansi GI1`1 { .method public hidebysig newslot abstract virtual - instance int32 Func([out] class [mscorlib]System.Type[]& types) cil managed + static int32 Func([out] class [mscorlib]System.Type[]& types) cil managed { } // end of method GI1`1::'GI1.Func' @@ -402,9 +358,9 @@ implements class GI1`1 { .method private hidebysig newslot virtual final - instance int32 'GI1.Func'([out] class [mscorlib]System.Type[]& types) cil managed + static int32 'GI1.Func'([out] class [mscorlib]System.Type[]& types) cil managed { - .override method instance int32 class GI1`1::Func<[1]>(class [mscorlib]System.Type[]&) + .override method int32 class GI1`1::Func<[1]>(class [mscorlib]System.Type[]&) // Code size 100 (0x64) .maxstack 5 .locals init (int32 V_0) @@ -432,7 +388,7 @@ IL_0031: call string [mscorlib]System.String::Concat(object[]) IL_0036: call void [mscorlib]System.Console::WriteLine(string) IL_003b: nop - IL_003c: ldarg.1 + IL_003c: ldarg.0 IL_003d: ldc.i4.2 IL_003e: newarr [mscorlib]System.Type IL_0043: dup @@ -459,9 +415,9 @@ implements class GI1`1 { .method private hidebysig newslot virtual final - instance int32 'GI1.Func'([out] class [mscorlib]System.Type[]& types) cil managed + static int32 'GI1.Func'([out] class [mscorlib]System.Type[]& types) cil managed { - .override method instance int32 class GI1`1::Func<[1]>(class [mscorlib]System.Type[]&) + .override method int32 class GI1`1::Func<[1]>(class [mscorlib]System.Type[]&) // Code size 100 (0x64) .maxstack 5 .locals init (int32 V_0) @@ -489,7 +445,7 @@ IL_0031: call string [mscorlib]System.String::Concat(object[]) IL_0036: call void [mscorlib]System.Console::WriteLine(string) IL_003b: nop - IL_003c: ldarg.1 + IL_003c: ldarg.0 IL_003d: ldc.i4.2 IL_003e: newarr [mscorlib]System.Type IL_0043: dup @@ -518,9 +474,9 @@ class GI3`1 { .method private hidebysig newslot virtual final - instance int32 'GI1.Func'([out] class [mscorlib]System.Type[]& types) cil managed + static int32 'GI1.Func'([out] class [mscorlib]System.Type[]& types) cil managed { - .override method instance int32 class GI1`1::Func<[1]>(class [mscorlib]System.Type[]&) + .override method int32 class GI1`1::Func<[1]>(class [mscorlib]System.Type[]&) // Code size 100 (0x64) .maxstack 5 .locals init (int32 V_0) @@ -548,7 +504,7 @@ IL_0031: call string [mscorlib]System.String::Concat(object[]) IL_0036: call void [mscorlib]System.Console::WriteLine(string) IL_003b: nop - IL_003c: ldarg.1 + IL_003c: ldarg.0 IL_003d: ldc.i4.2 IL_003e: newarr [mscorlib]System.Type IL_0043: dup @@ -577,17 +533,6 @@ class GI1`1, class GI3`1 { - .method public hidebysig specialname rtspecialname - instance void .ctor() cil managed - { - // Code size 8 (0x8) - .maxstack 8 - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Object::.ctor() - IL_0006: nop - IL_0007: ret - } // end of method GI23Class`1::.ctor - } // end of class GI23Class`1 .class private auto ansi beforefieldinit GI4Class`1 @@ -597,17 +542,6 @@ class GI1`1, class GI3`1 { - .method public hidebysig specialname rtspecialname - instance void .ctor() cil managed - { - // Code size 8 (0x8) - .maxstack 8 - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Object::.ctor() - IL_0006: nop - IL_0007: ret - } // end of method GI4Class`1::.ctor - } // end of class GI4Class`1 .class private auto ansi beforefieldinit Program @@ -617,30 +551,21 @@ { // Code size 225 (0xe1) .maxstack 2 - .locals init (class FooClass V_0, - class IFoo V_1, - class I47Class V_2, - class I1 V_3, - class GI23Class`1 V_4, - class GI1`1 V_5, + .locals init ( class [mscorlib]System.Exception V_6, class [mscorlib]System.Exception V_7, class [mscorlib]System.Type[] V_8, class [mscorlib]System.Exception V_9) IL_0000: nop - IL_0001: newobj instance void FooClass::.ctor() - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: stloc.1 IL_0009: ldstr "Calling IFoo.Foo on Foo - expecting exception." IL_000e: call void [mscorlib]System.Console::WriteLine(string) IL_0013: nop .try { IL_0014: nop - IL_0015: ldloc.1 IL_0016: ldc.i4.s 10 - IL_0018: callvirt instance int32 IFoo::Foo(int32) + constrained. class [svm_diamondshape]FooClass + IL_0018: call int32 IFoo::Foo(int32) IL_001d: pop IL_001e: ldc.i4.0 IL_001f: ldstr "Expecting exception on Foo" @@ -666,19 +591,15 @@ IL_0048: leave.s IL_004a } // end handler - IL_004a: newobj instance void I47Class::.ctor() - IL_004f: stloc.2 - IL_0050: ldloc.2 - IL_0051: stloc.3 - IL_0052: ldstr "Calling I1.Func on I47Class - expecting exception" + IL_004a: ldstr "Calling I1.Func on I47Class - expecting exception" IL_0057: call void [mscorlib]System.Console::WriteLine(string) IL_005c: nop .try { IL_005d: nop - IL_005e: ldloc.3 IL_005f: ldc.i4.s 10 - IL_0061: callvirt instance int32 I1::Func(int32) + constrained. class [svm_diamondshape]I47Class + IL_0061: call int32 I1::Func(int32) IL_0066: pop IL_0067: ldc.i4.0 IL_0068: ldstr "Expecting exception on I47Class" @@ -704,20 +625,16 @@ IL_0091: leave.s IL_0093 } // end handler - IL_0093: newobj instance void class GI23Class`1::.ctor() - IL_0098: stloc.s V_4 - IL_009a: ldloc.s V_4 - IL_009c: stloc.s V_5 - IL_009e: ldstr "Calling GI1.Func on GI23Class - expecting ex" + IL_0093: ldstr "Calling GI1.Func on GI23Class - expecting ex" + "ception" IL_00a3: call void [mscorlib]System.Console::WriteLine(string) IL_00a8: nop .try { IL_00a9: nop - IL_00aa: ldloc.s V_5 IL_00ac: ldloca.s V_8 - IL_00ae: callvirt instance int32 class GI1`1::Func(class [mscorlib]System.Type[]&) + constrained. class [svm_diamondshape]GI23Class` + IL_00ae: call int32 class GI1`1::Func(class [mscorlib]System.Type[]&) IL_00b3: pop IL_00b4: ldc.i4.0 IL_00b5: ldstr "Expecting exception on GI23Class" @@ -760,13 +677,9 @@ IL_0001: ldstr "Calling I1.Func on I4Class - expecting I4.Func" IL_0006: call void [mscorlib]System.Console::WriteLine(string) IL_000b: nop - IL_000c: newobj instance void I4Class::.ctor() - IL_0011: stloc.0 - IL_0012: ldloc.0 - IL_0013: stloc.1 - IL_0014: ldloc.1 IL_0015: ldc.i4.s 10 - IL_0017: callvirt instance int32 I1::Func(int32) + constrained. class I4Class + IL_0017: call int32 I1::Func(int32) IL_001c: ldc.i4.s 14 IL_001e: ceq IL_0020: ldstr "Expecting I1.Func to land on I4.Func" @@ -776,13 +689,9 @@ IL_002b: ldstr "Calling I1.Func on I8Class - expecting I8.Func" IL_0030: call void [mscorlib]System.Console::WriteLine(string) IL_0035: nop - IL_0036: newobj instance void I8Class::.ctor() - IL_003b: stloc.2 - IL_003c: ldloc.2 - IL_003d: stloc.1 - IL_003e: ldloc.1 IL_003f: ldc.i4.s 10 - IL_0041: callvirt instance int32 I1::Func(int32) + constrained. class [svm_diamondshape]I8Class + IL_0041: call int32 I1::Func(int32) IL_0046: ldc.i4.s 18 IL_0048: ceq IL_004a: ldstr "Expecting I1.Func to land on I8.Func" @@ -793,13 +702,9 @@ + "I4.Func" IL_005a: call void [mscorlib]System.Console::WriteLine(string) IL_005f: nop - IL_0060: newobj instance void class GI4Class`1::.ctor() - IL_0065: stloc.3 - IL_0066: ldloc.3 - IL_0067: stloc.s V_5 - IL_0069: ldloc.s V_5 IL_006b: ldloca.s V_4 - IL_006d: callvirt instance int32 class GI1`1::Func(class [mscorlib]System.Type[]&) + constrained. class [svm_diamondshape]GI4Class`1 + IL_006d: call int32 class GI1`1::Func(class [mscorlib]System.Type[]&) IL_0072: ldc.i4.4 IL_0073: ceq IL_0075: ldstr "Expecting GI1.Func to land on GII4.Func" From d50b83454b67fc8d79072977b308e6f3c6d3c378 Mon Sep 17 00:00:00 2001 From: Tomas Rylek Date: Fri, 18 Mar 2022 19:08:04 +0100 Subject: [PATCH 03/22] Fixes to svm_diamondshape to make it build; initial runtime changes --- src/coreclr/vm/methodtable.cpp | 100 ++++++++++-------- src/coreclr/vm/methodtablebuilder.cpp | 4 + .../DiamondShape/svm_diamondshape.il | 24 ++--- 3 files changed, 70 insertions(+), 58 deletions(-) diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 5aae97dd5cb67d..7e3c9911826a13 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -5510,58 +5510,61 @@ namespace MethodDesc *pMD = methodIt.GetMethodDesc(); int targetSlot = interfaceMD->GetSlot(); - // If this is not a MethodImpl, it can't be implementing the method we're looking for - if (!pMD->IsMethodImpl()) - continue; - - // We have a MethodImpl - iterate over all the declarations it's implementing, - // looking for the interface method we need. - MethodImpl::Iterator it(pMD); - for (; it.IsValid() && candidateMaybe == NULL; it.Next()) + if (pMD->IsMethodImpl()) { - MethodDesc *pDeclMD = it.GetMethodDesc(); - - // Is this the right slot? - if (pDeclMD->GetSlot() != targetSlot) - continue; - - // Is this the right interface? - if (!pDeclMD->HasSameMethodDefAs(interfaceMD)) - continue; - - if (interfaceMD->HasClassInstantiation()) + // We have a MethodImpl with slots - iterate over all the declarations it's implementing, + // looking for the interface method we need. + MethodImpl::Iterator it(pMD); + for (; it.IsValid() && candidateMaybe == NULL; it.Next()) { - // pInterfaceMD will be in the canonical form, so we need to check the specific - // instantiation against pInterfaceMT. - // - // The parent of pDeclMD is unreliable for this purpose because it may or - // may not be canonicalized. Let's go from the metadata. - - SigTypeContext typeContext = SigTypeContext(pMT); - - mdTypeRef tkParent; - IfFailThrow(pMD->GetModule()->GetMDImport()->GetParentToken(it.GetToken(), &tkParent)); - - MethodTable *pDeclMT = ClassLoader::LoadTypeDefOrRefOrSpecThrowing( - pMD->GetModule(), - tkParent, - &typeContext).AsMethodTable(); - - // We do CanCastToInterface to also cover variance. - // We already know this is a method on the same type definition as the (generic) - // interface but we need to make sure the instantiations match. - if ((allowVariance && pDeclMT->CanCastToInterface(interfaceMT)) - || pDeclMT == interfaceMT) + MethodDesc *pDeclMD = it.GetMethodDesc(); + + // Is this the right slot? + if (pDeclMD->GetSlot() != targetSlot) + continue; + + // Is this the right interface? + if (!pDeclMD->HasSameMethodDefAs(interfaceMD)) + continue; + + if (interfaceMD->HasClassInstantiation()) { - // We have a match + // pInterfaceMD will be in the canonical form, so we need to check the specific + // instantiation against pInterfaceMT. + // + // The parent of pDeclMD is unreliable for this purpose because it may or + // may not be canonicalized. Let's go from the metadata. + + SigTypeContext typeContext = SigTypeContext(pMT); + + mdTypeRef tkParent; + IfFailThrow(pMD->GetModule()->GetMDImport()->GetParentToken(it.GetToken(), &tkParent)); + + MethodTable *pDeclMT = ClassLoader::LoadTypeDefOrRefOrSpecThrowing( + pMD->GetModule(), + tkParent, + &typeContext).AsMethodTable(); + + // We do CanCastToInterface to also cover variance. + // We already know this is a method on the same type definition as the (generic) + // interface but we need to make sure the instantiations match. + if ((allowVariance && pDeclMT->CanCastToInterface(interfaceMT)) + || pDeclMT == interfaceMT) + { + // We have a match + candidateMaybe = pMD; + } + } + else + { + // No generics involved. If the method definitions match, it's a match. candidateMaybe = pMD; } } - else - { - // No generics involved. If the method definitions match, it's a match. - candidateMaybe = pMD; - } + } + else + { + wprintf(W("TODO!\n")); } } } @@ -8064,6 +8067,11 @@ MethodTable::ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* } } } + + if (FindDefaultInterfaceImplementation(pInterfaceMD, pInterfaceType, &pMD, /* allowVariance */ allowVariantMatches, /* throwOnConflict */ verifyImplemented)) + { + return pMD; + } } } diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index 78e26991bcf4a0..5d65609b8e4ca6 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -6393,6 +6393,10 @@ MethodTableBuilder::PlaceMethodImpls() } } } + else + { + + } iEntry++; diff --git a/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il index 85a6446b42f51a..24795445a76744 100644 --- a/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il +++ b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il @@ -61,7 +61,7 @@ .class interface private abstract auto ansi IFoo2 implements IFoo { - .method public hidebysig newslot virtual final + .method public hidebysig final static int32 Foo(int32 a) cil managed { .override method int32 IFoo::Foo(int32) @@ -87,7 +87,7 @@ .class interface private abstract auto ansi IFooEx implements IFoo { - .method public hidebysig newslot virtual final + .method public hidebysig final static int32 IFoo.Foo(int32 a) cil managed { .override method int32 IFoo::Foo(int32) @@ -129,7 +129,7 @@ .class interface private abstract auto ansi I2 implements I1 { - .method private hidebysig newslot virtual final + .method private hidebysig final static int32 I1.Func(int32 a) cil managed { .override method int32 I1::Func(int32) @@ -154,7 +154,7 @@ .class interface private abstract auto ansi I3 implements I1 { - .method private hidebysig newslot virtual final + .method private hidebysig final static int32 I1.Func(int32 a) cil managed { .override method int32 I1::Func(int32) @@ -181,7 +181,7 @@ I1, I3 { - .method private hidebysig newslot virtual final + .method private hidebysig final static int32 I1.Func(int32 a) cil managed { .override method int32 I1::Func(int32) @@ -215,7 +215,7 @@ .class interface private abstract auto ansi I5 implements I1 { - .method private hidebysig newslot virtual final + .method private hidebysig final static int32 I1.Func(int32 a) cil managed { .override method int32 I1::Func(int32) @@ -240,7 +240,7 @@ .class interface private abstract auto ansi I6 implements I1 { - .method private hidebysig newslot virtual final + .method private hidebysig final static int32 I1.Func(int32 a) cil managed { .override method int32 I1::Func(int32) @@ -267,7 +267,7 @@ I1, I6 { - .method private hidebysig newslot virtual final + .method private hidebysig final static int32 I1.Func(int32 a) cil managed { .override method int32 I1::Func(int32) @@ -298,7 +298,7 @@ I5, I6 { - .method private hidebysig newslot virtual final + .method private hidebysig final static int32 I1.Func(int32 a) cil managed { .override method int32 I1::Func(int32) @@ -357,7 +357,7 @@ .class interface private abstract auto ansi GI2`1 implements class GI1`1 { -.method private hidebysig newslot virtual final +.method private hidebysig final static int32 'GI1.Func'([out] class [mscorlib]System.Type[]& types) cil managed { .override method int32 class GI1`1::Func<[1]>(class [mscorlib]System.Type[]&) @@ -414,7 +414,7 @@ .class interface private abstract auto ansi GI3`1 implements class GI1`1 { -.method private hidebysig newslot virtual final +.method private hidebysig final static int32 'GI1.Func'([out] class [mscorlib]System.Type[]& types) cil managed { .override method int32 class GI1`1::Func<[1]>(class [mscorlib]System.Type[]&) @@ -473,7 +473,7 @@ class GI1`1, class GI3`1 { - .method private hidebysig newslot virtual final + .method private hidebysig final static int32 'GI1.Func'([out] class [mscorlib]System.Type[]& types) cil managed { .override method int32 class GI1`1::Func<[1]>(class [mscorlib]System.Type[]&) From b89edd7f5fa427019a959c78ec407e5c0ae4c266 Mon Sep 17 00:00:00 2001 From: Tomas Date: Sat, 19 Mar 2022 22:01:42 +0100 Subject: [PATCH 04/22] One more fix to the new diamondshape test; more runtime fixes --- src/coreclr/vm/jitinterface.cpp | 10 +++ src/coreclr/vm/methodtable.cpp | 68 ++++++++++++++----- src/coreclr/vm/methodtable.h | 11 ++- .../DiamondShape/svm_diamondshape.il | 2 +- 4 files changed, 72 insertions(+), 19 deletions(-) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 804ee2e274fe37..1c18719e508a7e 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -4921,6 +4921,16 @@ void CEEInfo::getCallInfo( exactType = constrainedType; } +#ifdef FEATURE_DEFAULT_INTERFACES + else if (directMethod && pMD->IsStatic()) + { + // Default interface implementation of static virtual method + pMDAfterConstraintResolution = directMethod; + fResolvedConstraint = TRUE; + pResult->thisTransform = CORINFO_NO_THIS_TRANSFORM; + exactType = directMethod->GetMethodTable(); + } +#endif else if (constrainedType.IsValueType()) { pResult->thisTransform = CORINFO_BOX_THIS; diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 7e3c9911826a13..bd2da9fac4c620 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -5475,7 +5475,8 @@ namespace MethodDesc *interfaceMD, MethodTable *interfaceMT, BOOL allowVariance, - MethodDesc **candidateMD) + MethodDesc **candidateMD, + BOOL throwOnConflict) { *candidateMD = NULL; @@ -5562,9 +5563,14 @@ namespace } } } - else + else if (pMD->IsStatic() && pMD->HasMethodImplSlot()) { - wprintf(W("TODO!\n")); + // Static virtual methods don't record MethodImpl slots so they need special handling + candidateMaybe = pMT->TryResolveVirtualStaticMethodOnThisType( + interfaceMT, + interfaceMD, + /* verifyImplemented */ FALSE, + /* throwOnConflict */ throwOnConflict); } } } @@ -5623,7 +5629,7 @@ BOOL MethodTable::FindDefaultInterfaceImplementation( // Check the current method table itself MethodDesc *candidateMaybe = NULL; - if (IsInterface() && TryGetCandidateImplementation(this, pInterfaceMD, pInterfaceMT, allowVariance, &candidateMaybe)) + if (IsInterface() && TryGetCandidateImplementation(this, pInterfaceMD, pInterfaceMT, allowVariance, &candidateMaybe, throwOnConflict)) { _ASSERTE(candidateMaybe != NULL); @@ -5663,7 +5669,7 @@ BOOL MethodTable::FindDefaultInterfaceImplementation( MethodTable *pCurMT = it.GetInterface(pMT); MethodDesc *pCurMD = NULL; - if (TryGetCandidateImplementation(pCurMT, pInterfaceMD, pInterfaceMT, allowVariance, &pCurMD)) + if (TryGetCandidateImplementation(pCurMT, pInterfaceMD, pInterfaceMT, allowVariance, &pCurMD, throwOnConflict)) { // // Found a match. But is it a more specific match (we want most specific interfaces) @@ -5753,8 +5759,8 @@ BOOL MethodTable::FindDefaultInterfaceImplementation( pBestCandidateMD = candidates[i].pMD; // If this is a second pass lookup, we know this is a variant match. As such - // we pick the first result as the winner and don't look for a conflict. - if (allowVariance) + // we pick the first result as the winner and don't look for a conflict for instance methods. + if (allowVariance && !pInterfaceMD->IsStatic()) break; } else if (pBestCandidateMT != candidates[i].pMT) @@ -5762,7 +5768,7 @@ BOOL MethodTable::FindDefaultInterfaceImplementation( if (throwOnConflict) ThrowExceptionForConflictingOverride(this, pInterfaceMT, pInterfaceMD); - *ppDefaultMethod = NULL; + *ppDefaultMethod = pBestCandidateMD; RETURN(FALSE); } } @@ -7986,7 +7992,13 @@ MethodDesc *MethodTable::GetDefaultConstructor(BOOL forceBoxedEntryPoint /* = FA //========================================================================================== // Finds the (non-unboxing) MethodDesc that implements the interface virtual static method pInterfaceMD. MethodDesc * -MethodTable::ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL allowNullResult, BOOL verifyImplemented, BOOL allowVariantMatches) +MethodTable::ResolveVirtualStaticMethod( + MethodTable* pInterfaceType, + MethodDesc* pInterfaceMD, + BOOL allowNullResult, + BOOL verifyImplemented, + BOOL allowVariantMatches, + BOOL throwOnConflict) { if (!pInterfaceMD->IsSharedByGenericMethodInstantiations() && !pInterfaceType->IsSharedByGenericInstantiations()) { @@ -8015,7 +8027,7 @@ MethodTable::ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* // Search for match on a per-level in the type hierarchy for (MethodTable* pMT = this; pMT != nullptr; pMT = pMT->GetParentMethodTable()) { - MethodDesc* pMD = pMT->TryResolveVirtualStaticMethodOnThisType(pInterfaceType, pInterfaceMD, verifyImplemented); + MethodDesc* pMD = pMT->TryResolveVirtualStaticMethodOnThisType(pInterfaceType, pInterfaceMD, verifyImplemented, throwOnConflict); if (pMD != nullptr) { return pMD; @@ -8059,7 +8071,7 @@ MethodTable::ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* { // Variant or equivalent matching interface found // Attempt to resolve on variance matched interface - pMD = pMT->TryResolveVirtualStaticMethodOnThisType(pItfInMap, pInterfaceMD, verifyImplemented); + pMD = pMT->TryResolveVirtualStaticMethodOnThisType(pItfInMap, pInterfaceMD, verifyImplemented, throwOnConflict); if (pMD != nullptr) { return pMD; @@ -8068,8 +8080,19 @@ MethodTable::ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* } } - if (FindDefaultInterfaceImplementation(pInterfaceMD, pInterfaceType, &pMD, /* allowVariance */ allowVariantMatches, /* throwOnConflict */ verifyImplemented)) + // I have yet to figure out how to modify getCallInfo to support lazy throws upon + // ambiguous default interface method resolution. For now this effectively disables + // the uniqueness check. + /*BOOL haveUniqueDefaultImplementation =*/ pMT->FindDefaultInterfaceImplementation( + pInterfaceMD, + pInterfaceType, + &pMD, + /* allowVariance */ allowVariantMatches, + /* throwOnConflict */ throwOnConflict); + // if (haveUniqueDefaultImplementation || (pMD != nullptr && verifyImplemented)) + if (pMD != nullptr) { + // We tolerate conflicts upon verification of implemented SVMs so that they only blow up when actually called at execution time. return pMD; } } @@ -8092,7 +8115,7 @@ MethodTable::ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* // Try to locate the appropriate MethodImpl matching a given interface static virtual method. // Returns nullptr on failure. MethodDesc* -MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL verifyImplemented) +MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL verifyImplemented, BOOL throwOnConflict) { HRESULT hr = S_OK; IMDInternalImport* pMDInternalImport = GetMDImport(); @@ -8227,7 +8250,7 @@ MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType { return pMethodImpl; } - if (pPrevMethodImpl != nullptr) + if (pPrevMethodImpl != nullptr && throwOnConflict) { // Two MethodImpl records found for the same virtual static interface method COMPlusThrow(kTypeLoadException, E_FAIL); @@ -8254,7 +8277,14 @@ MethodTable::VerifyThatAllVirtualStaticMethodsAreImplemented() MethodDesc *pMD = it.GetMethodDesc(); if (pMD->IsVirtual() && pMD->IsStatic() && - (pMD->IsAbstract() && !ResolveVirtualStaticMethod(pInterfaceMT, pMD, /* allowNullResult */ TRUE, /* verifyImplemented */ TRUE, /* allowVariantMatches */ FALSE))) + (pMD->IsAbstract() && + !ResolveVirtualStaticMethod( + pInterfaceMT, + pMD, + /* allowNullResult */ TRUE, + /* verifyImplemented */ TRUE, + /* allowVariantMatches */ FALSE, + /* throwOnConflict */ FALSE))) { IMDInternalImport* pInternalImport = GetModule()->GetMDImport(); GetModule()->GetAssembly()->ThrowTypeLoadException(pInternalImport, GetCl(), pMD->GetName(), IDS_CLASSLOAD_STATICVIRTUAL_NOTIMPL); @@ -8287,7 +8317,13 @@ MethodTable::TryResolveConstraintMethodApprox( { _ASSERTE(!thInterfaceType.IsTypeDesc()); _ASSERTE(thInterfaceType.IsInterface()); - MethodDesc *result = ResolveVirtualStaticMethod(thInterfaceType.GetMethodTable(), pInterfaceMD, pfForceUseRuntimeLookup != NULL); + MethodDesc *result = ResolveVirtualStaticMethod( + thInterfaceType.GetMethodTable(), + pInterfaceMD, + /* allowNullResult */pfForceUseRuntimeLookup != NULL, + /* verifyImplemented */ FALSE, + /* allowVariantMatches */ TRUE, + /* throwOnConflict */ FALSE); if (result == NULL) { *pfForceUseRuntimeLookup = TRUE; diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index 48ef1cd6894392..b6a3747a9f759f 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -2092,7 +2092,14 @@ class MethodTable // Specify allowNullResult to return NULL instead of throwing if the there is no implementation // Specify verifyImplemented to verify that there is a match, but do not actually return a final usable MethodDesc // Specify allowVariantMatches to permit generic interface variance - MethodDesc *ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL allowNullResult, BOOL verifyImplemented = FALSE, BOOL allowVariantMatches = TRUE); + // Specify throwOnConflict to throw upon ambiguous static virtual method resolution. + MethodDesc *ResolveVirtualStaticMethod( + MethodTable* pInterfaceType, + MethodDesc* pInterfaceMD, + BOOL allowNullResult, + BOOL verifyImplemented = FALSE, + BOOL allowVariantMatches = TRUE, + BOOL throwOnConflict = TRUE); // Try a partial resolve of the constraint call, up to generic code sharing. // @@ -2210,7 +2217,7 @@ class MethodTable // Try to resolve a given static virtual method override on this type. Return nullptr // when not found. - MethodDesc *TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL verifyImplemented); + MethodDesc *TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL verifyImplemented, BOOL throwOnConflict); public: static MethodDesc *MapMethodDeclToMethodImpl(MethodDesc *pMDDecl); diff --git a/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il index 24795445a76744..32f956d2376819 100644 --- a/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il +++ b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il @@ -633,7 +633,7 @@ { IL_00a9: nop IL_00ac: ldloca.s V_8 - constrained. class [svm_diamondshape]GI23Class` + constrained. class [svm_diamondshape]GI23Class`1 IL_00ae: call int32 class GI1`1::Func(class [mscorlib]System.Type[]&) IL_00b3: pop IL_00b4: ldc.i4.0 From 30eee66df762b82c6d132c2888c3fc8a8c2097f4 Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 27 Apr 2022 12:45:39 +0200 Subject: [PATCH 05/22] Partial change for default interface implementations of SVMs --- src/coreclr/inc/corinfo.h | 6 +- src/coreclr/inc/jithelpers.h | 2 + src/coreclr/jit/compiler.h | 1 + src/coreclr/jit/importer.cpp | 66 +++++++++++++++++-- .../tools/Common/JitInterface/CorInfoTypes.cs | 1 + src/coreclr/vm/jithelpers.cpp | 20 ++++++ src/coreclr/vm/jitinterface.cpp | 51 +++++++++++--- src/coreclr/vm/methodtable.cpp | 46 +++++++------ src/coreclr/vm/methodtable.h | 7 +- src/coreclr/vm/runtimehandles.cpp | 7 +- src/coreclr/vm/typedesc.cpp | 3 +- 11 files changed, 170 insertions(+), 40 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index b390f3b911cf2f..752c5da2822e9a 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -646,6 +646,8 @@ enum CorInfoHelpFunc CORINFO_HELP_VALIDATE_INDIRECT_CALL, // CFG: Validate function pointer CORINFO_HELP_DISPATCH_INDIRECT_CALL, // CFG: Validate and dispatch to pointer + CORINFO_HELP_VIRTUAL_STATIC, // Resolve virtual static method address based on method handle and type constraint + CORINFO_HELP_COUNT, }; @@ -1208,6 +1210,7 @@ enum CORINFO_RUNTIME_LOOKUP_KIND CORINFO_LOOKUP_THISOBJ, CORINFO_LOOKUP_METHODPARAM, CORINFO_LOOKUP_CLASSPARAM, + CORINFO_LOOKUP_VIRTUALSTATIC, CORINFO_LOOKUP_NOT_SUPPORTED, // Returned for attempts to inline dictionary lookups }; @@ -1459,7 +1462,8 @@ enum CORINFO_CALL_KIND CORINFO_CALL_CODE_POINTER, CORINFO_VIRTUALCALL_STUB, CORINFO_VIRTUALCALL_LDVIRTFTN, - CORINFO_VIRTUALCALL_VTABLE + CORINFO_VIRTUALCALL_VTABLE, + CORINFO_VIRTUALSTATICCALL_STUB, }; // Indicates that the CORINFO_VIRTUALCALL_VTABLE lookup needn't do a chunk indirection diff --git a/src/coreclr/inc/jithelpers.h b/src/coreclr/inc/jithelpers.h index 2ba380c7f7b145..b97b4841f49d44 100644 --- a/src/coreclr/inc/jithelpers.h +++ b/src/coreclr/inc/jithelpers.h @@ -339,6 +339,8 @@ JITHELPER(CORINFO_HELP_DISPATCH_INDIRECT_CALL, NULL, CORINFO_HELP_SIG_REG_ONLY) #endif + JITHELPER(CORINFO_HELP_VIRTUAL_STATIC, JIT_DispatchVirtualStatic, CORINFO_HELP_SIG_REG_ONLY) + #undef JITHELPER #undef DYNAMICJITHELPER #undef JITHELPER diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 21ec74fef1bcc7..68b70ca41e28f3 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -3847,6 +3847,7 @@ class Compiler GenTree* getRuntimeContextTree(CORINFO_RUNTIME_LOOKUP_KIND kind); GenTree* impRuntimeLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedToken, + CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken, CORINFO_LOOKUP* pLookup, void* compileTimeHandle); diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 8fc870a55d4a85..9ff9bc0c5d3004 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -2027,7 +2027,7 @@ GenTree* Compiler::impLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedToken, // Need to use dictionary-based access which depends on the typeContext // which is only available at runtime, not at compile-time. - return impRuntimeLookupToTree(pResolvedToken, pLookup, compileTimeHandle); + return impRuntimeLookupToTree(pResolvedToken, nullptr, pLookup, compileTimeHandle); } #ifdef FEATURE_READYTORUN @@ -2202,14 +2202,16 @@ GenTree* Compiler::getRuntimeContextTree(CORINFO_RUNTIME_LOOKUP_KIND kind) // context is the method table pointer of the this object ctxTree = gtNewMethodTableLookup(ctxTree); } - else + else if (kind == CORINFO_LOOKUP_METHODPARAM || kind == CORINFO_LOOKUP_CLASSPARAM) { - assert(kind == CORINFO_LOOKUP_METHODPARAM || kind == CORINFO_LOOKUP_CLASSPARAM); - // Exact method descriptor as passed in ctxTree = gtNewLclvNode(pRoot->info.compTypeCtxtArg, TYP_I_IMPL); ctxTree->gtFlags |= GTF_VAR_CONTEXT; } + else + { + assert(kind == CORINFO_LOOKUP_VIRTUALSTATIC); + } return ctxTree; } @@ -2232,10 +2234,20 @@ GenTree* Compiler::getRuntimeContextTree(CORINFO_RUNTIME_LOOKUP_KIND kind) */ GenTree* Compiler::impRuntimeLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedToken, + CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken, CORINFO_LOOKUP* pLookup, void* compileTimeHandle) { - GenTree* ctxTree = getRuntimeContextTree(pLookup->lookupKind.runtimeLookupKind); + GenTree* ctxTree; + + if (pLookup->lookupKind.runtimeLookupKind == CORINFO_LOOKUP_VIRTUALSTATIC) + { + ctxTree = gtNewIconEmbClsHndNode(pConstrainedResolvedToken->hClass); + } + else + { + ctxTree = getRuntimeContextTree(pLookup->lookupKind.runtimeLookupKind); + } CORINFO_RUNTIME_LOOKUP* pRuntimeLookup = &pLookup->runtimeLookup; // It's available only via the run-time helper function @@ -9455,6 +9467,48 @@ var_types Compiler::impImportCall(OPCODE opcode, switch (callInfo->kind) { + case CORINFO_VIRTUALSTATICCALL_STUB: + { + assert(mflags & CORINFO_FLG_STATIC); + if (callInfo->stubLookup.lookupKind.needsRuntimeLookup) + { + GenTree* stubAddr = impRuntimeLookupToTree(pResolvedToken, pConstrainedResolvedToken, &callInfo->stubLookup, methHnd); + assert(stubAddr != nullptr); + + // The stubAddr may be a + // complex expression. As it is evaluated after the args, + // it may cause registered args to be spilled. Simply spill it. + + unsigned lclNum = lvaGrabTemp(true DEBUGARG("VirtualCall with runtime lookup")); + impAssignTempGen(lclNum, stubAddr, (unsigned)CHECK_SPILL_NONE); + stubAddr = gtNewLclvNode(lclNum, TYP_I_IMPL); + + call = gtNewIndCallNode(stubAddr, callRetTyp); + + call->gtFlags |= GTF_EXCEPT | (stubAddr->gtFlags & GTF_GLOB_EFFECT); + call->gtFlags |= GTF_CALL_VIRT_STUB; + +#ifdef TARGET_X86 + // No tailcalls allowed for these yet... + canTailCall = false; + szCanTailCallFailReason = "VirtualStaticCall with runtime lookup"; +#endif + } + else + { + // The stub address is known at compile time + call = gtNewCallNode(CT_USER_FUNC, callInfo->hMethod, callRetTyp, di); + call->AsCall()->gtStubCallStubAddr = callInfo->stubLookup.constLookup.addr; + call->gtFlags |= GTF_CALL_VIRT_STUB; + assert(callInfo->stubLookup.constLookup.accessType != IAT_PPVALUE && + callInfo->stubLookup.constLookup.accessType != IAT_RELPVALUE); + if (callInfo->stubLookup.constLookup.accessType == IAT_PVALUE) + { + call->AsCall()->gtCallMoreFlags |= GTF_CALL_M_VIRTSTUB_REL_INDIRECT; + } + } + break; + } case CORINFO_VIRTUALCALL_STUB: { @@ -9470,7 +9524,7 @@ var_types Compiler::impImportCall(OPCODE opcode, return TYP_UNDEF; } - GenTree* stubAddr = impRuntimeLookupToTree(pResolvedToken, &callInfo->stubLookup, methHnd); + GenTree* stubAddr = impRuntimeLookupToTree(pResolvedToken, nullptr, &callInfo->stubLookup, methHnd); assert(!compDonotInline()); // This is the rough code to set up an indirect stub call diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index 9a173957fa007a..273e310d8c0bb3 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -191,6 +191,7 @@ public enum CORINFO_RUNTIME_LOOKUP_KIND CORINFO_LOOKUP_THISOBJ, CORINFO_LOOKUP_METHODPARAM, CORINFO_LOOKUP_CLASSPARAM, + CORINFO_LOOKUP_VIRTUALSTATIC, CORINFO_LOOKUP_NOT_SUPPORTED, // Returned for attempts to inline dictionary lookups } diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index 2c2d3b9a3048eb..dd5a9b816a3a22 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -5659,6 +5659,26 @@ HCIMPLEND_RAW EXTERN_C void JIT_ValidateIndirectCall(); EXTERN_C void JIT_DispatchIndirectCall(); +HCIMPL2_VV(void*, JIT_DispatchVirtualStatic, MethodTable *constrainedType, MethodDesc *interfaceMethod) +{ + FCALL_CONTRACT; + + BOOL uniqueResolution; + MethodDesc *methodDesc = constrainedType->ResolveVirtualStaticMethod( + interfaceMethod->GetMethodTable(), + interfaceMethod, + /* allowNullResult */ FALSE, + /* verifyImplemented */ FALSE, + /* allowVariantMatches */ TRUE, + /* uniqueResolution */ &uniqueResolution); + if (methodDesc != nullptr && !uniqueResolution) + { + FCThrow(kAmbiguousImplementationException); + } + return methodDesc; +} +HCIMPLEND + //======================================================================== // // JIT HELPERS INITIALIZATION diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 1c18719e508a7e..4134a08203bd0b 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -2875,6 +2875,18 @@ static bool IsTypeSpecForTypicalInstantiation(SigPointer sigptr) return IsSignatureForTypicalInstantiation(sigptr, ELEMENT_TYPE_VAR, ntypars); } +static void EncodeStaticVirtualRuntimeLookup(CORINFO_LOOKUP *pResultLookup, CORINFO_METHOD_HANDLE methodHandle) +{ + pResultLookup->lookupKind.runtimeLookupKind = CORINFO_LOOKUP_VIRTUALSTATIC; + + CORINFO_RUNTIME_LOOKUP *pResult = &pResultLookup->runtimeLookup; + pResult->signature = (void*)methodHandle; + pResult->helper = CORINFO_HELP_VIRTUAL_STATIC; + pResult->indirections = CORINFO_USEHELPER; + pResult->testForNull = 0; + pResult->testForFixup = 0; +} + void CEEInfo::ComputeRuntimeLookupForSharedGenericToken(DictionaryEntryKind entryKind, CORINFO_RESOLVED_TOKEN * pResolvedToken, CORINFO_RESOLVED_TOKEN * pConstrainedResolvedToken, @@ -2913,7 +2925,20 @@ void CEEInfo::ComputeRuntimeLookupForSharedGenericToken(DictionaryEntryKind entr // There is a pathological case where invalid IL refereces __Canon type directly, but there is no dictionary availabled to store the lookup. if (!pContextMD->IsSharedByGenericInstantiations()) + { + if (pTemplateMD->IsStatic() && pConstrainedResolvedToken != nullptr) + { + pResultLookup->lookupKind.runtimeLookupKind = CORINFO_LOOKUP_VIRTUALSTATIC; + pResult->signature = (void*)pResolvedToken->hMethod; + pResult->helper = CORINFO_HELP_VIRTUAL_STATIC; + pResult->indirections = CORINFO_USEHELPER; + pResult->testForNull = 0; + pResult->testForFixup = 0; + return; + } + COMPlusThrow(kInvalidProgramException); + } BOOL fInstrument = FALSE; @@ -4842,6 +4867,8 @@ void CEEInfo::getCallInfo( constrainedType = TypeHandle(pConstrainedResolvedToken->hClass); } + BOOL fIsStaticVirtualMethod = (pConstrainedResolvedToken != NULL && pMD->IsInterface() && pMD->IsStatic()); + BOOL fResolvedConstraint = FALSE; BOOL fForceUseRuntimeLookup = FALSE; @@ -4935,10 +4962,14 @@ void CEEInfo::getCallInfo( { pResult->thisTransform = CORINFO_BOX_THIS; } - else + else if (!fIsStaticVirtualMethod) { pResult->thisTransform = CORINFO_DEREF_THIS; } + else + { + pResult->thisTransform = CORINFO_NO_THIS_TRANSFORM; + } } // @@ -4988,7 +5019,7 @@ void CEEInfo::getCallInfo( bool directCall = false; bool resolvedCallVirt = false; - if (flags & CORINFO_CALLINFO_LDFTN) + if ((flags & CORINFO_CALLINFO_LDFTN) && (!fIsStaticVirtualMethod || fResolvedConstraint)) { // Since the ldvirtftn instruction resolves types // at run-time we do this earlier than ldftn. The @@ -5013,12 +5044,12 @@ void CEEInfo::getCallInfo( } else // Static methods are always direct calls - if (pTargetMD->IsStatic()) + if (pTargetMD->IsStatic() && (!fIsStaticVirtualMethod || fResolvedConstraint)) { directCall = true; } else - if (!(flags & CORINFO_CALLINFO_CALLVIRT) || fResolvedConstraint) + if ((!fIsStaticVirtualMethod && !(flags & CORINFO_CALLINFO_CALLVIRT)) || fResolvedConstraint) { directCall = true; } @@ -5146,13 +5177,13 @@ void CEEInfo::getCallInfo( // All virtual calls which take method instantiations must // currently be implemented by an indirect call via a runtime-lookup // function pointer - else if (pTargetMD->HasMethodInstantiation()) + else if (pTargetMD->HasMethodInstantiation() && !fIsStaticVirtualMethod) { pResult->kind = CORINFO_VIRTUALCALL_LDVIRTFTN; // stub dispatch can't handle generic method calls yet pResult->nullInstanceCheck = TRUE; } // Non-interface dispatches go through the vtable. - else if (!pTargetMD->IsInterface()) + else if (!pTargetMD->IsInterface() && !fIsStaticVirtualMethod) { pResult->kind = CORINFO_VIRTUALCALL_VTABLE; pResult->nullInstanceCheck = TRUE; @@ -5165,17 +5196,21 @@ void CEEInfo::getCallInfo( pResult->kind = CORINFO_VIRTUALCALL_LDVIRTFTN; #else // STUB_DISPATCH_PORTABLE pResult->kind = CORINFO_VIRTUALCALL_STUB; + if (fIsStaticVirtualMethod) + { + pResult->kind = CORINFO_VIRTUALSTATICCALL_STUB; + } // We can't make stub calls when we need exact information // for interface calls from shared code. - if (pResult->exactContextNeedsRuntimeLookup) + if (pResult->exactContextNeedsRuntimeLookup || fIsStaticVirtualMethod) { _ASSERTE(!m_pMethodBeingCompiled->IsDynamicMethod()); ComputeRuntimeLookupForSharedGenericToken(DispatchStubAddrSlot, pResolvedToken, - NULL, + pConstrainedResolvedToken, pMD, &pResult->stubLookup); } diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index bd2da9fac4c620..b6c31c4480c1d6 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -5476,7 +5476,7 @@ namespace MethodTable *interfaceMT, BOOL allowVariance, MethodDesc **candidateMD, - BOOL throwOnConflict) + BOOL *uniqueResolution) { *candidateMD = NULL; @@ -5570,7 +5570,7 @@ namespace interfaceMT, interfaceMD, /* verifyImplemented */ FALSE, - /* throwOnConflict */ throwOnConflict); + /* uniqueResolution */ uniqueResolution); } } } @@ -5616,7 +5616,7 @@ BOOL MethodTable::FindDefaultInterfaceImplementation( INSTANCE_CHECK; MODE_ANY; THROWS; - GC_TRIGGERS; + // GC_TRIGGERS; PRECONDITION(CheckPointer(pInterfaceMD)); PRECONDITION(CheckPointer(pInterfaceMT)); PRECONDITION(CheckPointer(ppDefaultMethod)); @@ -5629,7 +5629,8 @@ BOOL MethodTable::FindDefaultInterfaceImplementation( // Check the current method table itself MethodDesc *candidateMaybe = NULL; - if (IsInterface() && TryGetCandidateImplementation(this, pInterfaceMD, pInterfaceMT, allowVariance, &candidateMaybe, throwOnConflict)) + BOOL uniqueResolution; + if (IsInterface() && TryGetCandidateImplementation(this, pInterfaceMD, pInterfaceMT, allowVariance, &candidateMaybe, &uniqueResolution)) { _ASSERTE(candidateMaybe != NULL); @@ -5669,7 +5670,7 @@ BOOL MethodTable::FindDefaultInterfaceImplementation( MethodTable *pCurMT = it.GetInterface(pMT); MethodDesc *pCurMD = NULL; - if (TryGetCandidateImplementation(pCurMT, pInterfaceMD, pInterfaceMT, allowVariance, &pCurMD, throwOnConflict)) + if (TryGetCandidateImplementation(pCurMT, pInterfaceMD, pInterfaceMT, allowVariance, &pCurMD, &uniqueResolution)) { // // Found a match. But is it a more specific match (we want most specific interfaces) @@ -7998,7 +7999,7 @@ MethodTable::ResolveVirtualStaticMethod( BOOL allowNullResult, BOOL verifyImplemented, BOOL allowVariantMatches, - BOOL throwOnConflict) + BOOL* uniqueResolution) { if (!pInterfaceMD->IsSharedByGenericMethodInstantiations() && !pInterfaceType->IsSharedByGenericInstantiations()) { @@ -8027,7 +8028,7 @@ MethodTable::ResolveVirtualStaticMethod( // Search for match on a per-level in the type hierarchy for (MethodTable* pMT = this; pMT != nullptr; pMT = pMT->GetParentMethodTable()) { - MethodDesc* pMD = pMT->TryResolveVirtualStaticMethodOnThisType(pInterfaceType, pInterfaceMD, verifyImplemented, throwOnConflict); + MethodDesc* pMD = pMT->TryResolveVirtualStaticMethodOnThisType(pInterfaceType, pInterfaceMD, verifyImplemented, uniqueResolution); if (pMD != nullptr) { return pMD; @@ -8071,7 +8072,7 @@ MethodTable::ResolveVirtualStaticMethod( { // Variant or equivalent matching interface found // Attempt to resolve on variance matched interface - pMD = pMT->TryResolveVirtualStaticMethodOnThisType(pItfInMap, pInterfaceMD, verifyImplemented, throwOnConflict); + pMD = pMT->TryResolveVirtualStaticMethodOnThisType(pItfInMap, pInterfaceMD, verifyImplemented, uniqueResolution); if (pMD != nullptr) { return pMD; @@ -8080,19 +8081,19 @@ MethodTable::ResolveVirtualStaticMethod( } } - // I have yet to figure out how to modify getCallInfo to support lazy throws upon - // ambiguous default interface method resolution. For now this effectively disables - // the uniqueness check. - /*BOOL haveUniqueDefaultImplementation =*/ pMT->FindDefaultInterfaceImplementation( + BOOL haveUniqueDefaultImplementation = pMT->FindDefaultInterfaceImplementation( pInterfaceMD, pInterfaceType, &pMD, /* allowVariance */ allowVariantMatches, - /* throwOnConflict */ throwOnConflict); - // if (haveUniqueDefaultImplementation || (pMD != nullptr && verifyImplemented)) - if (pMD != nullptr) + /* throwOnConflict */ uniqueResolution == nullptr); + if (haveUniqueDefaultImplementation || (pMD != nullptr && verifyImplemented)) { // We tolerate conflicts upon verification of implemented SVMs so that they only blow up when actually called at execution time. + if (uniqueResolution != nullptr) + { + *uniqueResolution = haveUniqueDefaultImplementation; + } return pMD; } } @@ -8115,7 +8116,7 @@ MethodTable::ResolveVirtualStaticMethod( // Try to locate the appropriate MethodImpl matching a given interface static virtual method. // Returns nullptr on failure. MethodDesc* -MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL verifyImplemented, BOOL throwOnConflict) +MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL verifyImplemented, BOOL* uniqueResolution) { HRESULT hr = S_OK; IMDInternalImport* pMDInternalImport = GetMDImport(); @@ -8250,7 +8251,7 @@ MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType { return pMethodImpl; } - if (pPrevMethodImpl != nullptr && throwOnConflict) + if (pPrevMethodImpl != nullptr && uniqueResolution == nullptr) { // Two MethodImpl records found for the same virtual static interface method COMPlusThrow(kTypeLoadException, E_FAIL); @@ -8275,6 +8276,10 @@ MethodTable::VerifyThatAllVirtualStaticMethodsAreImplemented() for (MethodIterator it(pInterfaceMT); it.IsValid(); it.Next()) { MethodDesc *pMD = it.GetMethodDesc(); + // This flag is not really used, passing its address to ResolveVirtualStaticMethod just suppresses + // the ambiguous resolution exception throw as we should delay the exception until we actually hit + // the ambiguity at execution time. + BOOL uniqueResolution; if (pMD->IsVirtual() && pMD->IsStatic() && (pMD->IsAbstract() && @@ -8284,7 +8289,7 @@ MethodTable::VerifyThatAllVirtualStaticMethodsAreImplemented() /* allowNullResult */ TRUE, /* verifyImplemented */ TRUE, /* allowVariantMatches */ FALSE, - /* throwOnConflict */ FALSE))) + /* uniqueResolution */ &uniqueResolution))) { IMDInternalImport* pInternalImport = GetModule()->GetMDImport(); GetModule()->GetAssembly()->ThrowTypeLoadException(pInternalImport, GetCl(), pMD->GetName(), IDS_CLASSLOAD_STATICVIRTUAL_NOTIMPL); @@ -8317,14 +8322,15 @@ MethodTable::TryResolveConstraintMethodApprox( { _ASSERTE(!thInterfaceType.IsTypeDesc()); _ASSERTE(thInterfaceType.IsInterface()); + BOOL uniqueResolution; MethodDesc *result = ResolveVirtualStaticMethod( thInterfaceType.GetMethodTable(), pInterfaceMD, /* allowNullResult */pfForceUseRuntimeLookup != NULL, /* verifyImplemented */ FALSE, /* allowVariantMatches */ TRUE, - /* throwOnConflict */ FALSE); - if (result == NULL) + &uniqueResolution); + if (result == NULL || !uniqueResolution) { *pfForceUseRuntimeLookup = TRUE; } diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index b6a3747a9f759f..e2788419b5ea57 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -2092,14 +2092,15 @@ class MethodTable // Specify allowNullResult to return NULL instead of throwing if the there is no implementation // Specify verifyImplemented to verify that there is a match, but do not actually return a final usable MethodDesc // Specify allowVariantMatches to permit generic interface variance - // Specify throwOnConflict to throw upon ambiguous static virtual method resolution. + // Specify uniqueResolution to store the flag saying whether the resolution was unambiguous; + // when NULL, throw an AmbiguousResolutionException upon hitting ambiguous SVM resolution. MethodDesc *ResolveVirtualStaticMethod( MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL allowNullResult, BOOL verifyImplemented = FALSE, BOOL allowVariantMatches = TRUE, - BOOL throwOnConflict = TRUE); + BOOL *uniqueResolution = NULL); // Try a partial resolve of the constraint call, up to generic code sharing. // @@ -2217,7 +2218,7 @@ class MethodTable // Try to resolve a given static virtual method override on this type. Return nullptr // when not found. - MethodDesc *TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL verifyImplemented, BOOL throwOnConflict); + MethodDesc *TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL verifyImplemented, BOOL* uniqueResolution); public: static MethodDesc *MapMethodDeclToMethodImpl(MethodDesc *pMDDecl); diff --git a/src/coreclr/vm/runtimehandles.cpp b/src/coreclr/vm/runtimehandles.cpp index 400f3f2a1c61fd..c849a0a53d5eb3 100644 --- a/src/coreclr/vm/runtimehandles.cpp +++ b/src/coreclr/vm/runtimehandles.cpp @@ -1079,7 +1079,12 @@ extern "C" MethodDesc* QCALLTYPE RuntimeTypeHandle_GetInterfaceMethodImplementat if (pMD->IsStatic()) { - pResult = typeHandle.GetMethodTable()->ResolveVirtualStaticMethod(thOwnerOfMD.GetMethodTable(), pMD, /* allowNullResult */ TRUE, /* verifyImplemented*/ FALSE, /*allowVariantMatches */ TRUE); + pResult = typeHandle.GetMethodTable()->ResolveVirtualStaticMethod( + thOwnerOfMD.GetMethodTable(), + pMD, + /* allowNullResult */ TRUE, + /* verifyImplemented*/ FALSE, + /*allowVariantMatches */ TRUE); } else { diff --git a/src/coreclr/vm/typedesc.cpp b/src/coreclr/vm/typedesc.cpp index e7eacb9c2ff85f..af91c3dd03e89e 100644 --- a/src/coreclr/vm/typedesc.cpp +++ b/src/coreclr/vm/typedesc.cpp @@ -1607,7 +1607,8 @@ BOOL TypeVarTypeDesc::SatisfiesConstraints(SigTypeContext *pTypeContextOfConstra MethodDesc *pMD = it.GetMethodDesc(); if (pMD->IsVirtual() && pMD->IsStatic() && - (pMD->IsAbstract() && !thElem.AsMethodTable()->ResolveVirtualStaticMethod(pInterfaceMT, pMD, /* allowNullResult */ TRUE, /* verifyImplemented */ TRUE))) + (pMD->IsAbstract() && !thElem.AsMethodTable()->ResolveVirtualStaticMethod( + pInterfaceMT, pMD, /* allowNullResult */ TRUE, /* verifyImplemented */ TRUE))) { virtualStaticResolutionCheckFailed = true; break; From 21610bc1130cb484384256aa5cd47f2c33aa6d2b Mon Sep 17 00:00:00 2001 From: Tomas Rylek Date: Thu, 28 Apr 2022 01:02:54 +0200 Subject: [PATCH 06/22] Additional JIT changes to make default impl's of SVM resolve properly --- src/coreclr/vm/jithelpers.cpp | 12 ++++++++++-- src/coreclr/vm/methodtable.cpp | 7 ++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index dd5a9b816a3a22..a3e817f8e750d9 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -5664,17 +5664,25 @@ HCIMPL2_VV(void*, JIT_DispatchVirtualStatic, MethodTable *constrainedType, Metho FCALL_CONTRACT; BOOL uniqueResolution; + + FC_CAN_TRIGGER_GC(); MethodDesc *methodDesc = constrainedType->ResolveVirtualStaticMethod( interfaceMethod->GetMethodTable(), interfaceMethod, - /* allowNullResult */ FALSE, + /* allowNullResult */ TRUE, /* verifyImplemented */ FALSE, /* allowVariantMatches */ TRUE, /* uniqueResolution */ &uniqueResolution); - if (methodDesc != nullptr && !uniqueResolution) + FC_CAN_TRIGGER_GC_END(); + + if (!uniqueResolution) { FCThrow(kAmbiguousImplementationException); } + else if (methodDesc == nullptr) + { + FCThrow(kTypeLoadException); + } return methodDesc; } HCIMPLEND diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index b6c31c4480c1d6..9c9d4d7e0fb489 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -8001,6 +8001,10 @@ MethodTable::ResolveVirtualStaticMethod( BOOL allowVariantMatches, BOOL* uniqueResolution) { + if (uniqueResolution != nullptr) + { + *uniqueResolution = TRUE; + } if (!pInterfaceMD->IsSharedByGenericMethodInstantiations() && !pInterfaceType->IsSharedByGenericInstantiations()) { // Check that there is no implementation of the interface on this type which is the canonical interface for a shared generic. If so, that indicates that @@ -8087,7 +8091,7 @@ MethodTable::ResolveVirtualStaticMethod( &pMD, /* allowVariance */ allowVariantMatches, /* throwOnConflict */ uniqueResolution == nullptr); - if (haveUniqueDefaultImplementation || (pMD != nullptr && verifyImplemented)) + if (haveUniqueDefaultImplementation || (pMD != nullptr && (verifyImplemented || uniqueResolution != nullptr))) { // We tolerate conflicts upon verification of implemented SVMs so that they only blow up when actually called at execution time. if (uniqueResolution != nullptr) @@ -8333,6 +8337,7 @@ MethodTable::TryResolveConstraintMethodApprox( if (result == NULL || !uniqueResolution) { *pfForceUseRuntimeLookup = TRUE; + result = NULL; } return result; } From 7a2fbb971601b7697afbcb27fce339e35e6cfaf6 Mon Sep 17 00:00:00 2001 From: Tomas Rylek Date: Thu, 28 Apr 2022 01:15:56 +0200 Subject: [PATCH 07/22] Fix temporary instrumentation --- src/coreclr/vm/methodtable.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 9c9d4d7e0fb489..cf74b623a6b9a8 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -5616,7 +5616,7 @@ BOOL MethodTable::FindDefaultInterfaceImplementation( INSTANCE_CHECK; MODE_ANY; THROWS; - // GC_TRIGGERS; + GC_TRIGGERS; PRECONDITION(CheckPointer(pInterfaceMD)); PRECONDITION(CheckPointer(pInterfaceMT)); PRECONDITION(CheckPointer(ppDefaultMethod)); From e402364977b66c6f9df6393e0a136c40bd0c99ab Mon Sep 17 00:00:00 2001 From: Tomas Rylek Date: Thu, 28 Apr 2022 20:15:45 +0200 Subject: [PATCH 08/22] Address initial JanK's PR feedback --- src/coreclr/vm/jithelpers.cpp | 59 ++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index a3e817f8e750d9..a92bf7e620506b 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -3375,6 +3375,37 @@ NOINLINE HCIMPL3(CORINFO_MethodPtr, JIT_VirtualFunctionPointer_Framed, Object * } HCIMPLEND +HCIMPL2(void*, JIT_DispatchVirtualStatic, MethodTable *constrainedType, MethodDesc *interfaceMethod) +{ + FCALL_CONTRACT; + + BOOL uniqueResolution = FALSE; + MethodDesc *methodDesc = nullptr; + + HELPER_METHOD_FRAME_BEGIN_0(); + + methodDesc = constrainedType->ResolveVirtualStaticMethod( + interfaceMethod->GetMethodTable(), + interfaceMethod, + /* allowNullResult */ TRUE, + /* verifyImplemented */ FALSE, + /* allowVariantMatches */ TRUE, + /* uniqueResolution */ &uniqueResolution); + + HELPER_METHOD_FRAME_END(); + + if (!uniqueResolution) + { + FCThrow(kAmbiguousImplementationException); + } + else if (methodDesc == nullptr) + { + FCThrow(kTypeLoadException); + } + return methodDesc; +} +HCIMPLEND + HCIMPL1(Object*, JIT_GetRuntimeFieldStub, CORINFO_FIELD_HANDLE field) { FCALL_CONTRACT; @@ -5659,34 +5690,6 @@ HCIMPLEND_RAW EXTERN_C void JIT_ValidateIndirectCall(); EXTERN_C void JIT_DispatchIndirectCall(); -HCIMPL2_VV(void*, JIT_DispatchVirtualStatic, MethodTable *constrainedType, MethodDesc *interfaceMethod) -{ - FCALL_CONTRACT; - - BOOL uniqueResolution; - - FC_CAN_TRIGGER_GC(); - MethodDesc *methodDesc = constrainedType->ResolveVirtualStaticMethod( - interfaceMethod->GetMethodTable(), - interfaceMethod, - /* allowNullResult */ TRUE, - /* verifyImplemented */ FALSE, - /* allowVariantMatches */ TRUE, - /* uniqueResolution */ &uniqueResolution); - FC_CAN_TRIGGER_GC_END(); - - if (!uniqueResolution) - { - FCThrow(kAmbiguousImplementationException); - } - else if (methodDesc == nullptr) - { - FCThrow(kTypeLoadException); - } - return methodDesc; -} -HCIMPLEND - //======================================================================== // // JIT HELPERS INITIALIZATION From c1e1483fd1768422248146e70cfa41cbfdd92370 Mon Sep 17 00:00:00 2001 From: Tomas Rylek Date: Fri, 29 Apr 2022 01:20:00 +0200 Subject: [PATCH 09/22] Add struct variants for the svm_diamondshape tests This change extends the svm_diamondshape test to exercise value types. As next step I'll work on testing the ldftn instruction. Thanks Tomas --- .../DiamondShape/svm_diamondshape.il | 274 +++++++++++++++--- 1 file changed, 239 insertions(+), 35 deletions(-) diff --git a/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il index 32f956d2376819..c948d95fe29544 100644 --- a/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il +++ b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il @@ -117,6 +117,14 @@ { } // end of class FooClass +.class private auto ansi sealed beforefieldinit FooStruct + extends [mscorlib]System.ValueType + implements IFoo2, + IFoo, + IFooEx +{ +} // end of class FooStruct + .class interface private abstract auto ansi I1 { .method public hidebysig newslot abstract virtual @@ -212,6 +220,15 @@ { } // end of class I4Class +.class private auto ansi sealed beforefieldinit I4Struct + extends [mscorlib]System.ValueType + implements I4, + I2, + I1, + I3 +{ +} // end of class I4Struct + .class interface private abstract auto ansi I5 implements I1 { @@ -332,6 +349,18 @@ { } // end of class I47Class +.class private auto ansi sealed beforefieldinit I47Struct + extends [mscorlib]System.ValueType + implements I4, + I2, + I1, + I3, + I7, + I5, + I6 +{ +} // end of class I47Struct + .class private auto ansi beforefieldinit I8Class extends [mscorlib]System.Object implements I8, @@ -345,6 +374,19 @@ { } // end of class I8Class +.class private auto ansi sealed beforefieldinit I8Struct + extends [mscorlib]System.ValueType + implements I8, + I4, + I2, + I1, + I3, + I7, + I5, + I6 +{ +} // end of class I8Struct + .class interface private abstract auto ansi GI1`1 { .method public hidebysig newslot abstract virtual @@ -535,6 +577,14 @@ { } // end of class GI23Class`1 +.class private auto ansi sealed beforefieldinit GI23Struct`1 + extends [mscorlib]System.ValueType + implements class GI2`1, + class GI1`1, + class GI3`1 +{ +} // end of class GI23Struct`1 + .class private auto ansi beforefieldinit GI4Class`1 extends [mscorlib]System.Object implements class GI4`1, @@ -544,6 +594,15 @@ { } // end of class GI4Class`1 +.class private auto ansi sealed beforefieldinit GI4Struct`1 + extends [mscorlib]System.ValueType + implements class GI4`1, + class GI2`1, + class GI1`1, + class GI3`1 +{ +} // end of class GI4Struct`1 + .class private auto ansi beforefieldinit Program extends [mscorlib]System.Object { @@ -557,7 +616,7 @@ class [mscorlib]System.Type[] V_8, class [mscorlib]System.Exception V_9) IL_0000: nop - IL_0009: ldstr "Calling IFoo.Foo on Foo - expecting exception." + IL_0009: ldstr "Calling IFoo.Foo on FooClass - expecting exception." IL_000e: call void [mscorlib]System.Console::WriteLine(string) IL_0013: nop .try @@ -568,7 +627,7 @@ IL_0018: call int32 IFoo::Foo(int32) IL_001d: pop IL_001e: ldc.i4.0 - IL_001f: ldstr "Expecting exception on Foo" + IL_001f: ldstr "Expecting exception on FooClass" IL_0024: call void Test::Assert(bool, string) IL_0029: nop @@ -591,6 +650,41 @@ IL_0048: leave.s IL_004a } // end handler + + nop + ldstr "Calling IFoo.Foo on FooStruct - expecting exception." + call void [mscorlib]System.Console::WriteLine(string) + nop + .try + { + nop + ldc.i4.s 10 + constrained. class [svm_diamondshape]FooStruct + call int32 IFoo::Foo(int32) + pop + ldc.i4.0 + ldstr "Expecting exception on FooStruct" + call void Test::Assert(bool, + string) + nop + leave.s IL_004a + + } // end .try + catch [System.Runtime]System.Runtime.AmbiguousImplementationException + { + stloc.s V_6 + nop + ldstr "Exception caught: " + ldloc.s V_6 + callvirt instance string [mscorlib]System.Object::ToString() + call string [mscorlib]System.String::Concat(string, + string) + call void [mscorlib]System.Console::WriteLine(string) + nop + leave.s IL_004a + + } // end handler + IL_004a: ldstr "Calling I1.Func on I47Class - expecting exception" IL_0057: call void [mscorlib]System.Console::WriteLine(string) IL_005c: nop @@ -607,7 +701,7 @@ string) IL_0072: nop IL_0073: nop - IL_0074: leave.s IL_0093 + IL_0074: leave.s IL_0093a } // end .try catch [System.Runtime]System.Runtime.AmbiguousImplementationException @@ -622,11 +716,44 @@ IL_008a: call void [mscorlib]System.Console::WriteLine(string) IL_008f: nop IL_0090: nop - IL_0091: leave.s IL_0093 + IL_0091: leave.s IL_0093a + + } // end handler + + IL_0093a: ldstr "Calling I1.Func on I47Struct - expecting exception" + call void [mscorlib]System.Console::WriteLine(string) + nop + .try + { + nop + ldc.i4.s 10 + constrained. valuetype [svm_diamondshape]I47Struct + call int32 I1::Func(int32) + pop + ldc.i4.0 + ldstr "Expecting exception on I47Struct" + call void Test::Assert(bool, string) + nop + nop + leave.s IL_0093 + + } // end .try + catch [System.Runtime]System.Runtime.AmbiguousImplementationException + { + stloc.s V_7 + nop + ldstr "Exception caught: " + ldloc.s V_7 + callvirt instance string [mscorlib]System.Object::ToString() + call string [mscorlib]System.String::Concat(string, string) + call void [mscorlib]System.Console::WriteLine(string) + nop + nop + leave.s IL_0093 } // end handler - IL_0093: ldstr "Calling GI1.Func on GI23Class - expecting ex" - + "ception" + + IL_0093: ldstr "Calling GI1.Func on GI23Class - expecting exception" IL_00a3: call void [mscorlib]System.Console::WriteLine(string) IL_00a8: nop .try @@ -638,11 +765,10 @@ IL_00b3: pop IL_00b4: ldc.i4.0 IL_00b5: ldstr "Expecting exception on GI23Class" - IL_00ba: call void Test::Assert(bool, - string) + IL_00ba: call void Test::Assert(bool, string) IL_00bf: nop IL_00c0: nop - IL_00c1: leave.s IL_00e0 + IL_00c1: leave.s IL_00e0a } // end .try catch [System.Runtime]System.Runtime.AmbiguousImplementationException @@ -652,14 +778,47 @@ IL_00c6: ldstr "Exception caught: " IL_00cb: ldloc.s V_9 IL_00cd: callvirt instance string [mscorlib]System.Object::ToString() - IL_00d2: call string [mscorlib]System.String::Concat(string, - string) + IL_00d2: call string [mscorlib]System.String::Concat(string, string) IL_00d7: call void [mscorlib]System.Console::WriteLine(string) IL_00dc: nop - IL_00dd: nop - IL_00de: leave.s IL_00e0 + IL_00de: leave.s IL_00e0a + + } // end handler + + IL_00e0a: ldstr "Calling GI1.Func on GI23Struct - expecting exception" + call void [mscorlib]System.Console::WriteLine(string) + nop + .try + { + nop + ldloca.s V_8 + constrained. valuetype [svm_diamondshape]GI23Struct`1 + call int32 class GI1`1::Func(class [mscorlib]System.Type[]&) + pop + ldc.i4.0 + ldstr "Expecting exception on GI23Class" + call void Test::Assert(bool, + string) + nop + leave.s IL_00e0 + + } // end .try + catch [System.Runtime]System.Runtime.AmbiguousImplementationException + { + stloc.s V_9 + nop + ldstr "Exception caught: " + ldloc.s V_9 + callvirt instance string [mscorlib]System.Object::ToString() + call string [mscorlib]System.String::Concat(string, + string) + call void [mscorlib]System.Console::WriteLine(string) + nop + nop + leave.s IL_00e0 } // end handler + IL_00e0: ret } // end of method Program::Negative @@ -667,12 +826,7 @@ { // Code size 189 (0xbd) .maxstack 2 - .locals init (class I4Class V_0, - class I1 V_1, - class I8Class V_2, - class GI4Class`1 V_3, - class [mscorlib]System.Type[] V_4, - class GI1`1 V_5) + .locals init (class [mscorlib]System.Type[] V_4) IL_0000: nop IL_0001: ldstr "Calling I1.Func on I4Class - expecting I4.Func" IL_0006: call void [mscorlib]System.Console::WriteLine(string) @@ -683,9 +837,21 @@ IL_001c: ldc.i4.s 14 IL_001e: ceq IL_0020: ldstr "Expecting I1.Func to land on I4.Func" - IL_0025: call void Test::Assert(bool, - string) + IL_0025: call void Test::Assert(bool, string) IL_002a: nop + + ldstr "Calling I1.Func on I4Struct - expecting I4.Func" + call void [mscorlib]System.Console::WriteLine(string) + nop + ldc.i4.s 10 + constrained. valuetype I4Struct + call int32 I1::Func(int32) + ldc.i4.s 14 + ceq + ldstr "Expecting I1.Func to land on I4.Func" + call void Test::Assert(bool, string) + nop + IL_002b: ldstr "Calling I1.Func on I8Class - expecting I8.Func" IL_0030: call void [mscorlib]System.Console::WriteLine(string) IL_0035: nop @@ -695,11 +861,22 @@ IL_0046: ldc.i4.s 18 IL_0048: ceq IL_004a: ldstr "Expecting I1.Func to land on I8.Func" - IL_004f: call void Test::Assert(bool, - string) + IL_004f: call void Test::Assert(bool, string) IL_0054: nop - IL_0055: ldstr "Calling GI1.Func on GI4Class - expecting G" - + "I4.Func" + + ldstr "Calling I1.Func on I8Struct - expecting I8.Func" + call void [mscorlib]System.Console::WriteLine(string) + nop + ldc.i4.s 10 + constrained. valuetype [svm_diamondshape]I8Struct + call int32 I1::Func(int32) + ldc.i4.s 18 + ceq + ldstr "Expecting I1.Func to land on I8.Func" + call void Test::Assert(bool, string) + nop + + IL_0055: ldstr "Calling GI1.Func on GI4Class - expecting GI4.Func" IL_005a: call void [mscorlib]System.Console::WriteLine(string) IL_005f: nop IL_006b: ldloca.s V_4 @@ -708,30 +885,57 @@ IL_0072: ldc.i4.4 IL_0073: ceq IL_0075: ldstr "Expecting GI1.Func to land on GII4.Func" - IL_007a: call void Test::Assert(bool, - string) + IL_007a: call void Test::Assert(bool, string) IL_007f: nop + + ldloc.s V_4 + ldc.i4.0 + ldelem.ref + ldtoken [mscorlib]System.Object + call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + call bool [mscorlib]System.Type::op_Equality(class [mscorlib]System.Type, class [mscorlib]System.Type) + ldstr "T must be object" + call void Test::Assert(bool, string) + nop + ldloc.s V_4 + ldc.i4.1 + ldelem.ref + ldtoken [mscorlib]System.String + call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + call bool [mscorlib]System.Type::op_Equality(class [mscorlib]System.Type, class [mscorlib]System.Type) + ldstr "S must be string" + call void Test::Assert(bool, string) + nop + + ldstr "Calling GI1.Func on GI4Struct - expecting GI4.Func" + call void [mscorlib]System.Console::WriteLine(string) + nop + ldloca.s V_4 + constrained. valuetype [svm_diamondshape]GI4Struct`1 + call int32 class GI1`1::Func(class [mscorlib]System.Type[]&) + ldc.i4.4 + ceq + ldstr "Expecting GI1.Func to land on GII4.Func" + call void Test::Assert(bool, string) + nop + IL_0080: ldloc.s V_4 IL_0082: ldc.i4.0 IL_0083: ldelem.ref IL_0084: ldtoken [mscorlib]System.Object IL_0089: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) - IL_008e: call bool [mscorlib]System.Type::op_Equality(class [mscorlib]System.Type, - class [mscorlib]System.Type) + IL_008e: call bool [mscorlib]System.Type::op_Equality(class [mscorlib]System.Type, class [mscorlib]System.Type) IL_0093: ldstr "T must be object" - IL_0098: call void Test::Assert(bool, - string) + IL_0098: call void Test::Assert(bool, string) IL_009d: nop IL_009e: ldloc.s V_4 IL_00a0: ldc.i4.1 IL_00a1: ldelem.ref IL_00a2: ldtoken [mscorlib]System.String IL_00a7: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) - IL_00ac: call bool [mscorlib]System.Type::op_Equality(class [mscorlib]System.Type, - class [mscorlib]System.Type) + IL_00ac: call bool [mscorlib]System.Type::op_Equality(class [mscorlib]System.Type, class [mscorlib]System.Type) IL_00b1: ldstr "S must be string" - IL_00b6: call void Test::Assert(bool, - string) + IL_00b6: call void Test::Assert(bool, string) IL_00bb: nop IL_00bc: ret } // end of method Program::Positive From 32c3c9be18cf3d69db0e68dde9dc320d939f3544 Mon Sep 17 00:00:00 2001 From: Tomas Rylek Date: Fri, 29 Apr 2022 02:15:17 +0200 Subject: [PATCH 10/22] Add struct and ldftn testing per David's PR feedback --- src/coreclr/jit/compiler.h | 3 +- src/coreclr/jit/importer.cpp | 21 +- .../DiamondShape/svm_diamondshape.il | 330 +++++++++++++++++- 3 files changed, 338 insertions(+), 16 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 68b70ca41e28f3..f59934ca0f2555 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -3756,7 +3756,7 @@ class Compiler GenTree* impKeepAliveIntrinsic(GenTree* objToKeepAlive); - GenTree* impMethodPointer(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_CALL_INFO* pCallInfo); + GenTree* impMethodPointer(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken, CORINFO_CALL_INFO* pCallInfo); GenTree* impTransformThis(GenTree* thisPtr, CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken, @@ -3840,6 +3840,7 @@ class Compiler } GenTree* impLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedToken, + CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken, CORINFO_LOOKUP* pLookup, GenTreeFlags flags, void* compileTimeHandle); diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 9ff9bc0c5d3004..fee841fd88bab3 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -1957,7 +1957,7 @@ GenTree* Compiler::impTokenToHandle(CORINFO_RESOLVED_TOKEN* pResolvedToken, } // Generate the full lookup tree. May be null if we're abandoning an inline attempt. - GenTree* result = impLookupToTree(pResolvedToken, &embedInfo.lookup, gtTokenToIconFlags(pResolvedToken->token), + GenTree* result = impLookupToTree(pResolvedToken, nullptr, &embedInfo.lookup, gtTokenToIconFlags(pResolvedToken->token), embedInfo.compileTimeHandle); // If we have a result and it requires runtime lookup, wrap it in a runtime lookup node. @@ -1970,6 +1970,7 @@ GenTree* Compiler::impTokenToHandle(CORINFO_RESOLVED_TOKEN* pResolvedToken, } GenTree* Compiler::impLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedToken, + CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken, CORINFO_LOOKUP* pLookup, GenTreeFlags handleFlags, void* compileTimeHandle) @@ -2027,7 +2028,7 @@ GenTree* Compiler::impLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedToken, // Need to use dictionary-based access which depends on the typeContext // which is only available at runtime, not at compile-time. - return impRuntimeLookupToTree(pResolvedToken, nullptr, pLookup, compileTimeHandle); + return impRuntimeLookupToTree(pResolvedToken, pConstrainedResolvedToken, pLookup, compileTimeHandle); } #ifdef FEATURE_READYTORUN @@ -2141,7 +2142,7 @@ GenTreeCall* Compiler::impReadyToRunHelperToTree(CORINFO_RESOLVED_TOKEN* pResolv } #endif -GenTree* Compiler::impMethodPointer(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_CALL_INFO* pCallInfo) +GenTree* Compiler::impMethodPointer(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_RESOLVED_TOKEN *pConstrainedResolvedToken, CORINFO_CALL_INFO* pCallInfo) { GenTree* op1 = nullptr; @@ -2159,7 +2160,11 @@ GenTree* Compiler::impMethodPointer(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORI break; case CORINFO_CALL_CODE_POINTER: - op1 = impLookupToTree(pResolvedToken, &pCallInfo->codePointerLookup, GTF_ICON_FTN_ADDR, pCallInfo->hMethod); + op1 = impLookupToTree(pResolvedToken, nullptr, &pCallInfo->codePointerLookup, GTF_ICON_FTN_ADDR, pCallInfo->hMethod); + break; + + case CORINFO_VIRTUALSTATICCALL_STUB: + op1 = impLookupToTree(pResolvedToken, pConstrainedResolvedToken, &pCallInfo->codePointerLookup, GTF_ICON_FTN_ADDR, pCallInfo->hMethod); break; default: @@ -4036,7 +4041,7 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, CORINFO_GENERICHANDLE_RESULT embedInfo; info.compCompHnd->expandRawHandleIntrinsic(&resolvedToken, &embedInfo); - GenTree* rawHandle = impLookupToTree(&resolvedToken, &embedInfo.lookup, gtTokenToIconFlags(memberRef), + GenTree* rawHandle = impLookupToTree(&resolvedToken, nullptr, &embedInfo.lookup, gtTokenToIconFlags(memberRef), embedInfo.compileTimeHandle); if (rawHandle == nullptr) { @@ -7385,7 +7390,7 @@ GenTree* Compiler::impImportLdvirtftn(GenTree* thisPtr, if ((pCallInfo->sig.sigInst.methInstCount != 0) && IsTargetAbi(CORINFO_NATIVEAOT_ABI)) { GenTree* runtimeMethodHandle = - impLookupToTree(pResolvedToken, &pCallInfo->codePointerLookup, GTF_ICON_METHOD_HDL, pCallInfo->hMethod); + impLookupToTree(pResolvedToken, nullptr, &pCallInfo->codePointerLookup, GTF_ICON_METHOD_HDL, pCallInfo->hMethod); return gtNewHelperCallNode(CORINFO_HELP_GVMLOOKUP_FOR_SLOT, TYP_I_IMPL, thisPtr, runtimeMethodHandle); } @@ -9695,7 +9700,7 @@ var_types Compiler::impImportCall(OPCODE opcode, assert((sig->callConv & CORINFO_CALLCONV_MASK) != CORINFO_CALLCONV_NATIVEVARARG); GenTree* fptr = - impLookupToTree(pResolvedToken, &callInfo->codePointerLookup, GTF_ICON_FTN_ADDR, callInfo->hMethod); + impLookupToTree(pResolvedToken, nullptr, &callInfo->codePointerLookup, GTF_ICON_FTN_ADDR, callInfo->hMethod); if (compDonotInline()) { @@ -15184,7 +15189,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) impHandleAccessAllowed(callInfo.accessAllowed, &callInfo.callsiteCalloutHelper); DO_LDFTN: - op1 = impMethodPointer(&resolvedToken, &callInfo); + op1 = impMethodPointer(&resolvedToken, &constrainedResolvedToken, &callInfo); if (compDonotInline()) { diff --git a/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il index c948d95fe29544..212187f05b638c 100644 --- a/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il +++ b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il @@ -609,7 +609,7 @@ .method public hidebysig static void Negative() cil managed { // Code size 225 (0xe1) - .maxstack 2 + .maxstack 10 .locals init ( class [mscorlib]System.Exception V_6, class [mscorlib]System.Exception V_7, @@ -632,7 +632,7 @@ string) IL_0029: nop IL_002a: nop - IL_002b: leave.s IL_004a + IL_002b: leave.s IL_004aa } // end .try catch [System.Runtime]System.Runtime.AmbiguousImplementationException @@ -647,11 +647,42 @@ IL_0041: call void [mscorlib]System.Console::WriteLine(string) IL_0046: nop IL_0047: nop - IL_0048: leave.s IL_004a + IL_0048: leave.s IL_004aa } // end handler + IL_004aa: ldstr "resolving delegate IFoo.Foo on FooClass - expecting exception." + call void [mscorlib]System.Console::WriteLine(string) + nop + .try + { + nop + ldc.i4.s 10 + constrained. class [svm_diamondshape]FooClass + ldftn int32 IFoo::Foo(int32) + pop + ldc.i4.0 + ldstr "Expecting exception on FooClass" + call void Test::Assert(bool, string) + nop + leave.s IL_004ab + + } // end .try + catch [System.Runtime]System.Runtime.AmbiguousImplementationException + { + stloc.s V_6 nop + ldstr "Exception caught: " + ldloc.s V_6 + callvirt instance string [mscorlib]System.Object::ToString() + call string [mscorlib]System.String::Concat(string, string) + call void [mscorlib]System.Console::WriteLine(string) + nop + leave.s IL_004ab + + } // end handler + + IL_004ab: nop ldstr "Calling IFoo.Foo on FooStruct - expecting exception." call void [mscorlib]System.Console::WriteLine(string) nop @@ -659,7 +690,7 @@ { nop ldc.i4.s 10 - constrained. class [svm_diamondshape]FooStruct + constrained. valuetype [svm_diamondshape]FooStruct call int32 IFoo::Foo(int32) pop ldc.i4.0 @@ -667,6 +698,40 @@ call void Test::Assert(bool, string) nop + leave.s IL_004ac + + } // end .try + catch [System.Runtime]System.Runtime.AmbiguousImplementationException + { + stloc.s V_6 + nop + ldstr "Exception caught: " + ldloc.s V_6 + callvirt instance string [mscorlib]System.Object::ToString() + call string [mscorlib]System.String::Concat(string, + string) + call void [mscorlib]System.Console::WriteLine(string) + nop + leave.s IL_004ac + + } // end handler + + IL_004ac: nop + ldstr "Resolving delegate IFoo.Foo on FooStruct - expecting exception." + call void [mscorlib]System.Console::WriteLine(string) + nop + .try + { + nop + ldc.i4.s 10 + constrained. valuetype [svm_diamondshape]FooStruct + ldftn int32 IFoo::Foo(int32) + pop + ldc.i4.0 + ldstr "Expecting exception on FooStruct" + call void Test::Assert(bool, + string) + nop leave.s IL_004a } // end .try @@ -720,7 +785,40 @@ } // end handler - IL_0093a: ldstr "Calling I1.Func on I47Struct - expecting exception" + IL_0093a: ldstr "Resolving delegate I1.Func on I47Struct - expecting exception" + call void [mscorlib]System.Console::WriteLine(string) + nop + .try + { + nop + ldc.i4.s 10 + constrained. valuetype [svm_diamondshape]I47Struct + ldftn int32 I1::Func(int32) + pop + ldc.i4.0 + ldstr "Expecting exception on I47Struct" + call void Test::Assert(bool, string) + nop + nop + leave.s IL_0093b + + } // end .try + catch [System.Runtime]System.Runtime.AmbiguousImplementationException + { + stloc.s V_7 + nop + ldstr "Exception caught: " + ldloc.s V_7 + callvirt instance string [mscorlib]System.Object::ToString() + call string [mscorlib]System.String::Concat(string, string) + call void [mscorlib]System.Console::WriteLine(string) + nop + nop + leave.s IL_0093b + + } // end handler + + IL_0093b: ldstr "Calling I1.Func on I47Struct - expecting exception" call void [mscorlib]System.Console::WriteLine(string) nop .try @@ -735,6 +833,39 @@ call void Test::Assert(bool, string) nop nop + leave.s IL_0093c + + } // end .try + catch [System.Runtime]System.Runtime.AmbiguousImplementationException + { + stloc.s V_7 + nop + ldstr "Exception caught: " + ldloc.s V_7 + callvirt instance string [mscorlib]System.Object::ToString() + call string [mscorlib]System.String::Concat(string, string) + call void [mscorlib]System.Console::WriteLine(string) + nop + nop + leave.s IL_0093c + + } // end handler + + IL_0093c: ldstr "Resolving delegate I1.Func on I47Struct - expecting exception" + call void [mscorlib]System.Console::WriteLine(string) + nop + .try + { + nop + ldc.i4.s 10 + constrained. valuetype [svm_diamondshape]I47Struct + ldftn int32 I1::Func(int32) + pop + ldc.i4.0 + ldstr "Expecting exception on I47Struct" + call void Test::Assert(bool, string) + nop + nop leave.s IL_0093 } // end .try @@ -785,7 +916,41 @@ } // end handler - IL_00e0a: ldstr "Calling GI1.Func on GI23Struct - expecting exception" + IL_00e0a: ldstr "Resolving delegate GI1.Func on GI23Struct - expecting exception" + call void [mscorlib]System.Console::WriteLine(string) + nop + .try + { + nop + ldloca.s V_8 + constrained. valuetype [svm_diamondshape]GI23Struct`1 + ldftn int32 class GI1`1::Func(class [mscorlib]System.Type[]&) + pop + ldc.i4.0 + ldstr "Expecting exception on GI23Class" + call void Test::Assert(bool, + string) + nop + leave.s IL_00e0b + + } // end .try + catch [System.Runtime]System.Runtime.AmbiguousImplementationException + { + stloc.s V_9 + nop + ldstr "Exception caught: " + ldloc.s V_9 + callvirt instance string [mscorlib]System.Object::ToString() + call string [mscorlib]System.String::Concat(string, + string) + call void [mscorlib]System.Console::WriteLine(string) + nop + nop + leave.s IL_00e0b + + } // end handler + + IL_00e0b: ldstr "Calling GI1.Func on GI23Struct - expecting exception" call void [mscorlib]System.Console::WriteLine(string) nop .try @@ -800,6 +965,40 @@ call void Test::Assert(bool, string) nop + leave.s IL_00e0c + + } // end .try + catch [System.Runtime]System.Runtime.AmbiguousImplementationException + { + stloc.s V_9 + nop + ldstr "Exception caught: " + ldloc.s V_9 + callvirt instance string [mscorlib]System.Object::ToString() + call string [mscorlib]System.String::Concat(string, + string) + call void [mscorlib]System.Console::WriteLine(string) + nop + nop + leave.s IL_00e0c + + } // end handler + + IL_00e0c: ldstr "Resolving delegate GI1.Func on GI23Struct - expecting exception" + call void [mscorlib]System.Console::WriteLine(string) + nop + .try + { + nop + ldloca.s V_8 + constrained. valuetype [svm_diamondshape]GI23Struct`1 + ldftn int32 class GI1`1::Func(class [mscorlib]System.Type[]&) + pop + ldc.i4.0 + ldstr "Expecting exception on GI23Class" + call void Test::Assert(bool, + string) + nop leave.s IL_00e0 } // end .try @@ -825,7 +1024,7 @@ .method public hidebysig static void Positive() cil managed { // Code size 189 (0xbd) - .maxstack 2 + .maxstack 10 .locals init (class [mscorlib]System.Type[] V_4) IL_0000: nop IL_0001: ldstr "Calling I1.Func on I4Class - expecting I4.Func" @@ -840,6 +1039,19 @@ IL_0025: call void Test::Assert(bool, string) IL_002a: nop + ldstr "Calling I1.Func on I4Class as a delegate - expecting I4.Func" + call void [mscorlib]System.Console::WriteLine(string) + nop + ldc.i4.s 10 + constrained. class I4Class + ldftn int32 I1::Func(int32) + calli int32(int32) + ldc.i4.s 14 + ceq + ldstr "Expecting I1.Func to land on I4.Func" + call void Test::Assert(bool, string) + nop + ldstr "Calling I1.Func on I4Struct - expecting I4.Func" call void [mscorlib]System.Console::WriteLine(string) nop @@ -852,6 +1064,19 @@ call void Test::Assert(bool, string) nop + ldstr "Calling I1.Func on I4Struct as a delegate - expecting I4.Func" + call void [mscorlib]System.Console::WriteLine(string) + nop + ldc.i4.s 10 + constrained. valuetype I4Struct + ldftn int32 I1::Func(int32) + calli int32(int32) + ldc.i4.s 14 + ceq + ldstr "Expecting I1.Func to land on I4.Func" + call void Test::Assert(bool, string) + nop + IL_002b: ldstr "Calling I1.Func on I8Class - expecting I8.Func" IL_0030: call void [mscorlib]System.Console::WriteLine(string) IL_0035: nop @@ -864,6 +1089,19 @@ IL_004f: call void Test::Assert(bool, string) IL_0054: nop + ldstr "Calling I1.Func on I8Class as a delegate - expecting I8.Func" + call void [mscorlib]System.Console::WriteLine(string) + nop + ldc.i4.s 10 + constrained. class [svm_diamondshape]I8Class + ldftn int32 I1::Func(int32) + calli int32(int32) + ldc.i4.s 18 + ceq + ldstr "Expecting I1.Func to land on I8.Func" + call void Test::Assert(bool, string) + nop + ldstr "Calling I1.Func on I8Struct - expecting I8.Func" call void [mscorlib]System.Console::WriteLine(string) nop @@ -876,6 +1114,19 @@ call void Test::Assert(bool, string) nop + ldstr "Calling I1.Func on I8Struct as a delegate - expecting I8.Func" + call void [mscorlib]System.Console::WriteLine(string) + nop + ldc.i4.s 10 + constrained. valuetype [svm_diamondshape]I8Struct + ldftn int32 I1::Func(int32) + calli int32(int32) + ldc.i4.s 18 + ceq + ldstr "Expecting I1.Func to land on I8.Func" + call void Test::Assert(bool, string) + nop + IL_0055: ldstr "Calling GI1.Func on GI4Class - expecting GI4.Func" IL_005a: call void [mscorlib]System.Console::WriteLine(string) IL_005f: nop @@ -907,6 +1158,38 @@ call void Test::Assert(bool, string) nop + ldstr "Calling GI1.Func on GI4Class as a delegate - expecting GI4.Func" + call void [mscorlib]System.Console::WriteLine(string) + nop + ldloca.s V_4 + constrained. class [svm_diamondshape]GI4Class`1 + ldftn int32 class GI1`1::Func(class [mscorlib]System.Type[]&) + calli int32(class [mscorlib]System.Type[]&) + ldc.i4.4 + ceq + ldstr "Expecting GI1.Func to land on GII4.Func" + call void Test::Assert(bool, string) + nop + + ldloc.s V_4 + ldc.i4.0 + ldelem.ref + ldtoken [mscorlib]System.Object + call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + call bool [mscorlib]System.Type::op_Equality(class [mscorlib]System.Type, class [mscorlib]System.Type) + ldstr "T must be object" + call void Test::Assert(bool, string) + nop + ldloc.s V_4 + ldc.i4.1 + ldelem.ref + ldtoken [mscorlib]System.String + call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + call bool [mscorlib]System.Type::op_Equality(class [mscorlib]System.Type, class [mscorlib]System.Type) + ldstr "S must be string" + call void Test::Assert(bool, string) + nop + ldstr "Calling GI1.Func on GI4Struct - expecting GI4.Func" call void [mscorlib]System.Console::WriteLine(string) nop @@ -937,6 +1220,39 @@ IL_00b1: ldstr "S must be string" IL_00b6: call void Test::Assert(bool, string) IL_00bb: nop + + ldstr "Calling GI1.Func on GI4Struct as a delegate - expecting GI4.Func" + call void [mscorlib]System.Console::WriteLine(string) + nop + ldloca.s V_4 + constrained. valuetype [svm_diamondshape]GI4Struct`1 + ldftn int32 class GI1`1::Func(class [mscorlib]System.Type[]&) + calli int32(class [mscorlib]System.Type[]&) + ldc.i4.4 + ceq + ldstr "Expecting GI1.Func to land on GII4.Func" + call void Test::Assert(bool, string) + nop + + ldloc.s V_4 + ldc.i4.0 + ldelem.ref + ldtoken [mscorlib]System.Object + call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + call bool [mscorlib]System.Type::op_Equality(class [mscorlib]System.Type, class [mscorlib]System.Type) + ldstr "T must be object" + call void Test::Assert(bool, string) + nop + ldloc.s V_4 + ldc.i4.1 + ldelem.ref + ldtoken [mscorlib]System.String + call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + call bool [mscorlib]System.Type::op_Equality(class [mscorlib]System.Type, class [mscorlib]System.Type) + ldstr "S must be string" + call void Test::Assert(bool, string) + nop + IL_00bc: ret } // end of method Program::Positive From 3f0622eb02e5bd7a51d3bd3c60e0f58f48ba6391 Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 11 May 2022 13:18:55 +0200 Subject: [PATCH 11/22] Remove 'final' check; add reabstraction test cases --- src/coreclr/vm/methodtablebuilder.cpp | 4 +- .../DiamondShape/svm_diamondshape.il | 128 ++++++++++++++++-- 2 files changed, 117 insertions(+), 15 deletions(-) diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index 5d65609b8e4ca6..5f9d0e0582aba2 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -4829,8 +4829,8 @@ VOID MethodTableBuilder::TestMethodImpl( BuildMethodTableThrowException(IDS_CLASSLOAD_MI_FINAL_DECL); } - // Interface method body that has methodimpl should always be final - if (IsInterface() && !IsMdFinal(dwImplAttrs)) + // Non-static interface method body that has methodimpl should always be final + if (IsInterface() && !IsMdStatic(dwDeclAttrs) && !IsMdFinal(dwImplAttrs)) { BuildMethodTableThrowException(IDS_CLASSLOAD_MI_FINAL_IMPL); } diff --git a/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il index 212187f05b638c..9679a7333f0e6f 100644 --- a/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il +++ b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il @@ -61,7 +61,7 @@ .class interface private abstract auto ansi IFoo2 implements IFoo { - .method public hidebysig final + .method public hidebysig static int32 Foo(int32 a) cil managed { .override method int32 IFoo::Foo(int32) @@ -87,7 +87,7 @@ .class interface private abstract auto ansi IFooEx implements IFoo { - .method public hidebysig final + .method public hidebysig static int32 IFoo.Foo(int32 a) cil managed { .override method int32 IFoo::Foo(int32) @@ -109,6 +109,13 @@ } // end of method IFooEx::IFoo.Foo } // end of class IFooEx +.class interface private abstract auto ansi IFooExReabstract + implements IFooEx +{ + .method public hidebysig abstract final + static int32 IFoo.Foo(int32 a) cil managed; +} + .class private auto ansi beforefieldinit FooClass extends [mscorlib]System.Object implements IFoo2, @@ -117,6 +124,14 @@ { } // end of class FooClass +.class private auto ansi beforefieldinit FooClassReabstract + extends [mscorlib]System.Object + implements IFoo2, + IFoo, + IFooExReabstract +{ +} // end of class FooClass + .class private auto ansi sealed beforefieldinit FooStruct extends [mscorlib]System.ValueType implements IFoo2, @@ -137,7 +152,7 @@ .class interface private abstract auto ansi I2 implements I1 { - .method private hidebysig final + .method private hidebysig static int32 I1.Func(int32 a) cil managed { .override method int32 I1::Func(int32) @@ -162,7 +177,7 @@ .class interface private abstract auto ansi I3 implements I1 { - .method private hidebysig final + .method private hidebysig static int32 I1.Func(int32 a) cil managed { .override method int32 I1::Func(int32) @@ -189,7 +204,7 @@ I1, I3 { - .method private hidebysig final + .method private hidebysig static int32 I1.Func(int32 a) cil managed { .override method int32 I1::Func(int32) @@ -211,6 +226,38 @@ } // end of method I4::I1.Func } // end of class I4 +.class interface private abstract auto ansi I4Reabstract + implements I4 +{ + .method private hidebysig final abstract + static int32 I1.Func(int32 a) cil managed; +} // end of class I4Reabstract + +.class interface private abstract auto ansi I4Reimplement + implements I4Reabstract +{ + .method private hidebysig + static int32 I1.Func(int32 a) cil managed + { + .override method int32 I1::Func(int32) + // Code size 20 (0x14) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldstr "At I4Reimplement.Func" + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: ldarg.0 + IL_000d: ldc.i4.3 + IL_000e: add + IL_000f: stloc.0 + IL_0010: br.s IL_0012 + + IL_0012: ldloc.0 + IL_0013: ret + } // end of method I4Reimplement::I1.Func +} // end of class I4Reimplement + .class private auto ansi beforefieldinit I4Class extends [mscorlib]System.Object implements I4, @@ -220,6 +267,15 @@ { } // end of class I4Class +.class private auto ansi beforefieldinit I4ReimplementClass + extends [mscorlib]System.Object + implements I4Reimplement, + I2, + I1, + I3 +{ +} // end of class I4ReimplementClass + .class private auto ansi sealed beforefieldinit I4Struct extends [mscorlib]System.ValueType implements I4, @@ -232,7 +288,7 @@ .class interface private abstract auto ansi I5 implements I1 { - .method private hidebysig final + .method private hidebysig static int32 I1.Func(int32 a) cil managed { .override method int32 I1::Func(int32) @@ -257,7 +313,7 @@ .class interface private abstract auto ansi I6 implements I1 { - .method private hidebysig final + .method private hidebysig static int32 I1.Func(int32 a) cil managed { .override method int32 I1::Func(int32) @@ -284,7 +340,7 @@ I1, I6 { - .method private hidebysig final + .method private hidebysig static int32 I1.Func(int32 a) cil managed { .override method int32 I1::Func(int32) @@ -315,7 +371,7 @@ I5, I6 { - .method private hidebysig final + .method private hidebysig static int32 I1.Func(int32 a) cil managed { .override method int32 I1::Func(int32) @@ -399,7 +455,7 @@ .class interface private abstract auto ansi GI2`1 implements class GI1`1 { -.method private hidebysig final +.method private hidebysig static int32 'GI1.Func'([out] class [mscorlib]System.Type[]& types) cil managed { .override method int32 class GI1`1::Func<[1]>(class [mscorlib]System.Type[]&) @@ -456,7 +512,7 @@ .class interface private abstract auto ansi GI3`1 implements class GI1`1 { -.method private hidebysig final +.method private hidebysig static int32 'GI1.Func'([out] class [mscorlib]System.Type[]& types) cil managed { .override method int32 class GI1`1::Func<[1]>(class [mscorlib]System.Type[]&) @@ -515,7 +571,7 @@ class GI1`1, class GI3`1 { - .method private hidebysig final + .method private hidebysig static int32 'GI1.Func'([out] class [mscorlib]System.Type[]& types) cil managed { .override method int32 class GI1`1::Func<[1]>(class [mscorlib]System.Type[]&) @@ -665,7 +721,7 @@ ldstr "Expecting exception on FooClass" call void Test::Assert(bool, string) nop - leave.s IL_004ab + leave.s IL_004aa } // end .try catch [System.Runtime]System.Runtime.AmbiguousImplementationException @@ -678,6 +734,40 @@ call string [mscorlib]System.String::Concat(string, string) call void [mscorlib]System.Console::WriteLine(string) nop + leave.s IL_004aa + + } // end handler + + IL_004aa: nop + ldstr "Calling IFoo.Foo on FooClassReabstract - expecting exception." + call void [mscorlib]System.Console::WriteLine(string) + nop + .try + { + nop + ldc.i4.s 10 + constrained. valuetype [svm_diamondshape]FooStruct + call int32 IFoo::Foo(int32) + pop + ldc.i4.0 + ldstr "Expecting exception on FooStruct" + call void Test::Assert(bool, + string) + nop + leave.s IL_004ab + + } // end .try + catch [System.Runtime]System.Runtime.AmbiguousImplementationException + { + stloc.s V_6 + nop + ldstr "Exception caught: " + ldloc.s V_6 + callvirt instance string [mscorlib]System.Object::ToString() + call string [mscorlib]System.String::Concat(string, + string) + call void [mscorlib]System.Console::WriteLine(string) + nop leave.s IL_004ab } // end handler @@ -1039,6 +1129,18 @@ IL_0025: call void Test::Assert(bool, string) IL_002a: nop + ldstr "Calling I1.Func on I4ReimplementClass - expecting I4Reimplement.Func" + call void [mscorlib]System.Console::WriteLine(string) + nop + ldc.i4.s 10 + constrained. class I4ReimplementClass + call int32 I1::Func(int32) + ldc.i4.s 14 + ceq + ldstr "Expecting I1.Func to land on I4Reimplement.Func" + call void Test::Assert(bool, string) + nop + ldstr "Calling I1.Func on I4Class as a delegate - expecting I4.Func" call void [mscorlib]System.Console::WriteLine(string) nop From 32c3fa2d97d46e8a421315280aa7e3b4476c7e9a Mon Sep 17 00:00:00 2001 From: Tomas Rylek Date: Thu, 12 May 2022 20:34:05 +0200 Subject: [PATCH 12/22] Fix a few syntax errors in the IL --- .../DiamondShape/svm_diamondshape.il | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il index 9679a7333f0e6f..3d0894beb3bb3a 100644 --- a/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il +++ b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il @@ -113,7 +113,10 @@ implements IFooEx { .method public hidebysig abstract final - static int32 IFoo.Foo(int32 a) cil managed; + static int32 IFoo.Foo(int32 a) cil managed + { + } // end of method IFooExReabstract::Func + } .class private auto ansi beforefieldinit FooClass @@ -229,8 +232,11 @@ .class interface private abstract auto ansi I4Reabstract implements I4 { - .method private hidebysig final abstract - static int32 I1.Func(int32 a) cil managed; + .method private hidebysig final abstract virtual + static int32 Func(int32 a) cil managed + { + .override method int32 I1::Func(int32) + } // end of I4Reabstract::Func } // end of class I4Reabstract .class interface private abstract auto ansi I4Reimplement @@ -721,7 +727,7 @@ ldstr "Expecting exception on FooClass" call void Test::Assert(bool, string) nop - leave.s IL_004aa + leave.s IL_004ab } // end .try catch [System.Runtime]System.Runtime.AmbiguousImplementationException @@ -734,11 +740,11 @@ call string [mscorlib]System.String::Concat(string, string) call void [mscorlib]System.Console::WriteLine(string) nop - leave.s IL_004aa + leave.s IL_004ab } // end handler - IL_004aa: nop + IL_004ab: nop ldstr "Calling IFoo.Foo on FooClassReabstract - expecting exception." call void [mscorlib]System.Console::WriteLine(string) nop @@ -754,7 +760,7 @@ call void Test::Assert(bool, string) nop - leave.s IL_004ab + leave.s IL_004ac } // end .try catch [System.Runtime]System.Runtime.AmbiguousImplementationException @@ -768,11 +774,11 @@ string) call void [mscorlib]System.Console::WriteLine(string) nop - leave.s IL_004ab + leave.s IL_004ac } // end handler - IL_004ab: nop + IL_004ac: nop ldstr "Calling IFoo.Foo on FooStruct - expecting exception." call void [mscorlib]System.Console::WriteLine(string) nop @@ -788,7 +794,7 @@ call void Test::Assert(bool, string) nop - leave.s IL_004ac + leave.s IL_004ad } // end .try catch [System.Runtime]System.Runtime.AmbiguousImplementationException @@ -802,11 +808,11 @@ string) call void [mscorlib]System.Console::WriteLine(string) nop - leave.s IL_004ac + leave.s IL_004ad } // end handler - IL_004ac: nop + IL_004ad: nop ldstr "Resolving delegate IFoo.Foo on FooStruct - expecting exception." call void [mscorlib]System.Console::WriteLine(string) nop From a36136bec6d979d029d10377207f9640b827f7e6 Mon Sep 17 00:00:00 2001 From: Tomas Date: Mon, 16 May 2022 15:27:09 +0200 Subject: [PATCH 13/22] Relax flag checking for reabstracted SVMs; runtime resolution test --- src/coreclr/vm/methodtablebuilder.cpp | 2 +- .../DiamondShape/svm_diamondshape.il | 75 ++++++++++++++++++- 2 files changed, 73 insertions(+), 4 deletions(-) diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index 5f9d0e0582aba2..8a6ba5429575fb 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -2935,7 +2935,7 @@ MethodTableBuilder::EnumerateClassMethods() { BuildMethodTableThrowException(BFA_AB_METHOD_IN_AB_CLASS); } - if(!IsMdVirtual(dwMemberAttrs)) + if(!IsMdVirtual(dwMemberAttrs) && !IsMdStatic(dwMemberAttrs)) { BuildMethodTableThrowException(BFA_NONVIRT_AB_METHOD); } diff --git a/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il index 3d0894beb3bb3a..f2d421b8aea5f4 100644 --- a/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il +++ b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il @@ -232,7 +232,7 @@ .class interface private abstract auto ansi I4Reabstract implements I4 { - .method private hidebysig final abstract virtual + .method private hidebysig abstract static int32 Func(int32 a) cil managed { .override method int32 I1::Func(int32) @@ -254,7 +254,7 @@ IL_0006: call void [mscorlib]System.Console::WriteLine(string) IL_000b: nop IL_000c: ldarg.0 - IL_000d: ldc.i4.3 + IL_000d: ldc.i4.7 IL_000e: add IL_000f: stloc.0 IL_0010: br.s IL_0012 @@ -665,6 +665,55 @@ { } // end of class GI4Struct`1 +.class interface private abstract auto ansi IResolutionAtRuntime`1 +{ + .method public hidebysig newslot abstract virtual static int32 Func(int32 arg) cil managed + { + } // end of method IResolutionAtRuntime`1::Func + +} // end of class GI1`1 + +.class private auto ansi beforefieldinit ResolutionAtRuntime + implements class IResolutionAtRuntime`1, + class IResolutionAtRuntime`1 +{ + .method private hidebysig static int32 FuncObject(int32 arg) cil managed + { + .override method int32 class IResolutionAtRuntime`1::Func(int32) + ldstr "At ResolutionAtRuntime.FuncObject" + call void [mscorlib]System.Console::WriteLine(string) + nop + ldarg.0 + ldc.i4.s 19 + add + ret + } + + .method private hidebysig static int32 FuncString(int32 arg) cil managed + { + .override method int32 class IResolutionAtRuntime`1::Func(int32) + ldstr "At ResolutionAtRuntime.FuncString" + call void [mscorlib]System.Console::WriteLine(string) + nop + ldarg.0 + ldc.i4.s 23 + add + ret + } + + .method public hidebysig static int32 RuntimeResolvedFunc`2<(class IResolutionAtRuntime`1) T, V>(int32 arg) + { + ldstr "At RuntimeResolvedFunc" + call void [mscorlib]System.Console::WriteLine(string) + nop + ldarg.0 + constrained. !!T + call int32 class [svm_diamondshape]IResolutionAtRuntime`1::Func(int32) + ret + } + +} // end of class ResolutionAtRuntime + .class private auto ansi beforefieldinit Program extends [mscorlib]System.Object { @@ -1141,7 +1190,7 @@ ldc.i4.s 10 constrained. class I4ReimplementClass call int32 I1::Func(int32) - ldc.i4.s 14 + ldc.i4.s 17 ceq ldstr "Expecting I1.Func to land on I4Reimplement.Func" call void Test::Assert(bool, string) @@ -1361,6 +1410,26 @@ call void Test::Assert(bool, string) nop + ldstr "Calling ResolutionAtRuntime::RuntimeResolvedFunc - expecting ResolutionAtRuntime.FuncObject" + call void [mscorlib]System.Console::WriteLine(string) + nop + ldc.i4.s 100 + call int32 class [svm_diamondshape]ResolutionAtRuntime::RuntimeResolvedFunc`2(int32) + ldc.i4.s 119 + ceq + ldstr "Expecting ResolutionAtRuntime::RuntimeResolvedFunc to land on RuntimeResolvedFunc.FuncObject" + call void Test::Assert(bool, string) + + ldstr "Calling ResolutionAtRuntime::RuntimeResolvedFunc - expecting ResolutionAtRuntime.FuncString" + call void [mscorlib]System.Console::WriteLine(string) + nop + ldc.i4.s 100 + call int32 class [svm_diamondshape]ResolutionAtRuntime::RuntimeResolvedFunc`2(int32) + ldc.i4.s 123 + ceq + ldstr "Expecting ResolutionAtRuntime::RuntimeResolvedFunc to land on RuntimeResolvedFunc.FuncString" + call void Test::Assert(bool, string) + IL_00bc: ret } // end of method Program::Positive From 2bad14608ea5bd245e96f600eb4524cf043b2b53 Mon Sep 17 00:00:00 2001 From: Tomas Date: Tue, 24 May 2022 10:26:59 +0200 Subject: [PATCH 14/22] More changes based on David Wrighton's feedback --- src/coreclr/jit/compiler.h | 3 +- src/coreclr/jit/importer.cpp | 47 ++++++++++--------- src/coreclr/jit/morph.cpp | 3 +- src/coreclr/vm/jithelpers.cpp | 2 +- src/coreclr/vm/jitinterface.cpp | 27 +++++++---- .../DiamondShape/svm_diamondshape.il | 6 ++- 6 files changed, 50 insertions(+), 38 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index f59934ca0f2555..68b70ca41e28f3 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -3756,7 +3756,7 @@ class Compiler GenTree* impKeepAliveIntrinsic(GenTree* objToKeepAlive); - GenTree* impMethodPointer(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken, CORINFO_CALL_INFO* pCallInfo); + GenTree* impMethodPointer(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_CALL_INFO* pCallInfo); GenTree* impTransformThis(GenTree* thisPtr, CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken, @@ -3840,7 +3840,6 @@ class Compiler } GenTree* impLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedToken, - CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken, CORINFO_LOOKUP* pLookup, GenTreeFlags flags, void* compileTimeHandle); diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index fee841fd88bab3..4eca9b1c384212 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -1957,7 +1957,7 @@ GenTree* Compiler::impTokenToHandle(CORINFO_RESOLVED_TOKEN* pResolvedToken, } // Generate the full lookup tree. May be null if we're abandoning an inline attempt. - GenTree* result = impLookupToTree(pResolvedToken, nullptr, &embedInfo.lookup, gtTokenToIconFlags(pResolvedToken->token), + GenTree* result = impLookupToTree(pResolvedToken, &embedInfo.lookup, gtTokenToIconFlags(pResolvedToken->token), embedInfo.compileTimeHandle); // If we have a result and it requires runtime lookup, wrap it in a runtime lookup node. @@ -1970,7 +1970,6 @@ GenTree* Compiler::impTokenToHandle(CORINFO_RESOLVED_TOKEN* pResolvedToken, } GenTree* Compiler::impLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedToken, - CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken, CORINFO_LOOKUP* pLookup, GenTreeFlags handleFlags, void* compileTimeHandle) @@ -2028,7 +2027,7 @@ GenTree* Compiler::impLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedToken, // Need to use dictionary-based access which depends on the typeContext // which is only available at runtime, not at compile-time. - return impRuntimeLookupToTree(pResolvedToken, pConstrainedResolvedToken, pLookup, compileTimeHandle); + return impRuntimeLookupToTree(pResolvedToken, nullptr, pLookup, compileTimeHandle); } #ifdef FEATURE_READYTORUN @@ -2142,7 +2141,7 @@ GenTreeCall* Compiler::impReadyToRunHelperToTree(CORINFO_RESOLVED_TOKEN* pResolv } #endif -GenTree* Compiler::impMethodPointer(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_RESOLVED_TOKEN *pConstrainedResolvedToken, CORINFO_CALL_INFO* pCallInfo) +GenTree* Compiler::impMethodPointer(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_CALL_INFO* pCallInfo) { GenTree* op1 = nullptr; @@ -2160,11 +2159,7 @@ GenTree* Compiler::impMethodPointer(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORI break; case CORINFO_CALL_CODE_POINTER: - op1 = impLookupToTree(pResolvedToken, nullptr, &pCallInfo->codePointerLookup, GTF_ICON_FTN_ADDR, pCallInfo->hMethod); - break; - - case CORINFO_VIRTUALSTATICCALL_STUB: - op1 = impLookupToTree(pResolvedToken, pConstrainedResolvedToken, &pCallInfo->codePointerLookup, GTF_ICON_FTN_ADDR, pCallInfo->hMethod); + op1 = impLookupToTree(pResolvedToken, &pCallInfo->codePointerLookup, GTF_ICON_FTN_ADDR, pCallInfo->hMethod); break; default: @@ -2216,6 +2211,7 @@ GenTree* Compiler::getRuntimeContextTree(CORINFO_RUNTIME_LOOKUP_KIND kind) else { assert(kind == CORINFO_LOOKUP_VIRTUALSTATIC); + ctxTree = gtNewNull(); } return ctxTree; } @@ -2243,17 +2239,7 @@ GenTree* Compiler::impRuntimeLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedToken CORINFO_LOOKUP* pLookup, void* compileTimeHandle) { - GenTree* ctxTree; - - if (pLookup->lookupKind.runtimeLookupKind == CORINFO_LOOKUP_VIRTUALSTATIC) - { - ctxTree = gtNewIconEmbClsHndNode(pConstrainedResolvedToken->hClass); - } - else - { - ctxTree = getRuntimeContextTree(pLookup->lookupKind.runtimeLookupKind); - } - + GenTree* ctxTree = getRuntimeContextTree(pLookup->lookupKind.runtimeLookupKind); CORINFO_RUNTIME_LOOKUP* pRuntimeLookup = &pLookup->runtimeLookup; // It's available only via the run-time helper function if (pRuntimeLookup->indirections == CORINFO_USEHELPER) @@ -2265,7 +2251,24 @@ GenTree* Compiler::impRuntimeLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedToken &pLookup->lookupKind, ctxTree); } #endif - return gtNewRuntimeLookupHelperCallNode(pRuntimeLookup, ctxTree, compileTimeHandle); + GenTree* argNode; + if (pConstrainedResolvedToken != nullptr) + { + // SVM call: use generic lookup to resolve the exact type and method + argNode = gtNewHelperCallNode( + CORINFO_HELP_RUNTIMEHANDLE_CLASS, + TYP_I_IMPL, + gtNewIconEmbHndNode(pConstrainedResolvedToken->hClass, nullptr, GTF_ICON_GLOBAL_PTR, pConstrainedResolvedToken->hClass)); + ctxTree = gtNewHelperCallNode( + CORINFO_HELP_RUNTIMEHANDLE_METHOD, + TYP_I_IMPL, + gtNewIconEmbHndNode(pResolvedToken->hMethod, nullptr, GTF_ICON_GLOBAL_PTR, pResolvedToken->hMethod)); + } + else + { + argNode = gtNewIconEmbHndNode(pRuntimeLookup->signature, nullptr, GTF_ICON_GLOBAL_PTR, compileTimeHandle); + } + return gtNewRuntimeLookupHelperCallNode(pRuntimeLookup, ctxTree, argNode); } // Slot pointer @@ -9985,7 +9988,7 @@ var_types Compiler::impImportCall(OPCODE opcode, if (sig->callConv & CORINFO_CALLCONV_PARAMTYPE) { - assert(call->AsCall()->gtCallType == CT_USER_FUNC); + assert(call->AsCall()->gtCallType == CT_USER_FUNC || call->AsCall()->gtCallType == CT_INDIRECT); if (clsHnd == nullptr) { NO_WAY("CALLI on parameterized type"); diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 536407ba9a3ea2..f27b963126fa46 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -7532,9 +7532,10 @@ GenTree* Compiler::getRuntimeLookupTree(CORINFO_RESOLVED_TOKEN* pResolvedToken, // The long-term solution is to introduce a new node representing a runtime lookup, create instances // of that node both in the importer and here, and expand the node in lower (introducing control flow if // necessary). + GenTree* argNode = gtNewIconEmbHndNode(pRuntimeLookup->signature, nullptr, GTF_ICON_GLOBAL_PTR, compileTimeHandle); return gtNewRuntimeLookupHelperCallNode(pRuntimeLookup, getRuntimeContextTree(pLookup->lookupKind.runtimeLookupKind), - compileTimeHandle); + argNode); } GenTree* result = getRuntimeContextTree(pLookup->lookupKind.runtimeLookupKind); diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index a92bf7e620506b..6da6b00c918fd6 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -3375,7 +3375,7 @@ NOINLINE HCIMPL3(CORINFO_MethodPtr, JIT_VirtualFunctionPointer_Framed, Object * } HCIMPLEND -HCIMPL2(void*, JIT_DispatchVirtualStatic, MethodTable *constrainedType, MethodDesc *interfaceMethod) +HCIMPL2(void*, JIT_DispatchVirtualStatic, MethodDesc *interfaceMethod, MethodTable *constrainedType) { FCALL_CONTRACT; diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 4134a08203bd0b..2a65ceb0550809 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -2920,23 +2920,23 @@ void CEEInfo::ComputeRuntimeLookupForSharedGenericToken(DictionaryEntryKind entr return; } + MethodDesc* pTargetMD = (MethodDesc*)pResolvedToken->hMethod; MethodDesc* pContextMD = GetMethodFromContext(pResolvedToken->tokenContext); MethodTable* pContextMT = pContextMD->GetMethodTable(); + bool targetIsStaticVirtual = (pTargetMD != nullptr && pTargetMD->IsStatic() && pConstrainedResolvedToken != nullptr); - // There is a pathological case where invalid IL refereces __Canon type directly, but there is no dictionary availabled to store the lookup. - if (!pContextMD->IsSharedByGenericInstantiations()) + if (targetIsStaticVirtual) { - if (pTemplateMD->IsStatic() && pConstrainedResolvedToken != nullptr) + EncodeStaticVirtualRuntimeLookup(pResultLookup, pResolvedToken->hMethod); + if (!pContextMD->IsSharedByGenericInstantiations()) { - pResultLookup->lookupKind.runtimeLookupKind = CORINFO_LOOKUP_VIRTUALSTATIC; - pResult->signature = (void*)pResolvedToken->hMethod; - pResult->helper = CORINFO_HELP_VIRTUAL_STATIC; - pResult->indirections = CORINFO_USEHELPER; - pResult->testForNull = 0; - pResult->testForFixup = 0; return; } - + } + + // There is a pathological case where invalid IL refereces __Canon type directly, but there is no dictionary availabled to store the lookup. + if (!pContextMD->IsSharedByGenericInstantiations()) + { COMPlusThrow(kInvalidProgramException); } @@ -2954,6 +2954,13 @@ void CEEInfo::ComputeRuntimeLookupForSharedGenericToken(DictionaryEntryKind entr pResultLookup->lookupKind.runtimeLookupKind = CORINFO_LOOKUP_THISOBJ; } + if (targetIsStaticVirtual) + { + // For SVM lookup, we just need to fill in the generic context lookup kind, the runtime helper + // will take care of resolving the actual target method. + return; + } + // If we've got a method type parameter of any kind then we must look in the method desc arg if (pContextMD->RequiresInstMethodDescArg()) { diff --git a/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il index f2d421b8aea5f4..7454f8164807c5 100644 --- a/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il +++ b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il @@ -1171,6 +1171,7 @@ // Code size 189 (0xbd) .maxstack 10 .locals init (class [mscorlib]System.Type[] V_4) +/* IL_0000: nop IL_0001: ldstr "Calling I1.Func on I4Class - expecting I4.Func" IL_0006: call void [mscorlib]System.Console::WriteLine(string) @@ -1409,6 +1410,7 @@ ldstr "S must be string" call void Test::Assert(bool, string) nop +*/ ldstr "Calling ResolutionAtRuntime::RuntimeResolvedFunc - expecting ResolutionAtRuntime.FuncObject" call void [mscorlib]System.Console::WriteLine(string) @@ -1443,8 +1445,8 @@ .maxstack 1 .locals init (int32 V_0) IL_0000: nop - IL_0001: call void Program::Negative() - IL_0006: nop +// IL_0001: call void Program::Negative() +// IL_0006: nop IL_0007: call void Program::Positive() IL_000c: nop IL_000d: call int32 Test::Ret() From 01b38a2f064f1ab835fc0f9a9427bbfb3d673241 Mon Sep 17 00:00:00 2001 From: Tomas Rylek Date: Thu, 26 May 2022 12:16:44 +0200 Subject: [PATCH 15/22] Additional changes for shared generic support --- src/coreclr/inc/corinfo.h | 5 +- src/coreclr/inc/jithelpers.h | 2 +- src/coreclr/jit/compiler.h | 1 - src/coreclr/jit/importer.cpp | 104 ++++++---------- src/coreclr/jit/morph.cpp | 3 +- .../tools/Common/JitInterface/CorInfoTypes.cs | 1 - src/coreclr/vm/jithelpers.cpp | 39 +++--- src/coreclr/vm/jitinterface.cpp | 63 +++++----- .../DiamondShape/svm_diamondshape.il | 116 +++++++++++++++--- 9 files changed, 191 insertions(+), 143 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 752c5da2822e9a..9c4d2b9f6ec491 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -646,7 +646,7 @@ enum CorInfoHelpFunc CORINFO_HELP_VALIDATE_INDIRECT_CALL, // CFG: Validate function pointer CORINFO_HELP_DISPATCH_INDIRECT_CALL, // CFG: Validate and dispatch to pointer - CORINFO_HELP_VIRTUAL_STATIC, // Resolve virtual static method address based on method handle and type constraint + CORINFO_HELP_STATIC_VIRTUAL_AMBIGUOUS_RESOLUTION, // Throw AmbiguousResolutionException for failed static virtual method resolution CORINFO_HELP_COUNT, }; @@ -1210,7 +1210,6 @@ enum CORINFO_RUNTIME_LOOKUP_KIND CORINFO_LOOKUP_THISOBJ, CORINFO_LOOKUP_METHODPARAM, CORINFO_LOOKUP_CLASSPARAM, - CORINFO_LOOKUP_VIRTUALSTATIC, CORINFO_LOOKUP_NOT_SUPPORTED, // Returned for attempts to inline dictionary lookups }; @@ -1463,7 +1462,7 @@ enum CORINFO_CALL_KIND CORINFO_VIRTUALCALL_STUB, CORINFO_VIRTUALCALL_LDVIRTFTN, CORINFO_VIRTUALCALL_VTABLE, - CORINFO_VIRTUALSTATICCALL_STUB, + CORINFO_VIRTUALSTATICCALL, }; // Indicates that the CORINFO_VIRTUALCALL_VTABLE lookup needn't do a chunk indirection diff --git a/src/coreclr/inc/jithelpers.h b/src/coreclr/inc/jithelpers.h index b97b4841f49d44..54bef5a7bdbb97 100644 --- a/src/coreclr/inc/jithelpers.h +++ b/src/coreclr/inc/jithelpers.h @@ -339,7 +339,7 @@ JITHELPER(CORINFO_HELP_DISPATCH_INDIRECT_CALL, NULL, CORINFO_HELP_SIG_REG_ONLY) #endif - JITHELPER(CORINFO_HELP_VIRTUAL_STATIC, JIT_DispatchVirtualStatic, CORINFO_HELP_SIG_REG_ONLY) + JITHELPER(CORINFO_HELP_STATIC_VIRTUAL_AMBIGUOUS_RESOLUTION, JIT_StaticVirtualAmbiguousResolution, CORINFO_HELP_SIG_REG_ONLY) #undef JITHELPER #undef DYNAMICJITHELPER diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 68b70ca41e28f3..21ec74fef1bcc7 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -3847,7 +3847,6 @@ class Compiler GenTree* getRuntimeContextTree(CORINFO_RUNTIME_LOOKUP_KIND kind); GenTree* impRuntimeLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedToken, - CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken, CORINFO_LOOKUP* pLookup, void* compileTimeHandle); diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 4eca9b1c384212..989b68f19b70c5 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -2027,7 +2027,7 @@ GenTree* Compiler::impLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedToken, // Need to use dictionary-based access which depends on the typeContext // which is only available at runtime, not at compile-time. - return impRuntimeLookupToTree(pResolvedToken, nullptr, pLookup, compileTimeHandle); + return impRuntimeLookupToTree(pResolvedToken, pLookup, compileTimeHandle); } #ifdef FEATURE_READYTORUN @@ -2202,17 +2202,14 @@ GenTree* Compiler::getRuntimeContextTree(CORINFO_RUNTIME_LOOKUP_KIND kind) // context is the method table pointer of the this object ctxTree = gtNewMethodTableLookup(ctxTree); } - else if (kind == CORINFO_LOOKUP_METHODPARAM || kind == CORINFO_LOOKUP_CLASSPARAM) + else { + assert(kind == CORINFO_LOOKUP_METHODPARAM || kind == CORINFO_LOOKUP_CLASSPARAM); + // Exact method descriptor as passed in ctxTree = gtNewLclvNode(pRoot->info.compTypeCtxtArg, TYP_I_IMPL); ctxTree->gtFlags |= GTF_VAR_CONTEXT; } - else - { - assert(kind == CORINFO_LOOKUP_VIRTUALSTATIC); - ctxTree = gtNewNull(); - } return ctxTree; } @@ -2235,11 +2232,11 @@ GenTree* Compiler::getRuntimeContextTree(CORINFO_RUNTIME_LOOKUP_KIND kind) */ GenTree* Compiler::impRuntimeLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedToken, - CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken, CORINFO_LOOKUP* pLookup, void* compileTimeHandle) { GenTree* ctxTree = getRuntimeContextTree(pLookup->lookupKind.runtimeLookupKind); + CORINFO_RUNTIME_LOOKUP* pRuntimeLookup = &pLookup->runtimeLookup; // It's available only via the run-time helper function if (pRuntimeLookup->indirections == CORINFO_USEHELPER) @@ -2251,24 +2248,7 @@ GenTree* Compiler::impRuntimeLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedToken &pLookup->lookupKind, ctxTree); } #endif - GenTree* argNode; - if (pConstrainedResolvedToken != nullptr) - { - // SVM call: use generic lookup to resolve the exact type and method - argNode = gtNewHelperCallNode( - CORINFO_HELP_RUNTIMEHANDLE_CLASS, - TYP_I_IMPL, - gtNewIconEmbHndNode(pConstrainedResolvedToken->hClass, nullptr, GTF_ICON_GLOBAL_PTR, pConstrainedResolvedToken->hClass)); - ctxTree = gtNewHelperCallNode( - CORINFO_HELP_RUNTIMEHANDLE_METHOD, - TYP_I_IMPL, - gtNewIconEmbHndNode(pResolvedToken->hMethod, nullptr, GTF_ICON_GLOBAL_PTR, pResolvedToken->hMethod)); - } - else - { - argNode = gtNewIconEmbHndNode(pRuntimeLookup->signature, nullptr, GTF_ICON_GLOBAL_PTR, compileTimeHandle); - } - return gtNewRuntimeLookupHelperCallNode(pRuntimeLookup, ctxTree, argNode); + return gtNewRuntimeLookupHelperCallNode(pRuntimeLookup, ctxTree, compileTimeHandle); } // Slot pointer @@ -4044,7 +4024,7 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, CORINFO_GENERICHANDLE_RESULT embedInfo; info.compCompHnd->expandRawHandleIntrinsic(&resolvedToken, &embedInfo); - GenTree* rawHandle = impLookupToTree(&resolvedToken, nullptr, &embedInfo.lookup, gtTokenToIconFlags(memberRef), + GenTree* rawHandle = impLookupToTree(&resolvedToken, &embedInfo.lookup, gtTokenToIconFlags(memberRef), embedInfo.compileTimeHandle); if (rawHandle == nullptr) { @@ -7393,7 +7373,7 @@ GenTree* Compiler::impImportLdvirtftn(GenTree* thisPtr, if ((pCallInfo->sig.sigInst.methInstCount != 0) && IsTargetAbi(CORINFO_NATIVEAOT_ABI)) { GenTree* runtimeMethodHandle = - impLookupToTree(pResolvedToken, nullptr, &pCallInfo->codePointerLookup, GTF_ICON_METHOD_HDL, pCallInfo->hMethod); + impLookupToTree(pResolvedToken, &pCallInfo->codePointerLookup, GTF_ICON_METHOD_HDL, pCallInfo->hMethod); return gtNewHelperCallNode(CORINFO_HELP_GVMLOOKUP_FOR_SLOT, TYP_I_IMPL, thisPtr, runtimeMethodHandle); } @@ -9475,46 +9455,42 @@ var_types Compiler::impImportCall(OPCODE opcode, switch (callInfo->kind) { - case CORINFO_VIRTUALSTATICCALL_STUB: + case CORINFO_VIRTUALSTATICCALL: { - assert(mflags & CORINFO_FLG_STATIC); - if (callInfo->stubLookup.lookupKind.needsRuntimeLookup) - { - GenTree* stubAddr = impRuntimeLookupToTree(pResolvedToken, pConstrainedResolvedToken, &callInfo->stubLookup, methHnd); - assert(stubAddr != nullptr); + assert((mflags & CORINFO_FLG_STATIC)); + assert(!(clsFlags & CORINFO_FLG_VALUECLASS)); + assert(callInfo->stubLookup.lookupKind.needsRuntimeLookup); - // The stubAddr may be a - // complex expression. As it is evaluated after the args, - // it may cause registered args to be spilled. Simply spill it. + GenTree* targetAddrPtr = impRuntimeLookupToTree(pResolvedToken, &callInfo->stubLookup, methHnd); + assert(!compDonotInline()); - unsigned lclNum = lvaGrabTemp(true DEBUGARG("VirtualCall with runtime lookup")); - impAssignTempGen(lclNum, stubAddr, (unsigned)CHECK_SPILL_NONE); - stubAddr = gtNewLclvNode(lclNum, TYP_I_IMPL); + // This is the rough code to set up an indirect stub call + assert(targetAddrPtr != nullptr); - call = gtNewIndCallNode(stubAddr, callRetTyp); + // The targetAddrPtr may be a + // complex expression. As it is evaluated after the args, + // it may cause registered args to be spilled. Simply spill it. - call->gtFlags |= GTF_EXCEPT | (stubAddr->gtFlags & GTF_GLOB_EFFECT); - call->gtFlags |= GTF_CALL_VIRT_STUB; + unsigned lclNum = lvaGrabTemp(true DEBUGARG("VirtualStaticCall with runtime lookup")); + impAssignTempGen(lclNum, targetAddrPtr, (unsigned)CHECK_SPILL_NONE); + targetAddrPtr = gtNewLclvNode(lclNum, TYP_I_IMPL); + + // Create the actual call node + + assert((sig->callConv & CORINFO_CALLCONV_MASK) != CORINFO_CALLCONV_VARARG && + (sig->callConv & CORINFO_CALLCONV_MASK) != CORINFO_CALLCONV_NATIVEVARARG); + + call = gtNewIndCallNode(targetAddrPtr, callRetTyp); + + call->gtFlags |= GTF_EXCEPT | (targetAddrPtr->gtFlags & GTF_GLOB_EFFECT); + call->gtFlags |= GTF_CALL_NONVIRT; #ifdef TARGET_X86 - // No tailcalls allowed for these yet... - canTailCall = false; - szCanTailCallFailReason = "VirtualStaticCall with runtime lookup"; + // No tailcalls allowed for these yet... + canTailCall = false; + szCanTailCallFailReason = "VirtualStaticCall with runtime lookup"; #endif - } - else - { - // The stub address is known at compile time - call = gtNewCallNode(CT_USER_FUNC, callInfo->hMethod, callRetTyp, di); - call->AsCall()->gtStubCallStubAddr = callInfo->stubLookup.constLookup.addr; - call->gtFlags |= GTF_CALL_VIRT_STUB; - assert(callInfo->stubLookup.constLookup.accessType != IAT_PPVALUE && - callInfo->stubLookup.constLookup.accessType != IAT_RELPVALUE); - if (callInfo->stubLookup.constLookup.accessType == IAT_PVALUE) - { - call->AsCall()->gtCallMoreFlags |= GTF_CALL_M_VIRTSTUB_REL_INDIRECT; - } - } + break; } @@ -9532,7 +9508,7 @@ var_types Compiler::impImportCall(OPCODE opcode, return TYP_UNDEF; } - GenTree* stubAddr = impRuntimeLookupToTree(pResolvedToken, nullptr, &callInfo->stubLookup, methHnd); + GenTree* stubAddr = impRuntimeLookupToTree(pResolvedToken, &callInfo->stubLookup, methHnd); assert(!compDonotInline()); // This is the rough code to set up an indirect stub call @@ -9703,7 +9679,7 @@ var_types Compiler::impImportCall(OPCODE opcode, assert((sig->callConv & CORINFO_CALLCONV_MASK) != CORINFO_CALLCONV_NATIVEVARARG); GenTree* fptr = - impLookupToTree(pResolvedToken, nullptr, &callInfo->codePointerLookup, GTF_ICON_FTN_ADDR, callInfo->hMethod); + impLookupToTree(pResolvedToken, &callInfo->codePointerLookup, GTF_ICON_FTN_ADDR, callInfo->hMethod); if (compDonotInline()) { @@ -9988,7 +9964,7 @@ var_types Compiler::impImportCall(OPCODE opcode, if (sig->callConv & CORINFO_CALLCONV_PARAMTYPE) { - assert(call->AsCall()->gtCallType == CT_USER_FUNC || call->AsCall()->gtCallType == CT_INDIRECT); + assert(call->AsCall()->gtCallType == CT_USER_FUNC); if (clsHnd == nullptr) { NO_WAY("CALLI on parameterized type"); @@ -15192,7 +15168,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) impHandleAccessAllowed(callInfo.accessAllowed, &callInfo.callsiteCalloutHelper); DO_LDFTN: - op1 = impMethodPointer(&resolvedToken, &constrainedResolvedToken, &callInfo); + op1 = impMethodPointer(&resolvedToken, &callInfo); if (compDonotInline()) { diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index f27b963126fa46..536407ba9a3ea2 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -7532,10 +7532,9 @@ GenTree* Compiler::getRuntimeLookupTree(CORINFO_RESOLVED_TOKEN* pResolvedToken, // The long-term solution is to introduce a new node representing a runtime lookup, create instances // of that node both in the importer and here, and expand the node in lower (introducing control flow if // necessary). - GenTree* argNode = gtNewIconEmbHndNode(pRuntimeLookup->signature, nullptr, GTF_ICON_GLOBAL_PTR, compileTimeHandle); return gtNewRuntimeLookupHelperCallNode(pRuntimeLookup, getRuntimeContextTree(pLookup->lookupKind.runtimeLookupKind), - argNode); + compileTimeHandle); } GenTree* result = getRuntimeContextTree(pLookup->lookupKind.runtimeLookupKind); diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index 273e310d8c0bb3..9a173957fa007a 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -191,7 +191,6 @@ public enum CORINFO_RUNTIME_LOOKUP_KIND CORINFO_LOOKUP_THISOBJ, CORINFO_LOOKUP_METHODPARAM, CORINFO_LOOKUP_CLASSPARAM, - CORINFO_LOOKUP_VIRTUALSTATIC, CORINFO_LOOKUP_NOT_SUPPORTED, // Returned for attempts to inline dictionary lookups } diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index 6da6b00c918fd6..9fe9d2b9b95e42 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -3375,34 +3375,31 @@ NOINLINE HCIMPL3(CORINFO_MethodPtr, JIT_VirtualFunctionPointer_Framed, Object * } HCIMPLEND -HCIMPL2(void*, JIT_DispatchVirtualStatic, MethodDesc *interfaceMethod, MethodTable *constrainedType) +HCIMPL3(void, JIT_StaticVirtualAmbiguousResolution, + MethodDesc *method, + MethodTable *interfaceType, + MethodTable *targetType) { FCALL_CONTRACT; - BOOL uniqueResolution = FALSE; - MethodDesc *methodDesc = nullptr; + SString strMethodName; + SString strInterfaceName; + SString strTargetClassName; - HELPER_METHOD_FRAME_BEGIN_0(); + HELPER_METHOD_FRAME_BEGIN_0(); // Set up a frame - methodDesc = constrainedType->ResolveVirtualStaticMethod( - interfaceMethod->GetMethodTable(), - interfaceMethod, - /* allowNullResult */ TRUE, - /* verifyImplemented */ FALSE, - /* allowVariantMatches */ TRUE, - /* uniqueResolution */ &uniqueResolution); + TypeString::AppendMethod(strMethodName, method, method->GetMethodInstantiation()); + TypeString::AppendType(strInterfaceName, TypeHandle(interfaceType)); + TypeString::AppendType(strTargetClassName, targetType); - HELPER_METHOD_FRAME_END(); + HELPER_METHOD_FRAME_END(); // Set up a frame - if (!uniqueResolution) - { - FCThrow(kAmbiguousImplementationException); - } - else if (methodDesc == nullptr) - { - FCThrow(kTypeLoadException); - } - return methodDesc; + FCThrowExVoid( + kAmbiguousImplementationException, + IDS_CLASSLOAD_AMBIGUOUS_OVERRIDE, + strMethodName, + strInterfaceName, + strTargetClassName); } HCIMPLEND diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 2a65ceb0550809..0ce3a418a71cf8 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -2875,18 +2875,6 @@ static bool IsTypeSpecForTypicalInstantiation(SigPointer sigptr) return IsSignatureForTypicalInstantiation(sigptr, ELEMENT_TYPE_VAR, ntypars); } -static void EncodeStaticVirtualRuntimeLookup(CORINFO_LOOKUP *pResultLookup, CORINFO_METHOD_HANDLE methodHandle) -{ - pResultLookup->lookupKind.runtimeLookupKind = CORINFO_LOOKUP_VIRTUALSTATIC; - - CORINFO_RUNTIME_LOOKUP *pResult = &pResultLookup->runtimeLookup; - pResult->signature = (void*)methodHandle; - pResult->helper = CORINFO_HELP_VIRTUAL_STATIC; - pResult->indirections = CORINFO_USEHELPER; - pResult->testForNull = 0; - pResult->testForFixup = 0; -} - void CEEInfo::ComputeRuntimeLookupForSharedGenericToken(DictionaryEntryKind entryKind, CORINFO_RESOLVED_TOKEN * pResolvedToken, CORINFO_RESOLVED_TOKEN * pConstrainedResolvedToken, @@ -2920,25 +2908,13 @@ void CEEInfo::ComputeRuntimeLookupForSharedGenericToken(DictionaryEntryKind entr return; } - MethodDesc* pTargetMD = (MethodDesc*)pResolvedToken->hMethod; MethodDesc* pContextMD = GetMethodFromContext(pResolvedToken->tokenContext); MethodTable* pContextMT = pContextMD->GetMethodTable(); - bool targetIsStaticVirtual = (pTargetMD != nullptr && pTargetMD->IsStatic() && pConstrainedResolvedToken != nullptr); - - if (targetIsStaticVirtual) - { - EncodeStaticVirtualRuntimeLookup(pResultLookup, pResolvedToken->hMethod); - if (!pContextMD->IsSharedByGenericInstantiations()) - { - return; - } - } + bool isStaticVirtual = (pConstrainedResolvedToken != nullptr && pContextMD != nullptr && pContextMD->IsStatic()); // There is a pathological case where invalid IL refereces __Canon type directly, but there is no dictionary availabled to store the lookup. if (!pContextMD->IsSharedByGenericInstantiations()) - { COMPlusThrow(kInvalidProgramException); - } BOOL fInstrument = FALSE; @@ -2954,13 +2930,6 @@ void CEEInfo::ComputeRuntimeLookupForSharedGenericToken(DictionaryEntryKind entr pResultLookup->lookupKind.runtimeLookupKind = CORINFO_LOOKUP_THISOBJ; } - if (targetIsStaticVirtual) - { - // For SVM lookup, we just need to fill in the generic context lookup kind, the runtime helper - // will take care of resolving the actual target method. - return; - } - // If we've got a method type parameter of any kind then we must look in the method desc arg if (pContextMD->RequiresInstMethodDescArg()) { @@ -5205,17 +5174,17 @@ void CEEInfo::getCallInfo( pResult->kind = CORINFO_VIRTUALCALL_STUB; if (fIsStaticVirtualMethod) { - pResult->kind = CORINFO_VIRTUALSTATICCALL_STUB; + pResult->kind = CORINFO_VIRTUALSTATICCALL; } // We can't make stub calls when we need exact information // for interface calls from shared code. - if (pResult->exactContextNeedsRuntimeLookup || fIsStaticVirtualMethod) + if (pResult->exactContextNeedsRuntimeLookup) { _ASSERTE(!m_pMethodBeingCompiled->IsDynamicMethod()); - ComputeRuntimeLookupForSharedGenericToken(DispatchStubAddrSlot, + ComputeRuntimeLookupForSharedGenericToken(fIsStaticVirtualMethod ? ConstrainedMethodEntrySlot : DispatchStubAddrSlot, pResolvedToken, pConstrainedResolvedToken, pMD, @@ -5442,6 +5411,30 @@ void CEEInfo::getCallInfo( signatureKind = SK_CALLSITE; } getMethodSigInternal(pResult->hMethod, &pResult->sig, (pResult->hMethod == pResolvedToken->hMethod) ? pResolvedToken->hClass : NULL, signatureKind); + if (fIsStaticVirtualMethod) + { + if (fResolvedConstraint) + { + // Runtime lookup for static virtual methods always returns exact call addresses not requiring the instantiation argument + pResult->sig.callConv = (CorInfoCallConv)(pResult->sig.callConv & ~CORINFO_CALLCONV_PARAMTYPE); + } + else + { + // Unresolved static virtual method in the absence of shared generics means + // that the runtime needs to throw when reaching the call. SVM resolution within + // shared generics is covered by the ConstrainedMethodEntrySlot dictionary entry. + pResult->kind = CORINFO_CALL; + pResult->accessAllowed = CORINFO_ACCESS_ILLEGAL; + pResult->callsiteCalloutHelper.helperNum = CORINFO_HELP_STATIC_VIRTUAL_AMBIGUOUS_RESOLUTION; + pResult->callsiteCalloutHelper.numArgs = 3; + pResult->callsiteCalloutHelper.args[0].methodHandle = (CORINFO_METHOD_HANDLE)pMD; + pResult->callsiteCalloutHelper.args[0].argType = CORINFO_HELPER_ARG_TYPE_Method; + pResult->callsiteCalloutHelper.args[1].classHandle = (CORINFO_CLASS_HANDLE)th.AsMethodTable(); + pResult->callsiteCalloutHelper.args[1].argType = CORINFO_HELPER_ARG_TYPE_Class; + pResult->callsiteCalloutHelper.args[2].classHandle = (CORINFO_CLASS_HANDLE)constrainedType.AsMethodTable(); + pResult->callsiteCalloutHelper.args[2].argType = CORINFO_HELPER_ARG_TYPE_Class; + } + } if (flags & CORINFO_CALLINFO_VERIFICATION) { diff --git a/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il index 7454f8164807c5..3190f1c2e2f384 100644 --- a/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il +++ b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il @@ -673,14 +673,14 @@ } // end of class GI1`1 -.class private auto ansi beforefieldinit ResolutionAtRuntime +.class private auto ansi beforefieldinit ResolutionAtRuntimeBase implements class IResolutionAtRuntime`1, class IResolutionAtRuntime`1 { .method private hidebysig static int32 FuncObject(int32 arg) cil managed { .override method int32 class IResolutionAtRuntime`1::Func(int32) - ldstr "At ResolutionAtRuntime.FuncObject" + ldstr "At ResolutionAtRuntimeBase.FuncObject" call void [mscorlib]System.Console::WriteLine(string) nop ldarg.0 @@ -692,7 +692,7 @@ .method private hidebysig static int32 FuncString(int32 arg) cil managed { .override method int32 class IResolutionAtRuntime`1::Func(int32) - ldstr "At ResolutionAtRuntime.FuncString" + ldstr "At ResolutionAtRuntimeBase.FuncString" call void [mscorlib]System.Console::WriteLine(string) nop ldarg.0 @@ -700,10 +700,54 @@ add ret } +} + +.class private auto ansi beforefieldinit ResolutionAtRuntimeThisObj`2<(class IResolutionAtRuntime`1) T, V> + extends [mscorlib]System.Object +{ + .method public hidebysig specialname rtspecialname + instance void .ctor() il managed + { + ldarg.0 + call instance void [mscorlib]System.Object::.ctor() + ret + } // end of method 'Test::.ctor' + + + .method public hidebysig instance int32 RuntimeResolvedFunc(int32 arg) + { + ldstr "At ResolutionAtRuntimeThisObj::RuntimeResolvedFunc" + call void [mscorlib]System.Console::WriteLine(string) + nop + ldarg.1 + constrained. !T + call int32 class [svm_diamondshape]IResolutionAtRuntime`1::Func(int32) + ret + } + +} // end of class ResolutionAtRuntimeThisObj + + +.class private auto ansi beforefieldinit ResolutionAtRuntimeClassParam`2<(class IResolutionAtRuntime`1) T, V> +{ + .method public hidebysig static int32 RuntimeResolvedFunc(int32 arg) + { + ldstr "At ResolutionAtRuntimeClassParam::RuntimeResolvedFunc" + call void [mscorlib]System.Console::WriteLine(string) + nop + ldarg.0 + constrained. !T + call int32 class [svm_diamondshape]IResolutionAtRuntime`1::Func(int32) + ret + } + +} // end of class ResolutionAtRuntimeClassParam +.class private auto ansi beforefieldinit ResolutionAtRuntimeMethodParam +{ .method public hidebysig static int32 RuntimeResolvedFunc`2<(class IResolutionAtRuntime`1) T, V>(int32 arg) { - ldstr "At RuntimeResolvedFunc" + ldstr "At ResolutionAtRuntimeMethodParam::RuntimeResolvedFunc" call void [mscorlib]System.Console::WriteLine(string) nop ldarg.0 @@ -712,7 +756,7 @@ ret } -} // end of class ResolutionAtRuntime +} // end of class ResolutionAtRuntimeMethodParam .class private auto ansi beforefieldinit Program extends [mscorlib]System.Object @@ -1171,7 +1215,6 @@ // Code size 189 (0xbd) .maxstack 10 .locals init (class [mscorlib]System.Type[] V_4) -/* IL_0000: nop IL_0001: ldstr "Calling I1.Func on I4Class - expecting I4.Func" IL_0006: call void [mscorlib]System.Console::WriteLine(string) @@ -1410,26 +1453,69 @@ ldstr "S must be string" call void Test::Assert(bool, string) nop -*/ - ldstr "Calling ResolutionAtRuntime::RuntimeResolvedFunc - expecting ResolutionAtRuntime.FuncObject" + ldstr "Calling ResolutionAtRuntimeThisObj::RuntimeResolvedFunc - expecting ResolutionAtRuntimeBase.FuncObject" + call void [mscorlib]System.Console::WriteLine(string) + nop + newobj instance void class ResolutionAtRuntimeThisObj`2::.ctor() + ldc.i4.s 200 + call instance int32 class ResolutionAtRuntimeThisObj`2::RuntimeResolvedFunc(int32) + ldc.i4.s 219 + ceq + ldstr "Expecting ResolutionAtRuntimeThisObj::RuntimeResolvedFunc to land on ResolutionAtRuntimeBase.FuncObject" + call void Test::Assert(bool, string) + + ldstr "Calling ResolutionAtRuntimeThisObj::RuntimeResolvedFunc - expecting ResolutionAtRuntimeBase.FuncString" + call void [mscorlib]System.Console::WriteLine(string) + nop + newobj instance void class ResolutionAtRuntimeThisObj`2::.ctor() + ldc.i4.s 200 + call instance int32 class ResolutionAtRuntimeThisObj`2::RuntimeResolvedFunc(int32) + ldc.i4.s 223 + ceq + ldstr "Expecting ResolutionAtRuntimeThisObj::RuntimeResolvedFunc to land on ResolutionAtRuntimeBase.FuncString" + call void Test::Assert(bool, string) + + + ldstr "Calling ResolutionAtRuntimeClassParam::RuntimeResolvedFunc - expecting ResolutionAtRuntimeBase.FuncObject" + call void [mscorlib]System.Console::WriteLine(string) + nop + ldc.i4.s 200 + call int32 class ResolutionAtRuntimeClassParam`2::RuntimeResolvedFunc(int32) + ldc.i4.s 219 + ceq + ldstr "Expecting ResolutionAtRuntimeClassParam::RuntimeResolvedFunc to land on ResolutionAtRuntimeBase.FuncObject" + call void Test::Assert(bool, string) + + ldstr "Calling ResolutionAtRuntimeClassParam::RuntimeResolvedFunc - expecting ResolutionAtRuntimeBase.FuncString" + call void [mscorlib]System.Console::WriteLine(string) + nop + ldc.i4.s 200 + call int32 class ResolutionAtRuntimeClassParam`2::RuntimeResolvedFunc(int32) + ldc.i4.s 223 + ceq + ldstr "Expecting ResolutionAtRuntimeClassParam::RuntimeResolvedFunc to land on ResolutionAtRuntimeBase.FuncString" + call void Test::Assert(bool, string) + + + ldstr "Calling ResolutionAtRuntimeMethodParam::RuntimeResolvedFunc - expecting ResolutionAtRuntimeBase.FuncObject" call void [mscorlib]System.Console::WriteLine(string) nop ldc.i4.s 100 - call int32 class [svm_diamondshape]ResolutionAtRuntime::RuntimeResolvedFunc`2(int32) + call int32 class ResolutionAtRuntimeMethodParam::RuntimeResolvedFunc`2(int32) ldc.i4.s 119 ceq - ldstr "Expecting ResolutionAtRuntime::RuntimeResolvedFunc to land on RuntimeResolvedFunc.FuncObject" + ldstr "Expecting ResolutionAtRuntimeMethodParam::RuntimeResolvedFunc to land on ResolutionAtRuntimeBase.FuncObject" call void Test::Assert(bool, string) - ldstr "Calling ResolutionAtRuntime::RuntimeResolvedFunc - expecting ResolutionAtRuntime.FuncString" + ldstr "Calling ResolutionAtRuntimeMethodParam::RuntimeResolvedFunc - expecting ResolutionAtRuntimeBase.FuncString" call void [mscorlib]System.Console::WriteLine(string) nop ldc.i4.s 100 - call int32 class [svm_diamondshape]ResolutionAtRuntime::RuntimeResolvedFunc`2(int32) + call int32 class ResolutionAtRuntimeMethodParam::RuntimeResolvedFunc`2(int32) ldc.i4.s 123 ceq - ldstr "Expecting ResolutionAtRuntime::RuntimeResolvedFunc to land on RuntimeResolvedFunc.FuncString" + ldstr "Expecting ResolutionAtRuntimeMethodParam::RuntimeResolvedFunc to land on ResolutionAtRuntimeBase.FuncString" call void Test::Assert(bool, string) IL_00bc: ret @@ -1445,8 +1531,8 @@ .maxstack 1 .locals init (int32 V_0) IL_0000: nop -// IL_0001: call void Program::Negative() -// IL_0006: nop + IL_0001: call void Program::Negative() + IL_0006: nop IL_0007: call void Program::Positive() IL_000c: nop IL_000d: call int32 Test::Ret() From 7d0b7afcdae28c49e6949fe540317dedc9bfbfc0 Mon Sep 17 00:00:00 2001 From: Tomas Date: Fri, 27 May 2022 19:59:36 +0200 Subject: [PATCH 16/22] Fix calling convention flag setting in getCallInfo --- src/coreclr/vm/jitinterface.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 0ce3a418a71cf8..f2e3e119fad5ae 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -5411,9 +5411,9 @@ void CEEInfo::getCallInfo( signatureKind = SK_CALLSITE; } getMethodSigInternal(pResult->hMethod, &pResult->sig, (pResult->hMethod == pResolvedToken->hMethod) ? pResolvedToken->hClass : NULL, signatureKind); - if (fIsStaticVirtualMethod) + if (fIsStaticVirtualMethod && !fResolvedConstraint) { - if (fResolvedConstraint) + if (pResult->exactContextNeedsRuntimeLookup) { // Runtime lookup for static virtual methods always returns exact call addresses not requiring the instantiation argument pResult->sig.callConv = (CorInfoCallConv)(pResult->sig.callConv & ~CORINFO_CALLCONV_PARAMTYPE); From 8a873a4dcad6e080f7602391027ad12a6117cc5c Mon Sep 17 00:00:00 2001 From: Tomas Date: Fri, 27 May 2022 21:09:20 +0200 Subject: [PATCH 17/22] Fix test C# source code according to the IL; fix a few IL typos --- .../DiamondShape/svm_diamondshape.cs | 438 +++++++++++++++--- .../DiamondShape/svm_diamondshape.il | 22 +- 2 files changed, 377 insertions(+), 83 deletions(-) diff --git a/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.cs b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.cs index 723600f15f5cb7..0d7287f40af8c5 100644 --- a/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.cs +++ b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.cs @@ -5,22 +5,13 @@ interface IFoo { - static int Foo(int a); -} - -class IFoo_Impl -{ - static int Foo(int a) + virtual static int Foo(int a) { return a; } } interface IFoo2 : IFoo -{ -} - -class IFoo2_Impl : IFoo { static int IFoo.Foo(int a) { @@ -30,25 +21,29 @@ static int IFoo.Foo(int a) } interface IFooEx : IFoo -{ -} - -class IFooEx_Impl : IFoo { static int IFoo.Foo(int a) { Console.WriteLine("At IFooEx.Foo"); return a + 2; - } + } } -class FooClass : IFoo2, IFooEx +interface IFooExReabstract : IFooEx +{ + abstract static int IFoo.Foo(int a); +} + +class FooClass : IFoo2, IFoo, IFooEx +{ +} + +class FooClassReabstract : IFoo2, IFoo, IFooExReabstract +{ +} + +struct FooStruct : IFoo2, IFoo, IFooEx { - // Dummy - public static int Foo(int a) - { - return 0; - } } interface I1 @@ -58,101 +53,207 @@ interface I1 interface I2 : I1 { - // static int I1.Func(int a) { return a + 2; } + static int I1.Func(int a) + { + Console.WriteLine("At I2.Func"); + return a + 2; + } } interface I3 : I1 { - // static int I1.Func(int a) { return a + 3; } + static int I1.Func(int a) + { + Console.WriteLine("At I3.Func"); + return a + 3; + } +} + +interface I4 : I2, I1, I3 +{ + static int I1.Func(int a) + { + Console.WriteLine("At I4.Func"); + return a + 4; + } } -interface I4 : I2, I3 +interface I4Reabstract : I4 { - // static int I1.Func(int a) { return a + 4; } + abstract static int I1.Func(int a); } -class I4Class : I4 +interface I4Reimplement : I4Reabstract { - // @REMOVE static int I1.Func(int a) { - Console.WriteLine("At I4Class.Func"); - return a + 4; + Console.WriteLine("At I4Reimplement.Func"); + return a + 17; } } -interface I5: I1 +class I4Class : I4, I2, I1, I3 { - // static int I1.Func(int a) { return a + 5; } } -interface I6: I1 +class I4ReimplementClass : I4Reimplement, I2, I1, I3 { - // static int I1.Func(int a) { return a + 6; } } -interface I7: I5, I6 +struct I4Struct : I4, I2, I1, I3 { - // static int I1.Func(int a) { return a + 7; } } -interface I8: I4, I7 +interface I5 : I1 { - // static int I1.Func(int a) { return a + 8; } + static int I1.Func(int a) + { + Console.WriteLine("At I5.Func"); + return a + 5; + } } -class I47Class: I4, I7 +interface I6 : I1 { - // @REMOVE static int I1.Func(int a) { - Console.WriteLine("At I4Class.Func"); - return a + 8; - } + Console.WriteLine("At I6.Func"); + return a + 6; + } +} +interface I7 : I5, I6 +{ + static int I1.Func(int a) + { + Console.WriteLine("At I7.Func"); + return a + 7; + } } -class I8Class: I8 +interface I8 : I4, I2, I1, I3, I7, I5, I6 { - // @REMOVE static int I1.Func(int a) { - Console.WriteLine("At I4Class.Func"); + Console.WriteLine("At I8.Func"); return a + 8; - } + } +} + +class I47Class : I4, I2, I1, I3, I7, I5, I6 +{ +} + +struct I47Struct : I4, I2, I1, I3, I7, I5, I6 +{ +} + +class I8Class : I8, I4, I2, I1, I3, I7, I5, I6 +{ +} + +struct I8Struct : I8, I4, I2, I1, I3, I7, I5, I6 +{ } interface GI1 { - static int Func(out Type[] types); + static int Func(out Type[] types); } interface GI2 : GI1 { - // static int GI1.Func(out Type[] types) { Console.WriteLine(typeof(T) + ", "typeof(S) + ", GI1Class"); types = new Type[] { typeof(T), typeof(S) }; return 2; } + static int GI1.Func(out Type[] types) + { + Console.WriteLine(typeof(T) + ", " + typeof(S) + ", GI2"); + types = new Type[] { typeof(T), typeof(S) }; + return 2; + } -} +} interface GI3 : GI1 { - // static int GI1.Func(out Type[] types) { Console.WriteLine(typeof(T) + ", "typeof(S) + ", GI1Class"); types = new Type[] { typeof(T), typeof(S) }; return 3; } -} + static int GI1.Func(out Type[] types) + { + Console.WriteLine(typeof(T) + ", " + typeof(S) + ", GI3"); + types = new Type[] { typeof(T), typeof(S) }; + return 3; + } +} + +interface GI4 : GI2, GI1, GI3 +{ + static int GI1.Func(out Type[] types) + { + Console.WriteLine(typeof(T) + ", " + typeof(S) + ", GI4"); + types = new Type[] { typeof(T), typeof(S) }; + return 4; + } +} + +class GI23Class : GI2, GI1, GI3 +{ +} + +struct GI23Struct : GI2, GI1, GI3 +{ +} + +class GI4Class : GI4, GI2, GI1, GI3 +{ +} + +struct GI4Struct : GI4, GI2, GI1, GI3 +{ +} + +interface IResolutionAtRuntime +{ + virtual abstract static int Func(int a); +} + +class ResolutionAtRuntimeBase : IResolutionAtRuntime, IResolutionAtRuntime +{ + static int IResolutionAtRuntime.Func(int a) + { + Console.WriteLine("At ResolutionAtRuntimeBase.FuncObject"); + return a + 19; + } + + static int IResolutionAtRuntime.Func(int a) + { + Console.WriteLine("At ResolutionAtRuntimeBase.FuncString"); + return a + 23; + } +} -interface GI4 : GI2, GI3 +class ResolutionAtRuntimeThisObj : T is IResolutionAtRuntime { - // static int GI1.Func(out Type[] types) { Console.WriteLine(typeof(T) + ", "typeof(S) + ", GI1Class"); types = new Type[] { typeof(T), typeof(S) }; return 4; } -} + public int RuntimeResolvedFunc(int a) + { + Console.WriteLine("At ResolutionAtRuntimeThisObj.RuntimeResolvedFunc"); + return T.Func(a); + } +} -class GI23Class: GI2, GI3 +class ResolutionAtRuntimeClassParam : T is IResolutionAtRuntime { - // @REMOVE - static int GI1.Func(out Type[] types) { Console.WriteLine(typeof(T) + ", " + typeof(S) + ", GI1Class"); types = new Type[] { typeof(T), typeof(S) }; return 4; } + public static int RuntimeResolvedFunc(int a) + { + Console.WriteLine("At ResolutionAtRuntimeClassParam.RuntimeResolvedFunc"); + return T.Func(a); + } } -class GI4Class: GI4 +class ResolutionAtRuntimeMethodParam { - // @REMOVE - static int GI1.Func(out Type[] types) { Console.WriteLine(typeof(T) + ", " + typeof(S) + ", GI1Class"); types = new Type[] { typeof(T), typeof(S) }; return 4; } + public static int RuntimeResolvedFunc(int a) + : T is IResolutionAtRuntime + { + Console.WriteLine("At ResolutionAtRuntimeMethodParam.RuntimeResolvedFunc"); + return T.Func(a); + } } class Program @@ -162,26 +263,89 @@ private static void CallFoo(int value) { T.Foo(value); } - + + private static Func GetFooDelegate() + { + return new Func(T.Foo); + } + private static void CallI1Func(int value) : T is I1 { T.Func(value); } - + + private static Func GetI1FuncDelegate() + : T is I1 + { + return new Func(T.Func); + } + private static void CallGI1Func(out Type[] types) : T is GI1 { T.Func(out types); } - + + private delegate static int GI1Delegate(out Type[]); + + private static GI1Delegate GetGI1FuncDelegate() + : T is GI1 + { + return T.Func; + } + public static void Negative() { - Console.WriteLine("Calling IFoo.Foo on Foo - expecting exception."); + Console.WriteLine("Calling IFoo.Foo on FooClass - expecting exception."); try { CallFoo(10); - Test.Assert(false, "Expecting exception on Foo"); + Test.Assert(false, "Expecting exception on FooClass"); + } + catch(Exception ex) + { + Console.WriteLine("Exception caught: " + ex.ToString()); + } + + Console.WriteLine("Resolving delegate IFoo.Foo on FooClass - expecting exception."); + try + { + GetFooDelegate(); + Test.Assert(false, "Expecting exception on FooClass"); + } + catch(Exception ex) + { + Console.WriteLine("Exception caught: " + ex.ToString()); + } + + Console.WriteLine("Calling IFoo.Foo on FooClassReabstract - expecting exception."); + try + { + CallFoo(10); + Test.Assert(false, "Expecting exception on FooClassReabstract"); + } + catch(Exception ex) + { + Console.WriteLine("Exception caught: " + ex.ToString()); + } + + Console.WriteLine("Calling IFoo.Foo on FooStruct - expecting exception."); + try + { + CallFoo(10); + Test.Assert(false, "Expecting exception on FooStruct"); + } + catch(Exception ex) + { + Console.WriteLine("Exception caught: " + ex.ToString()); + } + + Console.WriteLine("Resolving delegate IFoo.Foo on FooStruct - expecting exception."); + try + { + GetFooDelegate(); + Test.Assert(false, "Expecting exception on FooStruct"); } catch(Exception ex) { @@ -199,8 +363,39 @@ public static void Negative() Console.WriteLine("Exception caught: " + ex.ToString()); } - var gi23Class = new GI23Class(); - GI1 gi1 = (GI1) gi23Class; + Console.WriteLine("Resolving delegate I1.Func on I47Class - expecting exception"); + try + { + GetI1FuncDelegate(); + Test.Assert(false, "Expecting exception on I47Class"); + } + catch(Exception ex) + { + Console.WriteLine("Exception caught: " + ex.ToString()); + } + + Console.WriteLine("Calling I1.Func on I47Struct - expecting exception"); + try + { + CallI1Func(10); + Test.Assert(false, "Expecting exception on I47Struct"); + } + catch(Exception ex) + { + Console.WriteLine("Exception caught: " + ex.ToString()); + } + + Console.WriteLine("Resolving delegate I1.Func on I47Struct - expecting exception"); + try + { + GetI1FuncDelegate(); + Test.Assert(false, "Expecting exception on I47Struct"); + } + catch(Exception ex) + { + Console.WriteLine("Exception caught: " + ex.ToString()); + } + Console.WriteLine("Calling GI1.Func on GI23Class - expecting exception"); try { @@ -211,7 +406,41 @@ public static void Negative() catch(Exception ex) { Console.WriteLine("Exception caught: " + ex.ToString()); - } + } + + Console.WriteLine("Resolving delegate GI1.Func on GI23Class - expecting exception"); + try + { + GetGI1FuncDelegate, GT1, string>(); + Test.Assert(false, "Expecting exception on GI23Class"); + } + catch(Exception ex) + { + Console.WriteLine("Exception caught: " + ex.ToString()); + } + + Console.WriteLine("Calling GI1.Func on GI23Struct - expecting exception"); + try + { + Type[] types; + CallGI1Func, GT1, string>(out types); + Test.Assert(false, "Expecting exception on GI23Struct"); + } + catch(Exception ex) + { + Console.WriteLine("Exception caught: " + ex.ToString()); + } + + Console.WriteLine("Resolving delegate GI1.Func on GI23Struct - expecting exception"); + try + { + GetGI1FuncDelegate, GT1, string>(); + Test.Assert(false, "Expecting exception on GI23Struct"); + } + catch(Exception ex) + { + Console.WriteLine("Exception caught: " + ex.ToString()); + } } public static void Positive() @@ -219,17 +448,82 @@ public static void Positive() Console.WriteLine("Calling I1.Func on I4Class - expecting I4.Func"); Test.Assert(CallI1Func(10) == 14, "Expecting I1.Func to land on I4.Func"); - + + Console.WriteLine("Calling I1.Func on I4ReimplementClass - expecting I4Reimplement.Func"); + + Test.Assert(CallI1Func(10) == 27, "Expecting I1.Func to land on I4Reimplement.Func"); + + Console.WriteLine("Calling I1.Func on I4Class as a delegate - expecting I4.Func"); + + Test.Assert(GetI1FuncDelegate()(10) == 14, "Expecting I1.Func to land on I4.Func"); + + Console.WriteLine("Calling I1.Func on I4Struct - expecting I4.Func"); + + Test.Assert(CallI1Func(10) == 14, "Expecting I1.Func to land on I4.Func"); + + Console.WriteLine("Calling I1.Func on I4Struct as a delegate - expecting I4.Func"); + + Test.Assert(GetI1FuncDelegate()(10) == 14, "Expecting I1.Func to land on I4.Func"); + Console.WriteLine("Calling I1.Func on I8Class - expecting I8.Func"); Test.Assert(CallI1Func(10) == 18, "Expecting I1.Func to land on I8.Func"); - Console.WriteLine("Calling GI1.Func on GI4Class - expecting GI4.Func"); + Console.WriteLine("Calling I1.Func on I8Class as a delegate - expecting I8.Func"); + + Test.Assert(GetI1FuncDelegate()(10) == 18, "Expecting I1.Func to land on I8.Func"); + + Console.WriteLine("Calling I1.Func on I8Struct - expecting I8.Func"); + + Test.Assert(CallI1Func(10) == 18, "Expecting I1.Func to land on I8.Func"); + + Console.WriteLine("Calling I1.Func on I8Struct as a delegate - expecting I8.Func"); + + Test.Assert(GetI1FuncDelegate()(10) == 18, "Expecting I1.Func to land on I8.Func"); Type[] types; + + Console.WriteLine("Calling GI1.Func on GI4Class - expecting GI4.Func"); + Test.Assert(CallGI1Func, GI1, string>(out types) == 4, "Expecting GI1.Func to land on GII4.Func"); Test.Assert(types[0] == typeof(object), "T must be object"); - Test.Assert(types[1] == typeof(string), "S must be string"); + Test.Assert(types[1] == typeof(string), "S must be string"); + + Console.WriteLine("Calling GI1.Func on GI4Class as a delegate - expecting GI4.Func"); + + Test.Assert(GetGI1FuncDelegate, GI1, string>()(out types) == 4, "Expecting GI1.Func to land on GII4.Func"); + Test.Assert(types[0] == typeof(object), "T must be object"); + Test.Assert(types[1] == typeof(string), "S must be string"); + + Console.WriteLine("Calling GI1.Func on GI4Struct - expecting GI4.Func"); + + Test.Assert(CallGI1Func, GI1, string>(out types) == 4, "Expecting GI1.Func to land on GII4.Func"); + Test.Assert(types[0] == typeof(object), "T must be object"); + Test.Assert(types[1] == typeof(string), "S must be string"); + + Console.WriteLine("Calling GI1.Func on GI4Struct as a delegate - expecting GI4.Func"); + + Test.Assert(GetGI1FuncDelegate, GI1, string>()(out types) == 4, "Expecting GI1.Func to land on GII4.Func"); + Test.Assert(types[0] == typeof(object), "T must be object"); + Test.Assert(types[1] == typeof(string), "S must be string"); + + Console.WriteLine("Calling ResolutionAtRuntimeThisObj::RuntimeResolvedFunc - expecting ResolutionAtRuntimeBase.FuncObject"); + Test.Assert(new ResolutionAtRuntimeThisObj().RuntimeResolvedFunc(200) == 219, "Expecting ResolutionAtRuntimeThisObj::RuntimeResolvedFunc to land on ResolutionAtRuntimeBase.FuncObject"); + + Console.WriteLine("Calling ResolutionAtRuntimeThisObj::RuntimeResolvedFunc - expecting ResolutionAtRuntimeBase.FuncString"); + Test.Assert(new ResolutionAtRuntimeThisObj().RuntimeResolvedFunc(200) == 223, "Expecting ResolutionAtRuntimeThisObj::RuntimeResolvedFunc to land on ResolutionAtRuntimeBase.FuncString"); + + Console.WriteLine("Calling ResolutionAtRuntimeClassParam::RuntimeResolvedFunc - expecting ResolutionAtRuntimeBase.FuncObject"); + Test.Assert(ResolutionAtRuntimeClassParam.RuntimeResolvedFunc(200) == 219, "Expecting ResolutionAtRuntimeClassParam::RuntimeResolvedFunc to land on ResolutionAtRuntimeBase.FuncObject"); + + Console.WriteLine("Calling ResolutionAtRuntimeClassParam::RuntimeResolvedFunc - expecting ResolutionAtRuntimeBase.FuncString"); + Test.Assert(ResolutionAtRuntimeClassParam.RuntimeResolvedFunc(200) == 223, "Expecting ResolutionAtRuntimeClassParam::RuntimeResolvedFunc to land on ResolutionAtRuntimeBase.FuncString"); + + Console.WriteLine("Calling ResolutionAtRuntimeMethodParam::RuntimeResolvedFunc - expecting ResolutionAtRuntimeBase.FuncObject"); + Test.Assert(ResolutionAtRuntimeClassParam.RuntimeResolvedFunc(100) == 119, "Expecting ResolutionAtRuntimeMethodParam::RuntimeResolvedFunc to land on ResolutionAtRuntimeBase.FuncObject"); + + Console.WriteLine("Calling ResolutionAtRuntimeMethodParam::RuntimeResolvedFunc - expecting ResolutionAtRuntimeBase.FuncString"); + Test.Assert(ResolutionAtRuntimeClassParam.RuntimeResolvedFunc(100) == 123, "Expecting ResolutionAtRuntimeMethodParam::RuntimeResolvedFunc to land on ResolutionAtRuntimeBase.FuncString"); } public static int Main() @@ -246,7 +540,7 @@ class Test public static int Ret() { - return Pass? 100 : 101; + return Pass ? 100 : 101; } public static void Assert(bool cond, string msg) diff --git a/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il index 3190f1c2e2f384..50c83313560ff8 100644 --- a/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il +++ b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il @@ -133,7 +133,7 @@ IFoo, IFooExReabstract { -} // end of class FooClass +} // end of class FooClassReabstract .class private auto ansi sealed beforefieldinit FooStruct extends [mscorlib]System.ValueType @@ -254,7 +254,7 @@ IL_0006: call void [mscorlib]System.Console::WriteLine(string) IL_000b: nop IL_000c: ldarg.0 - IL_000d: ldc.i4.7 + IL_000d: ldc.i4.s 17 IL_000e: add IL_000f: stloc.0 IL_0010: br.s IL_0012 @@ -806,7 +806,7 @@ } // end handler - IL_004aa: ldstr "resolving delegate IFoo.Foo on FooClass - expecting exception." + IL_004aa: ldstr "Resolving delegate IFoo.Foo on FooClass - expecting exception." call void [mscorlib]System.Console::WriteLine(string) nop .try @@ -845,11 +845,11 @@ { nop ldc.i4.s 10 - constrained. valuetype [svm_diamondshape]FooStruct + constrained. class [svm_diamondshape]FooClassReabstract call int32 IFoo::Foo(int32) pop ldc.i4.0 - ldstr "Expecting exception on FooStruct" + ldstr "Expecting exception on FooClassReabstract" call void Test::Assert(bool, string) nop @@ -974,18 +974,18 @@ } // end handler - IL_0093a: ldstr "Resolving delegate I1.Func on I47Struct - expecting exception" + IL_0093a: ldstr "Resolving delegate I1.Func on I47Class - expecting exception" call void [mscorlib]System.Console::WriteLine(string) nop .try { nop ldc.i4.s 10 - constrained. valuetype [svm_diamondshape]I47Struct + constrained. class [svm_diamondshape]I47Class ldftn int32 I1::Func(int32) pop ldc.i4.0 - ldstr "Expecting exception on I47Struct" + ldstr "Expecting exception on I47Class" call void Test::Assert(bool, string) nop nop @@ -1105,14 +1105,14 @@ } // end handler - IL_00e0a: ldstr "Resolving delegate GI1.Func on GI23Struct - expecting exception" + IL_00e0a: ldstr "Resolving delegate GI1.Func on GI23Class - expecting exception" call void [mscorlib]System.Console::WriteLine(string) nop .try { nop ldloca.s V_8 - constrained. valuetype [svm_diamondshape]GI23Struct`1 + constrained. class [svm_diamondshape]GI23Class`1 ldftn int32 class GI1`1::Func(class [mscorlib]System.Type[]&) pop ldc.i4.0 @@ -1234,7 +1234,7 @@ ldc.i4.s 10 constrained. class I4ReimplementClass call int32 I1::Func(int32) - ldc.i4.s 17 + ldc.i4.s 27 ceq ldstr "Expecting I1.Func to land on I4Reimplement.Func" call void Test::Assert(bool, string) From 1d6d27f2f2dbba8f293f512783511c59f392b4a1 Mon Sep 17 00:00:00 2001 From: Tomas Date: Fri, 27 May 2022 21:25:22 +0200 Subject: [PATCH 18/22] Remove dummy 'else' block --- src/coreclr/vm/methodtablebuilder.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index 8a6ba5429575fb..9f02e5d5e8f776 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -6393,10 +6393,6 @@ MethodTableBuilder::PlaceMethodImpls() } } } - else - { - - } iEntry++; From 23ce9aa2a2db6461d2849c04c849077842f82d66 Mon Sep 17 00:00:00 2001 From: Tomas Rylek Date: Mon, 30 May 2022 22:22:33 +0200 Subject: [PATCH 19/22] Fix failures in several pre-existing SVM tests 1. TryToResolveStaticVirtualMethodOnThisType doesn't need the uniqueResolution flag - non-unique resolution on a single type (i.o.w. multiple MethodImpl records resolving the same MethodDecl) is always an error. 2. In getCallInfo, treat a SVM call as requiring runtime lookup when the constrained type is a canonical subtype. Thanks Tomas --- src/coreclr/jit/importer.cpp | 4 ++++ src/coreclr/vm/jitinterface.cpp | 12 ++++++++++-- src/coreclr/vm/methodtable.cpp | 19 ++++++++----------- src/coreclr/vm/methodtable.h | 2 +- 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 989b68f19b70c5..e2d41d3537c011 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -2162,6 +2162,10 @@ GenTree* Compiler::impMethodPointer(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORI op1 = impLookupToTree(pResolvedToken, &pCallInfo->codePointerLookup, GTF_ICON_FTN_ADDR, pCallInfo->hMethod); break; + case CORINFO_VIRTUALSTATICCALL: + op1 = impRuntimeLookupToTree(pResolvedToken, &pCallInfo->stubLookup, pCallInfo->hMethod); + break; + default: noway_assert(!"unknown call kind"); break; diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index f2e3e119fad5ae..7ddee65acaaae7 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -4955,10 +4955,15 @@ void CEEInfo::getCallInfo( MethodDesc * pTargetMD = pMDAfterConstraintResolution; DWORD dwTargetMethodAttrs = pTargetMD->GetAttrs(); + pResult->exactContextNeedsRuntimeLookup = (!constrainedType.IsNull() && constrainedType.IsCanonicalSubtype()); + if (pTargetMD->HasMethodInstantiation()) { pResult->contextHandle = MAKE_METHODCONTEXT(pTargetMD); - pResult->exactContextNeedsRuntimeLookup = pTargetMD->GetMethodTable()->IsSharedByGenericInstantiations() || TypeHandle::IsCanonicalSubtypeInstantiation(pTargetMD->GetMethodInstantiation()); + if (pTargetMD->GetMethodTable()->IsSharedByGenericInstantiations() || TypeHandle::IsCanonicalSubtypeInstantiation(pTargetMD->GetMethodInstantiation())) + { + pResult->exactContextNeedsRuntimeLookup = TRUE; + } } else { @@ -4972,7 +4977,10 @@ void CEEInfo::getCallInfo( } pResult->contextHandle = MAKE_CLASSCONTEXT(exactType.AsPtr()); - pResult->exactContextNeedsRuntimeLookup = exactType.IsSharedByGenericInstantiations(); + if (exactType.IsSharedByGenericInstantiations()) + { + pResult->exactContextNeedsRuntimeLookup = TRUE; + } // Use main method as the context as long as the methods are called on the same type if (pResult->exactContextNeedsRuntimeLookup && diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index cf74b623a6b9a8..9e30c3782076a9 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -5475,8 +5475,7 @@ namespace MethodDesc *interfaceMD, MethodTable *interfaceMT, BOOL allowVariance, - MethodDesc **candidateMD, - BOOL *uniqueResolution) + MethodDesc **candidateMD) { *candidateMD = NULL; @@ -5569,8 +5568,7 @@ namespace candidateMaybe = pMT->TryResolveVirtualStaticMethodOnThisType( interfaceMT, interfaceMD, - /* verifyImplemented */ FALSE, - /* uniqueResolution */ uniqueResolution); + /* verifyImplemented */ FALSE); } } } @@ -5629,8 +5627,7 @@ BOOL MethodTable::FindDefaultInterfaceImplementation( // Check the current method table itself MethodDesc *candidateMaybe = NULL; - BOOL uniqueResolution; - if (IsInterface() && TryGetCandidateImplementation(this, pInterfaceMD, pInterfaceMT, allowVariance, &candidateMaybe, &uniqueResolution)) + if (IsInterface() && TryGetCandidateImplementation(this, pInterfaceMD, pInterfaceMT, allowVariance, &candidateMaybe)) { _ASSERTE(candidateMaybe != NULL); @@ -5670,7 +5667,7 @@ BOOL MethodTable::FindDefaultInterfaceImplementation( MethodTable *pCurMT = it.GetInterface(pMT); MethodDesc *pCurMD = NULL; - if (TryGetCandidateImplementation(pCurMT, pInterfaceMD, pInterfaceMT, allowVariance, &pCurMD, &uniqueResolution)) + if (TryGetCandidateImplementation(pCurMT, pInterfaceMD, pInterfaceMT, allowVariance, &pCurMD)) { // // Found a match. But is it a more specific match (we want most specific interfaces) @@ -8032,7 +8029,7 @@ MethodTable::ResolveVirtualStaticMethod( // Search for match on a per-level in the type hierarchy for (MethodTable* pMT = this; pMT != nullptr; pMT = pMT->GetParentMethodTable()) { - MethodDesc* pMD = pMT->TryResolveVirtualStaticMethodOnThisType(pInterfaceType, pInterfaceMD, verifyImplemented, uniqueResolution); + MethodDesc* pMD = pMT->TryResolveVirtualStaticMethodOnThisType(pInterfaceType, pInterfaceMD, verifyImplemented); if (pMD != nullptr) { return pMD; @@ -8076,7 +8073,7 @@ MethodTable::ResolveVirtualStaticMethod( { // Variant or equivalent matching interface found // Attempt to resolve on variance matched interface - pMD = pMT->TryResolveVirtualStaticMethodOnThisType(pItfInMap, pInterfaceMD, verifyImplemented, uniqueResolution); + pMD = pMT->TryResolveVirtualStaticMethodOnThisType(pItfInMap, pInterfaceMD, verifyImplemented); if (pMD != nullptr) { return pMD; @@ -8120,7 +8117,7 @@ MethodTable::ResolveVirtualStaticMethod( // Try to locate the appropriate MethodImpl matching a given interface static virtual method. // Returns nullptr on failure. MethodDesc* -MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL verifyImplemented, BOOL* uniqueResolution) +MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL verifyImplemented) { HRESULT hr = S_OK; IMDInternalImport* pMDInternalImport = GetMDImport(); @@ -8255,7 +8252,7 @@ MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType { return pMethodImpl; } - if (pPrevMethodImpl != nullptr && uniqueResolution == nullptr) + if (pPrevMethodImpl != nullptr) { // Two MethodImpl records found for the same virtual static interface method COMPlusThrow(kTypeLoadException, E_FAIL); diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index e2788419b5ea57..4b1d4cbd6e6979 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -2218,7 +2218,7 @@ class MethodTable // Try to resolve a given static virtual method override on this type. Return nullptr // when not found. - MethodDesc *TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL verifyImplemented, BOOL* uniqueResolution); + MethodDesc *TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL verifyImplemented); public: static MethodDesc *MapMethodDeclToMethodImpl(MethodDesc *pMDDecl); From 21c38da63cad2b16547404230d330758cc803625 Mon Sep 17 00:00:00 2001 From: Tomas Rylek Date: Mon, 30 May 2022 22:47:26 +0200 Subject: [PATCH 20/22] Revert CORINFO_STATICVIRTUALCALL per Michal's PR feedback As Michal pointed out, implementation of this call kind is quite similar to CORINFO_CALL_CODE_POINTER; initially I had trouble making it work but now the rest of the change is working fine it turns out that CORINFO_CALL_CODE_POINTER works fine so I removed the new kind I had originally introduced. Thanks Tomas --- src/coreclr/inc/corinfo.h | 3 +-- src/coreclr/jit/importer.cpp | 43 --------------------------------- src/coreclr/vm/jitinterface.cpp | 2 +- 3 files changed, 2 insertions(+), 46 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 9c4d2b9f6ec491..156444dfeb8952 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -1461,8 +1461,7 @@ enum CORINFO_CALL_KIND CORINFO_CALL_CODE_POINTER, CORINFO_VIRTUALCALL_STUB, CORINFO_VIRTUALCALL_LDVIRTFTN, - CORINFO_VIRTUALCALL_VTABLE, - CORINFO_VIRTUALSTATICCALL, + CORINFO_VIRTUALCALL_VTABLE }; // Indicates that the CORINFO_VIRTUALCALL_VTABLE lookup needn't do a chunk indirection diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index e2d41d3537c011..41e8879a1e27aa 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -2162,10 +2162,6 @@ GenTree* Compiler::impMethodPointer(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORI op1 = impLookupToTree(pResolvedToken, &pCallInfo->codePointerLookup, GTF_ICON_FTN_ADDR, pCallInfo->hMethod); break; - case CORINFO_VIRTUALSTATICCALL: - op1 = impRuntimeLookupToTree(pResolvedToken, &pCallInfo->stubLookup, pCallInfo->hMethod); - break; - default: noway_assert(!"unknown call kind"); break; @@ -9459,45 +9455,6 @@ var_types Compiler::impImportCall(OPCODE opcode, switch (callInfo->kind) { - case CORINFO_VIRTUALSTATICCALL: - { - assert((mflags & CORINFO_FLG_STATIC)); - assert(!(clsFlags & CORINFO_FLG_VALUECLASS)); - assert(callInfo->stubLookup.lookupKind.needsRuntimeLookup); - - GenTree* targetAddrPtr = impRuntimeLookupToTree(pResolvedToken, &callInfo->stubLookup, methHnd); - assert(!compDonotInline()); - - // This is the rough code to set up an indirect stub call - assert(targetAddrPtr != nullptr); - - // The targetAddrPtr may be a - // complex expression. As it is evaluated after the args, - // it may cause registered args to be spilled. Simply spill it. - - unsigned lclNum = lvaGrabTemp(true DEBUGARG("VirtualStaticCall with runtime lookup")); - impAssignTempGen(lclNum, targetAddrPtr, (unsigned)CHECK_SPILL_NONE); - targetAddrPtr = gtNewLclvNode(lclNum, TYP_I_IMPL); - - // Create the actual call node - - assert((sig->callConv & CORINFO_CALLCONV_MASK) != CORINFO_CALLCONV_VARARG && - (sig->callConv & CORINFO_CALLCONV_MASK) != CORINFO_CALLCONV_NATIVEVARARG); - - call = gtNewIndCallNode(targetAddrPtr, callRetTyp); - - call->gtFlags |= GTF_EXCEPT | (targetAddrPtr->gtFlags & GTF_GLOB_EFFECT); - call->gtFlags |= GTF_CALL_NONVIRT; - -#ifdef TARGET_X86 - // No tailcalls allowed for these yet... - canTailCall = false; - szCanTailCallFailReason = "VirtualStaticCall with runtime lookup"; -#endif - - break; - } - case CORINFO_VIRTUALCALL_STUB: { assert(!(mflags & CORINFO_FLG_STATIC)); // can't call a static method diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 7ddee65acaaae7..ca92f3f44d0321 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -5182,7 +5182,7 @@ void CEEInfo::getCallInfo( pResult->kind = CORINFO_VIRTUALCALL_STUB; if (fIsStaticVirtualMethod) { - pResult->kind = CORINFO_VIRTUALSTATICCALL; + pResult->kind = CORINFO_CALL_CODE_POINTER; } // We can't make stub calls when we need exact information From 326dbeced2c631eb15f840f9885020a1e7926fe1 Mon Sep 17 00:00:00 2001 From: Tomas Rylek Date: Tue, 31 May 2022 21:07:58 +0200 Subject: [PATCH 21/22] Temporarily disable the SVM diamond shape test on mono with #70040 Disable the test before the Mono support is provided. --- src/tests/issues.targets | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 227cb28d266914..76743bf3a1e8e3 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1500,6 +1500,9 @@ Tests features specific to coreclr + + https://github.com/dotnet/runtime/issues/70040 + https://github.com/dotnet/runtime/issues/40394 From 915d5ceb3b03a12dc4a94fb08a27f256b462de8e Mon Sep 17 00:00:00 2001 From: Tomas Rylek Date: Tue, 31 May 2022 21:33:09 +0200 Subject: [PATCH 22/22] Remove the issues.targets entry to see if Mono works now --- src/tests/issues.targets | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 76743bf3a1e8e3..227cb28d266914 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1500,9 +1500,6 @@ Tests features specific to coreclr - - https://github.com/dotnet/runtime/issues/70040 - https://github.com/dotnet/runtime/issues/40394