diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/FeatureSwitchManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/FeatureSwitchManager.cs index 0aa28d85d513c..600f24e82c950 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/FeatureSwitchManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/FeatureSwitchManager.cs @@ -591,6 +591,13 @@ private bool TryGetConstantArgument(MethodIL methodIL, byte[] body, OpcodeFlags[ { return true; } + else if (method.IsIntrinsic && method.Name is "get_IsValueType" + && method.OwningType is MetadataType mdt + && mdt.Name == "Type" && mdt.Namespace == "System" && mdt.Module == mdt.Context.SystemModule + && TryExpandTypeIsValueType(methodIL, body, flags, currentOffset, out constant)) + { + return true; + } else { constant = 0; @@ -745,6 +752,40 @@ private string GetResourceStringForAccessor(EcmaMethod method) return null; } + private static bool TryExpandTypeIsValueType(MethodIL methodIL, byte[] body, OpcodeFlags[] flags, int offset, out int constant) + { + // We expect to see a sequence: + // ldtoken Foo + // call GetTypeFromHandle + // -> offset points here + constant = 0; + const int SequenceLength = 10; + if (offset < SequenceLength) + return false; + + if ((flags[offset - SequenceLength] & OpcodeFlags.InstructionStart) == 0) + return false; + + ILReader reader = new ILReader(body, offset - SequenceLength); + + TypeDesc type = ReadLdToken(ref reader, methodIL, flags); + if (type == null) + return false; + + if (!ReadGetTypeFromHandle(ref reader, methodIL, flags)) + return false; + + // Dataflow runs on top of uninstantiated IL and we can't answer some questions there. + // Unfortunately this means dataflow will still see code that the rest of the system + // might have optimized away. It should not be a problem in practice. + if (type.IsSignatureVariable) + return false; + + constant = type.IsValueType ? 1 : 0; + + return true; + } + private static bool TryExpandTypeEquality(MethodIL methodIL, byte[] body, OpcodeFlags[] flags, int offset, string op, out int constant) { // We expect to see a sequence: @@ -793,37 +834,37 @@ private static bool TryExpandTypeEquality(MethodIL methodIL, byte[] body, Opcode constant ^= 1; return true; + } - static TypeDesc ReadLdToken(ref ILReader reader, MethodIL methodIL, OpcodeFlags[] flags) - { - ILOpcode opcode = reader.ReadILOpcode(); - if (opcode != ILOpcode.ldtoken) - return null; + private static TypeDesc ReadLdToken(ref ILReader reader, MethodIL methodIL, OpcodeFlags[] flags) + { + ILOpcode opcode = reader.ReadILOpcode(); + if (opcode != ILOpcode.ldtoken) + return null; - TypeDesc t = (TypeDesc)methodIL.GetObject(reader.ReadILToken()); + TypeDesc t = (TypeDesc)methodIL.GetObject(reader.ReadILToken()); - if ((flags[reader.Offset] & OpcodeFlags.BasicBlockStart) != 0) - return null; + if ((flags[reader.Offset] & OpcodeFlags.BasicBlockStart) != 0) + return null; - return t; - } + return t; + } - static bool ReadGetTypeFromHandle(ref ILReader reader, MethodIL methodIL, OpcodeFlags[] flags) - { - ILOpcode opcode = reader.ReadILOpcode(); - if (opcode != ILOpcode.call) - return false; + private static bool ReadGetTypeFromHandle(ref ILReader reader, MethodIL methodIL, OpcodeFlags[] flags) + { + ILOpcode opcode = reader.ReadILOpcode(); + if (opcode != ILOpcode.call) + return false; - MethodDesc method = (MethodDesc)methodIL.GetObject(reader.ReadILToken()); + MethodDesc method = (MethodDesc)methodIL.GetObject(reader.ReadILToken()); - if (!method.IsIntrinsic || method.Name != "GetTypeFromHandle") - return false; + if (!method.IsIntrinsic || method.Name != "GetTypeFromHandle") + return false; - if ((flags[reader.Offset] & OpcodeFlags.BasicBlockStart) != 0) - return false; + if ((flags[reader.Offset] & OpcodeFlags.BasicBlockStart) != 0) + return false; - return true; - } + return true; } private sealed class SubstitutedMethodIL : MethodIL diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs index 858ecbbbe4804..21cc8c1fb63c5 100644 --- a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs @@ -21,6 +21,7 @@ public static int Run() TestArrayElementTypeOperations.Run(); TestStaticVirtualMethodOptimizations.Run(); TestTypeEquals.Run(); + TestTypeIsValueType.Run(); TestBranchesInGenericCodeRemoval.Run(); TestUnmodifiableStaticFieldOptimization.Run(); TestUnmodifiableInstanceFieldOptimization.Run(); @@ -355,6 +356,31 @@ public static void Run() } } + class TestTypeIsValueType + { + class Never { } + + class Ever { } + + static void Generic() + { + if (typeof(T).IsValueType) + { + Activator.CreateInstance(typeof(Never)); + } + + Activator.CreateInstance(typeof(Ever)); + } + + public static void Run() + { + Generic(); + + ThrowIfPresent(typeof(TestTypeIsValueType), nameof(Never)); + ThrowIfNotPresent(typeof(TestTypeIsValueType), nameof(Ever)); + } + } + class TestBranchesInGenericCodeRemoval { class ClassWithUnusedVirtual