diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/SubstitutedILProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/SubstitutedILProvider.cs index db8bec02ff2b9c..8dce45e8fd8abf 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/SubstitutedILProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/SubstitutedILProvider.cs @@ -659,6 +659,67 @@ public MethodIL GetMethodILWithInlinedSubstitutions(MethodIL method) return new SubstitutedMethodIL(method.GetMethodILDefinition(), newBody, newEHRegions.ToArray(), debugInfo, newStrings.ToArray()); } + private bool TryGetMethodConstantValue(MethodDesc method, out int constant, int level = 0) + { + method = method.GetTypicalMethodDefinition(); + + TypeFlags returnType = method.Signature.ReturnType.UnderlyingType.Category; + if (returnType is < TypeFlags.Boolean or > TypeFlags.UInt32 + || method.IsIntrinsic + || _nestedILProvider.GetMethodIL(method) is not MethodIL methodIL) + { + constant = 0; + return false; + } + + var reader = new ILReader(methodIL.GetILBytes()); + var opcode = reader.ReadILOpcode(); + switch (opcode) + { + case ILOpcode.ldc_i4: constant = (int)reader.ReadILUInt32(); break; + case ILOpcode.ldc_i4_s: constant = (sbyte)reader.ReadILByte(); break; + case >= ILOpcode.ldc_i4_0 and <= ILOpcode.ldc_i4_8: constant = opcode - ILOpcode.ldc_i4_0; break; + case ILOpcode.ldc_i4_m1: constant = -1; break; + + case ILOpcode.call: + { + MethodDesc callee = (MethodDesc)methodIL.GetObject(reader.ReadILToken()); + if (reader.ReadILOpcode() != ILOpcode.ret) + { + constant = 0; + return false; + } + + BodySubstitution substitution = _substitutionProvider.GetSubstitution(method); + if (substitution != null && substitution.Value is int c) + { + constant = c; + return true; + } + + if (level > 4) + { + constant = 0; + return false; + } + + return TryGetMethodConstantValue(callee, out constant, level + 1); + } + + default: + constant = 0; + return false; + } + + if (reader.ReadILOpcode() != ILOpcode.ret) + { + constant = 0; + return false; + } + + return true; + } + private bool TryGetConstantArgument(MethodIL methodIL, byte[] body, OpcodeFlags[] flags, int offset, int argIndex, out int constant) { if ((flags[offset] & OpcodeFlags.BasicBlockStart) != 0) @@ -686,6 +747,11 @@ private bool TryGetConstantArgument(MethodIL methodIL, byte[] body, OpcodeFlags[ constant = (int)substitution.Value; return true; } + if ((opcode != ILOpcode.callvirt || !method.IsVirtual) + && TryGetMethodConstantValue(method, out constant)) + { + return true; + } else if (method.IsIntrinsic && method.Name is "get_IsValueType" or "get_IsEnum" && method.OwningType is MetadataType mdt && mdt.Name == "Type" && mdt.Namespace == "System" && mdt.Module == mdt.Context.SystemModule diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs index 2d3979ec735ee9..66e39892d27441 100644 --- a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs @@ -18,6 +18,7 @@ public static int Run() TestAbstractNeverDerivedWithDevirtualizedCall.Run(); TestAbstractDerivedByUnrelatedTypeWithDevirtualizedCall.Run(); TestUnusedDefaultInterfaceMethod.Run(); + TestInlinedDeadBranchElimination.Run(); TestArrayElementTypeOperations.Run(); TestStaticVirtualMethodOptimizations.Run(); TestTypeEquals.Run(); @@ -251,6 +252,37 @@ public static void Run() } } + class TestInlinedDeadBranchElimination + { + static int GetIntConstant() => 42; + static int GetIntConstantWrapper() => GetIntConstant(); + + class NeverReferenced1 { } + + enum MyEnum { One, Two } + + static MyEnum GetEnumConstant() => MyEnum.Two; + + class NeverReferenced2 { } + + public static void Run() + { + if (GetIntConstantWrapper() == 1) + { + Activator.CreateInstance(typeof(NeverReferenced1)); + } + + ThrowIfPresent(typeof(TestInlinedDeadBranchElimination), nameof(NeverReferenced1)); + + if (GetEnumConstant() == MyEnum.One) + { + Activator.CreateInstance(typeof(NeverReferenced2)); + } + + ThrowIfPresent(typeof(TestInlinedDeadBranchElimination), nameof(NeverReferenced2)); + } + } + class TestArrayElementTypeOperations { public static void Run()