From e92a39293bd738d6c9fe20adbc0ae720f976416b Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Thu, 10 Sep 2026 15:10:53 -0700 Subject: [PATCH] =?UTF-8?q?Revert=20"Fold=20RuntimeInformation.IsOSPlatfor?= =?UTF-8?q?m=20/=20OperatingSystem.IsOSPlatform=20i=E2=80=A6"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit b50ab6f8755da35982794c5f085a859a0df20bc5. --- .../ILTrim.Tests/ILTrimExpectedFailures.txt | 1 - .../UnreachableBlocksOptimizer.cs | 90 ------------------- .../UnreachableBlockTests.g.cs | 6 -- .../UnreachableBlock/OSPlatformGuard.cs | 89 ------------------ 4 files changed, 186 deletions(-) delete mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/UnreachableBlock/OSPlatformGuard.cs diff --git a/src/coreclr/tools/ILTrim.Tests/ILTrimExpectedFailures.txt b/src/coreclr/tools/ILTrim.Tests/ILTrimExpectedFailures.txt index 9c2ff8298099f4..a872f2dffdec3f 100644 --- a/src/coreclr/tools/ILTrim.Tests/ILTrimExpectedFailures.txt +++ b/src/coreclr/tools/ILTrim.Tests/ILTrimExpectedFailures.txt @@ -463,7 +463,6 @@ UnreachableBlock.MethodArgumentPropagation UnreachableBlock.MethodWithParametersSubstitutions UnreachableBlock.MultiStageRemoval UnreachableBlock.NestedFinallyInFinallyHandler -UnreachableBlock.OSPlatformGuard UnreachableBlock.ReplacedJumpTarget UnreachableBlock.ReplacedReturns UnreachableBlock.ResultInliningNotPossible diff --git a/src/tools/illink/src/linker/Linker.Steps/UnreachableBlocksOptimizer.cs b/src/tools/illink/src/linker/Linker.Steps/UnreachableBlocksOptimizer.cs index ef4fef6b5c50bb..784f44248b2115 100644 --- a/src/tools/illink/src/linker/Linker.Steps/UnreachableBlocksOptimizer.cs +++ b/src/tools/illink/src/linker/Linker.Steps/UnreachableBlocksOptimizer.cs @@ -898,83 +898,6 @@ public bool RewriteBody() return true; } - // Recognizes the OS platform-guard idiom and evaluates it to a constant. Unlike the - // OperatingSystem.IsX() predicates (per-target literals the generic analyzer already folds), - // IsOSPlatform routes through an instance String.Equals and, for the RuntimeInformation - // overload, an opaque OSPlatform struct, so it needs direct handling. The recognized platform - // token is mapped to its OperatingSystem.IsX() predicate, which is then folded normally -- that - // predicate also encodes the OSX/MacCatalyst aliasing, so no manual replication is needed. - bool TryEvaluateOSPlatformGuard(in UnreachableBlocksOptimizer optimizer, MethodDefinition md, Collection instructions, int callIndex, [NotNullWhen(true)] out Instruction? result) - { - result = null; - - if (md.Name != "IsOSPlatform" || !md.IsStatic || md.GetMetadataParametersCount() != 1 || callIndex < 1) - return false; - - bool isRuntimeInformation = md.DeclaringType.IsTypeOf("System.Runtime.InteropServices", "RuntimeInformation"); - bool isOperatingSystem = md.DeclaringType.IsTypeOf("System", "OperatingSystem"); - if (!isRuntimeInformation && !isOperatingSystem) - return false; - - Instruction argProducer = instructions[callIndex - 1]; - string? token; - - if (isOperatingSystem) - { - // OperatingSystem.IsOSPlatform(string) with a literal platform name. - token = argProducer.OpCode.Code == Code.Ldstr ? (string)argProducer.Operand : null; - } - else - { - // RuntimeInformation.IsOSPlatform(OSPlatform) built from a well-known OSPlatform value. - if (argProducer.OpCode.Code != Code.Call) - return false; - - MethodDefinition? producer = context.TryResolve((MethodReference)argProducer.Operand); - if (producer == null || !producer.DeclaringType.IsTypeOf("System.Runtime.InteropServices", "OSPlatform")) - return false; - - token = producer.Name switch - { - "get_Windows" => "WINDOWS", - "get_Linux" => "LINUX", - "get_OSX" => "OSX", - "get_FreeBSD" => "FREEBSD", - "Create" when callIndex >= 2 && instructions[callIndex - 2].OpCode.Code == Code.Ldstr - => (string)instructions[callIndex - 2].Operand, - _ => null, - }; - } - - string? predicate = token?.ToUpperInvariant() switch - { - "WINDOWS" => "IsWindows", - "LINUX" => "IsLinux", - "OSX" or "MACOS" => "IsMacOS", - "BROWSER" => "IsBrowser", - "WASI" => "IsWasi", - "ANDROID" => "IsAndroid", - "IOS" => "IsIOS", - "MACCATALYST" => "IsMacCatalyst", - "TVOS" => "IsTvOS", - "WATCHOS" => "IsWatchOS", - "FREEBSD" => "IsFreeBSD", - _ => null, // unknown/custom platform: leave the guard untouched - }; - if (predicate == null) - return false; - - TypeDefinition? operatingSystem = md.DeclaringType.Module.GetType("System.OperatingSystem"); - MethodDefinition? isPlatform = operatingSystem?.Methods.FirstOrDefault( - m => m.Name == predicate && m.IsStatic && m.HasBody && m.GetMetadataParametersCount() == 0); - if (isPlatform == null) - return false; - - // The predicate body is a per-target 'return true/false', which the constant analyzer folds. - result = optimizer.TryGetMethodCallResult(new CalleePayload(isPlatform, Array.Empty()))?.Instruction; - return result != null; - } - public bool ApplyTemporaryInlining(in UnreachableBlocksOptimizer optimizer) { bool changed = false; @@ -997,19 +920,6 @@ public bool ApplyTemporaryInlining(in UnreachableBlocksOptimizer optimizer) if (md.IsVirtual || md.CallingConvention == MethodCallingConvention.VarArg) break; - // RuntimeInformation.IsOSPlatform(OSPlatform.X) / OperatingSystem.IsOSPlatform("X") - // don't fold through the generic constant analyzer (the argument flows through an - // opaque OSPlatform struct or an instance String.Equals). Recognize the guard idiom - // directly so its dead branch (and any foreign P/Invoke it protects) can be trimmed. - if (context.IsOptimizationEnabled(CodeOptimizations.IPConstantPropagation, Body.Method) - && TryEvaluateOSPlatformGuard(optimizer, md, instructions, i, out Instruction? osGuardResult)) - { - RewriteToNop(i - 1, 1); - Rewrite(i, osGuardResult); - changed = true; - break; - } - Instruction[]? args = GetArgumentsOnStack(md, FoldedInstructions ?? instructions, i); targetResult = args?.Length > 0 && md.IsStatic ? EvaluateIntrinsicCall(md, args) : null; diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/UnreachableBlockTests.g.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/UnreachableBlockTests.g.cs index 2b0953f5519a2a..f9ce05416fcc8d 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/UnreachableBlockTests.g.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/UnreachableBlockTests.g.cs @@ -67,12 +67,6 @@ public Task MultiStageRemoval() return RunTest(allowMissingWarnings: true); } - [Fact] - public Task OSPlatformGuard() - { - return RunTest(allowMissingWarnings: true); - } - [Fact] public Task ReplacedJumpTarget() { diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/UnreachableBlock/OSPlatformGuard.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/UnreachableBlock/OSPlatformGuard.cs deleted file mode 100644 index 5ad02d329cdff2..00000000000000 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/UnreachableBlock/OSPlatformGuard.cs +++ /dev/null @@ -1,89 +0,0 @@ -using System.Runtime.InteropServices; -using Mono.Linker.Tests.Cases.Expectations.Assertions; -using Mono.Linker.Tests.Cases.Expectations.Metadata; - -namespace Mono.Linker.Tests.Cases.UnreachableBlock -{ - [SetupCSharpCompilerToUse("csc")] - [SetupCompileArgument("/optimize+")] - [SetupLinkerArgument("--enable-opt", "ipconstprop")] - public class OSPlatformGuard - { - public static void Main() - { - TestRuntimeInformationIsOSPlatform(); - TestRuntimeInformationCreate(); - TestOperatingSystemIsOSPlatform(); - - // Keep every branch target reachable independently so their retention is platform-independent. - // The guards above are still folded to a constant, asserted via ExpectBodyModified (without the - // platform-guard folding IsOSPlatform never folds and the bodies would be unchanged). - Windows(); - NotWindows(); - Linux(); - NotLinux(); - FreeBSD(); - NotFreeBSD(); - } - - [Kept] - [ExpectBodyModified] - static void TestRuntimeInformationIsOSPlatform() - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - Windows(); - else - NotWindows(); - } - - [Kept] - [ExpectBodyModified] - static void TestRuntimeInformationCreate() - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("FREEBSD"))) - FreeBSD(); - else - NotFreeBSD(); - } - - [Kept] - [ExpectBodyModified] - static void TestOperatingSystemIsOSPlatform() - { - if (System.OperatingSystem.IsOSPlatform("Linux")) - Linux(); - else - NotLinux(); - } - - [Kept] - static void Windows() - { - } - - [Kept] - static void NotWindows() - { - } - - [Kept] - static void Linux() - { - } - - [Kept] - static void NotLinux() - { - } - - [Kept] - static void FreeBSD() - { - } - - [Kept] - static void NotFreeBSD() - { - } - } -}