diff --git a/src/coreclr/jit/optimizebools.cpp b/src/coreclr/jit/optimizebools.cpp index f9ca5451dd56d6..fde530b11756dc 100644 --- a/src/coreclr/jit/optimizebools.cpp +++ b/src/coreclr/jit/optimizebools.cpp @@ -1767,12 +1767,26 @@ bool Compiler::fgFoldCondToReturnBlock(BasicBlock* block) return modified; } - // Is block a BBJ_RETURN(1/0) ? (single statement) + // Is block a BBJ_RETURN(1/0) ? auto isReturnBool = [](const BasicBlock* block, bool value) { - if (block->KindIs(BBJ_RETURN) && block->hasSingleStmt() && (block->lastStmt() != nullptr)) + if (block->KindIs(BBJ_RETURN) && (block->lastStmt() != nullptr)) { GenTree* node = block->lastStmt()->GetRootNode(); - return node->OperIs(GT_RETURN) && node->gtGetOp1()->IsIntegralConst(value ? 1 : 0); + if (!(node->OperIs(GT_RETURN) && node->gtGetOp1()->IsIntegralConst(value ? 1 : 0))) + { + return false; + } + // Allow preceding statements if they have no globally visible side effects + // (e.g., dead local stores left over from inlining). + for (Statement* stmt = block->firstStmt(); stmt != block->lastStmt(); + stmt = stmt->GetNextStmt()) + { + if (GTF_GLOBALLY_VISIBLE_SIDE_EFFECTS(stmt->GetRootNode()->gtFlags)) + { + return false; + } + } + return true; } return false; }; diff --git a/src/tests/JIT/opt/OptimizeBools/Runtime_123621.cs b/src/tests/JIT/opt/OptimizeBools/Runtime_123621.cs new file mode 100644 index 00000000000000..430fdd579cf8e3 --- /dev/null +++ b/src/tests/JIT/opt/OptimizeBools/Runtime_123621.cs @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// When a constant-folded operand appears after a non-constant operand in a +// short-circuit && expression, inlining may leave dead local stores in the +// return block. fgFoldCondToReturnBlock failed to optimize this pattern +// because isReturnBool required hasSingleStmt(), which was false due to the +// leftover dead stores. + +using System; +using System.Runtime.CompilerServices; +using Xunit; + +public class Runtime_123621 +{ + [MethodImpl(MethodImplOptions.NoInlining)] + public static bool Hoisted(byte v) + { + bool isUnix = Environment.NewLine != "\r\n"; + return (v == 2 && isUnix); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static bool Inline_Before(byte v) + { + return (Environment.NewLine != "\r\n" && v == 2); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static bool Inline_After(byte v) + { + return (v == 2 && Environment.NewLine != "\r\n"); + } + + [Fact] + public static void TestEntryPoint() + { + bool isUnix = Environment.NewLine != "\r\n"; + + Assert.Equal(isUnix, Hoisted(2)); + Assert.False(Hoisted(3)); + + Assert.Equal(isUnix, Inline_Before(2)); + Assert.False(Inline_Before(3)); + + Assert.Equal(isUnix, Inline_After(2)); + Assert.False(Inline_After(3)); + + // All three methods must produce the same result. + Assert.Equal(Hoisted(2), Inline_After(2)); + Assert.Equal(Inline_Before(2), Inline_After(2)); + } +} diff --git a/src/tests/JIT/opt/OptimizeBools/Runtime_123621.csproj b/src/tests/JIT/opt/OptimizeBools/Runtime_123621.csproj new file mode 100644 index 00000000000000..b47c3e8e8d9f55 --- /dev/null +++ b/src/tests/JIT/opt/OptimizeBools/Runtime_123621.csproj @@ -0,0 +1,9 @@ + + + PdbOnly + True + + + + +