Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 57 additions & 100 deletions src/jit/liveness.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2235,137 +2235,95 @@ bool Compiler::fgRemoveDeadStore(GenTree** pTree,
return false;
}

/* Test for interior statement */
// Check for side effects
GenTree* sideEffList = nullptr;
if (rhsNode->gtFlags & GTF_SIDE_EFFECT)
{
#ifdef DEBUG
if (verbose)
{
printf(FMT_BB " - Dead assignment has side effects...\n", compCurBB->bbNum);
gtDispTree(asgNode);
printf("\n");
}
#endif // DEBUG
// Extract the side effects
if (rhsNode->TypeGet() == TYP_STRUCT)
{
// This is a block assignment. An indirection of the rhs is not considered to
// happen until the assignment, so we will extract the side effects from only
// the address.
if (rhsNode->OperIsIndir())
{
assert(rhsNode->OperGet() != GT_NULLCHECK);
rhsNode = rhsNode->AsIndir()->Addr();
}
}
gtExtractSideEffList(rhsNode, &sideEffList);
}

// Test for interior statement

if (asgNode->gtNext == nullptr)
{
/* This is a "NORMAL" statement with the
* assignment node hanging from the GT_STMT node */
// This is a "NORMAL" statement with the assignment node hanging from the GT_STMT node.

noway_assert(compCurStmt->gtStmtExpr == asgNode);
JITDUMP("top level assign\n");

/* Check for side effects */

if (rhsNode->gtFlags & GTF_SIDE_EFFECT)
if (sideEffList != nullptr)
{
EXTRACT_SIDE_EFFECTS:
/* Extract the side effects */

GenTree* sideEffList = nullptr;
noway_assert(sideEffList->gtFlags & GTF_SIDE_EFFECT);
#ifdef DEBUG
if (verbose)
{
printf(FMT_BB " - Dead assignment has side effects...\n", compCurBB->bbNum);
gtDispTree(asgNode);
printf("Extracted side effects list...\n");
gtDispTree(sideEffList);
printf("\n");
}
#endif // DEBUG
if (rhsNode->TypeGet() == TYP_STRUCT)
{
// This is a block assignment. An indirection of the rhs is not considered to
// happen until the assignment, so we will extract the side effects from only
// the address.
if (rhsNode->OperIsIndir())
{
assert(rhsNode->OperGet() != GT_NULLCHECK);
rhsNode = rhsNode->AsIndir()->Addr();
}
}
gtExtractSideEffList(rhsNode, &sideEffList);

if (sideEffList)
{
noway_assert(sideEffList->gtFlags & GTF_SIDE_EFFECT);
#ifdef DEBUG
if (verbose)
{
printf("Extracted side effects list...\n");
gtDispTree(sideEffList);
printf("\n");
}
#endif // DEBUG

/* Replace the assignment statement with the list of side effects */
noway_assert(sideEffList->gtOper != GT_STMT);
// Replace the assignment statement with the list of side effects
noway_assert(sideEffList->gtOper != GT_STMT);

*pTree = compCurStmt->gtStmtExpr = sideEffList;
*pTree = compCurStmt->gtStmtExpr = sideEffList;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible that pTree != &compCurStmt->gtStmtExpr?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pTree is an address of a local GenTree* variable in the caller, (e.g., address of tree in fgComputeLife.

#ifdef DEBUG
*treeModf = true;
*treeModf = true;
#endif // DEBUG
/* Update ordering, costs, FP levels, etc. */
gtSetStmtInfo(compCurStmt);

/* Re-link the nodes for this statement */
fgSetStmtSeq(compCurStmt);
// Update ordering, costs, FP levels, etc.
gtSetStmtInfo(compCurStmt);

// Since the whole statement gets replaced it is safe to
// re-thread and update order. No need to compute costs again.
*pStmtInfoDirty = false;
// Re-link the nodes for this statement
fgSetStmtSeq(compCurStmt);

/* Compute the live set for the new statement */
*doAgain = true;
return false;
}
else
{
/* No side effects, most likely we forgot to reset some flags */
fgRemoveStmt(compCurBB, compCurStmt);
// Since the whole statement gets replaced it is safe to
// re-thread and update order. No need to compute costs again.
*pStmtInfoDirty = false;

return true;
}
// Compute the live set for the new statement
*doAgain = true;
return false;
}
else
{
/* If this is GT_CATCH_ARG saved to a local var don't bother */

JITDUMP("removing stmt with no side effects\n");

if (asgNode->gtFlags & GTF_ORDER_SIDEEFF)
{
if (rhsNode->gtOper == GT_CATCH_ARG)
{
goto EXTRACT_SIDE_EFFECTS;
}
}

/* No side effects - remove the whole statement from the block->bbTreeList */

// No side effects - remove the whole statement from the block->bbTreeList
fgRemoveStmt(compCurBB, compCurStmt);

/* Since we removed it do not process the rest (i.e. RHS) of the statement
* variables in the RHS will not be marked as live, so we get the benefit of
* propagating dead variables up the chain */

// Since we removed it do not process the rest (i.e. RHS) of the statement
// variables in the RHS will not be marked as live, so we get the benefit of
// propagating dead variables up the chain
return true;
}
}
else
{
/* This is an INTERIOR STATEMENT with a dead assignment - remove it */

// This is an INTERIOR STATEMENT with a dead assignment - remove it
noway_assert(!VarSetOps::IsMember(this, life, varDsc->lvVarIndex));

if (rhsNode->gtFlags & GTF_SIDE_EFFECT)
if (sideEffList != nullptr)
{
/* :-( we have side effects */

GenTree* sideEffList = nullptr;
#ifdef DEBUG
if (verbose)
{
printf(FMT_BB " - INTERIOR dead assignment has side effects...\n", compCurBB->bbNum);
gtDispTree(asgNode);
printf("\n");
}
#endif // DEBUG
gtExtractSideEffList(rhsNode, &sideEffList);

if (!sideEffList)
{
goto NO_SIDE_EFFECTS;
}

noway_assert(sideEffList->gtFlags & GTF_SIDE_EFFECT);
#ifdef DEBUG
if (verbose)
Expand All @@ -2389,7 +2347,7 @@ bool Compiler::fgRemoveDeadStore(GenTree** pTree,
#ifdef DEBUG
*treeModf = true;
#endif // DEBUG
/* Change the node to a GT_COMMA holding the side effect list */
// Change the node to a GT_COMMA holding the side effect list
asgNode->gtBashToNOP();

asgNode->ChangeOper(GT_COMMA);
Expand All @@ -2409,7 +2367,6 @@ bool Compiler::fgRemoveDeadStore(GenTree** pTree,
}
else
{
NO_SIDE_EFFECTS:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love getting rid of gotos!!

#ifdef DEBUG
if (verbose)
{
Expand All @@ -2420,15 +2377,15 @@ bool Compiler::fgRemoveDeadStore(GenTree** pTree,
printf("\n");
}
#endif // DEBUG
/* No side effects - Change the assignment to a GT_NOP node */
// No side effects - Change the assignment to a GT_NOP node
asgNode->gtBashToNOP();

#ifdef DEBUG
*treeModf = true;
#endif // DEBUG
}

/* Re-link the nodes for this statement - Do not update ordering! */
// Re-link the nodes for this statement - Do not update ordering!

// Do not update costs by calling gtSetStmtInfo. fgSetStmtSeq modifies
// the tree threading based on the new costs. Removing nodes could
Expand All @@ -2439,7 +2396,7 @@ bool Compiler::fgRemoveDeadStore(GenTree** pTree,

fgSetStmtSeq(compCurStmt);

/* Continue analysis from this node */
// Continue analysis from this node

*pTree = asgNode;

Expand Down
57 changes: 57 additions & 0 deletions tests/src/JIT/Regression/JitBlue/GitHub_24253/GitHub_24253.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Runtime.CompilerServices;

// Test removal of a dead struct assignment when the assignment is "internal"
// i.e., is not a direct child of a statement node.
// In this example, the statement is STMT(COMMA(CALL HELPER.CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE, ASG)).

namespace GitHub_24253
{
struct TestStruct
{
public static readonly TestStruct ZeroStruct = new TestStruct(0);

[MethodImpl(MethodImplOptions.NoInlining)]
public TestStruct(int i)
{
this.i = i;
this.j = 5;
this.k = 5;
this.l = 5;
}

int i;
int j;
short k;
short l;
}

class Program
{
static int Main(string[] args)
{
GetStruct(1);
return 100;
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static TestStruct GetStruct(int i)
{
// This is the dead assignment that is causing the bug assert.
TestStruct result = TestStruct.ZeroStruct;
try
{
result = new TestStruct(i);
}
catch
{
throw new ArgumentException();
}
return result;
}
}
}
33 changes: 33 additions & 0 deletions tests/src/JIT/Regression/JitBlue/GitHub_24253/GitHub_24253.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
<OutputType>Exe</OutputType>
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
</PropertyGroup>
<!-- Default configurations to help VS understand the configurations -->
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "></PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " />
<ItemGroup>
<CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
<Visible>False</Visible>
</CodeAnalysisDependentAssemblyPaths>
</ItemGroup>
<PropertyGroup>
<DebugType>None</DebugType>
<Optimize>True</Optimize>
</PropertyGroup>
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<ItemGroup>
<Compile Include="$(MSBuildProjectName).cs" />
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
<PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "></PropertyGroup>
</Project>