Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Commit

Permalink
Merge pull request #23138 from AndyAyersMS/Port23044To2.1
Browse files Browse the repository at this point in the history
JIT: fix filter liveness computation
  • Loading branch information
BruceForstall authored Mar 14, 2019
2 parents 0184f75 + 73b3489 commit 9848919
Show file tree
Hide file tree
Showing 3 changed files with 201 additions and 5 deletions.
113 changes: 108 additions & 5 deletions src/jit/liveness.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1104,6 +1104,42 @@ void Compiler::fgExtendDbgLifetimes()
#endif // DEBUG
}

//------------------------------------------------------------------------
// fgGetHandlerLiveVars: determine set of locals live because of implicit
// exception flow from a block.
//
// Arguments:
// block - the block in question
//
// Returns:
// Additional set of locals to be considered live throughout the block.
//
// Notes:
// Assumes caller has screened candidate blocks to only those with
// exception flow, via `ehBlockHasExnFlowDsc`.
//
// Exception flow can arise because of a newly raised exception (for
// blocks within try regions) or because of an actively propagating exception
// (for filter blocks). This flow effectively creates additional successor
// edges in the flow graph that the jit does not model. This method computes
// the net contribution from all the missing successor edges.
//
// For example, with the following C# source, during EH processing of the throw,
// the outer filter will execute in pass1, before the inner handler executes
// in pass2, and so the filter blocks should show the inner handler's local is live.
//
// try
// {
// using (AllocateObject()) // ==> try-finally; handler calls Dispose
// {
// throw new Exception();
// }
// }
// catch (Exception e1) when (IsExpectedException(e1))
// {
// Console.WriteLine("In catch 1");
// }

VARSET_VALRET_TP Compiler::fgGetHandlerLiveVars(BasicBlock* block)
{
noway_assert(block);
Expand All @@ -1115,7 +1151,6 @@ VARSET_VALRET_TP Compiler::fgGetHandlerLiveVars(BasicBlock* block)
do
{
/* Either we enter the filter first or the catch/finally */

if (HBtab->HasFilter())
{
VarSetOps::UnionD(this, liveVars, HBtab->ebdFilter->bbLiveIn);
Expand Down Expand Up @@ -1147,6 +1182,72 @@ VARSET_VALRET_TP Compiler::fgGetHandlerLiveVars(BasicBlock* block)

} while (true);

// If this block is within a filter, we also need to report as live
// any vars live into enclosed finally or fault handlers, since the
// filter will run during the first EH pass, and enclosed or enclosing
// handlers will run during the second EH pass. So all these handlers
// are "exception flow" successors of the filter.
//
// Note we are relying on ehBlockHasExnFlowDsc to return true
// for any filter block that we should examine here.
if (block->hasHndIndex())
{
const unsigned thisHndIndex = block->getHndIndex();
EHblkDsc* enclosingHBtab = ehGetDsc(thisHndIndex);

if (enclosingHBtab->InFilterRegionBBRange(block))
{
assert(enclosingHBtab->HasFilter());

// Search the EH table for enclosed regions.
//
// All the enclosed regions will be lower numbered and
// immediately prior to and contiguous with the enclosing
// region in the EH tab.
unsigned index = thisHndIndex;

while (index > 0)
{
index--;
unsigned enclosingIndex = ehGetEnclosingTryIndex(index);
bool isEnclosed = false;

// To verify this is an enclosed region, search up
// through the enclosing regions until we find the
// region associated with the filter.
while (enclosingIndex != EHblkDsc::NO_ENCLOSING_INDEX)
{
if (enclosingIndex == thisHndIndex)
{
isEnclosed = true;
break;
}

enclosingIndex = ehGetEnclosingTryIndex(enclosingIndex);
}

// If we found an enclosed region, check if the region
// is a try fault or try finally, and if so, add any
// locals live into the enclosed region's handler into this
// block's live-in set.
if (isEnclosed)
{
EHblkDsc* enclosedHBtab = ehGetDsc(index);

if (enclosedHBtab->HasFinallyOrFaultHandler())
{
VarSetOps::UnionD(this, liveVars, enclosedHBtab->ebdHndBeg->bbLiveIn);
}
}
// Once we run across a non-enclosed region, we can stop searching.
else
{
break;
}
}
}
}

return liveVars;
}

Expand Down Expand Up @@ -1219,14 +1320,17 @@ class LiveVarAnalysis
// since (without proof otherwise) the use and def may touch different memory at run-time.
m_memoryLiveIn = m_memoryLiveOut | block->bbMemoryUse;

/* Can exceptions from this block be handled (in this function)? */

// Does this block have implicit exception flow to a filter or handler?
// If so, include the effects of that flow.
if (m_compiler->ehBlockHasExnFlowDsc(block))
{
const VARSET_TP& liveVars(m_compiler->fgGetHandlerLiveVars(block));

VarSetOps::UnionD(m_compiler, m_liveIn, liveVars);
VarSetOps::UnionD(m_compiler, m_liveOut, liveVars);

// Implicit eh edges can induce loop-like behavior,
// so make sure we iterate to closure.
m_hasPossibleBackEdge = true;
}

/* Has there been any change in either live set? */
Expand Down Expand Up @@ -3093,7 +3197,6 @@ void Compiler::fgInterBlockLocalVarLiveness()
if (block->bbCatchTyp != BBCT_NONE)
{
/* Note the set of variables live on entry to exception handler */

VarSetOps::UnionD(this, exceptVars, block->bbLiveIn);
}

Expand Down
60 changes: 60 additions & 0 deletions tests/src/JIT/Regression/JitBlue/GitHub_22820/GitHub_22820.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// 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;

// Repro for issue 22820. On x86 we need to report enclosed handler
// live-in locals as live into any enclosing filter.
//
// Run with optimized codegen and COMPlus_GCStress=0x4

class DisposableObject : IDisposable
{
public void Dispose()
{
Console.WriteLine("In dispose");
}
}

class Program
{
public static bool IsExpectedException(Exception e)
{
Console.WriteLine("In filter");
GC.Collect();
return e is OperationCanceledException;
}

public static IDisposable AllocateObject()
{
return new DisposableObject();
}

static int Main(string[] args)
{
int result = 0;

try
{
try
{
using (AllocateObject())
{
throw new Exception();
}
}
catch (Exception e1) when (IsExpectedException(e1))
{
Console.WriteLine("In catch 1");
}
}
catch (Exception e2)
{
Console.WriteLine("In catch 2");
result = 100;
}

return result;
}
}
33 changes: 33 additions & 0 deletions tests/src/JIT/Regression/JitBlue/GitHub_22820/GitHub_22820.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>{2649FAFE-07BF-4F93-8120-BA9A69285ABB}</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' "></PropertyGroup>
<PropertyGroup>
<DebugType>None</DebugType>
<Optimize>True</Optimize>
</PropertyGroup>
<ItemGroup>
<CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
<Visible>False</Visible>
</CodeAnalysisDependentAssemblyPaths>
</ItemGroup>
<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>

0 comments on commit 9848919

Please sign in to comment.