summaryrefslogtreecommitdiff
path: root/Documentation
diff options
context:
space:
mode:
authorPat Gavlin <pagavlin@microsoft.com>2017-04-21 10:26:05 -0700
committerPat Gavlin <pagavlin@microsoft.com>2017-04-25 10:10:48 -0700
commitc0987857bd44ab415d886094548590c880e12373 (patch)
tree8c3ec196ee191c26271828d8577b164934f7cb77 /Documentation
parentb5e4d03243de5fea54ac478e66e051ed19f55f6b (diff)
downloadcoreclr-c0987857bd44ab415d886094548590c880e12373.tar.gz
coreclr-c0987857bd44ab415d886094548590c880e12373.tar.bz2
coreclr-c0987857bd44ab415d886094548590c880e12373.zip
Fix fgNewBBinRegion when inserting into filters.
In order to ensure that the GC operates properly when handling exceptions, the CLR ABI requires that control exits a filter region throguh its terminal instruction. `fgNewBBinRegion` was violating this invariant, however, which lead to GC stress failures when the GC was invoked immediately after a filter finished executing but before control entered any handlers. This change fixes the behavior of `fgNewBBinRegion` when inserting new blocks into filter regions. There are two cases to consider when inserting a new BB into a filter region: 1. The filter region consists of multiple blocks 2. The filter region consists of a single block The first case is straightforward: instead of inserting a new BB after the last block of the filter, insert it before the last block of the filter. Because the filter contains multiple blocks, the predecessor of the filter must also be in the filter region. The latter case requires splitting the single block, as inserting a new block before the single block would otherwise change control flow (the filter would begin with the new block rather than the single block). In order to acheive this with minimal complexity, this change first inserts a `BBJ_ALWAYS` block that transfers control to the existing single block, then inserts a second new block immediately before the existing single block. The second new block is then returned. To put it visually, the transformation is from this: ``` filter start: BBN (BBJ_EHFILTERRET) ``` to this: ``` filter start: BBN + 1 (BBJ_ALWAYS -> BBN) BBN + 2 (jumpKind) BBN (BBJ_EHFILTERRET) ``` and the function returns `BBN + 2`. Fixes VSO 406163.
Diffstat (limited to 'Documentation')
-rw-r--r--Documentation/botr/clr-abi.md2
1 files changed, 1 insertions, 1 deletions
diff --git a/Documentation/botr/clr-abi.md b/Documentation/botr/clr-abi.md
index 8a226ed590..a85bfa4c60 100644
--- a/Documentation/botr/clr-abi.md
+++ b/Documentation/botr/clr-abi.md
@@ -470,7 +470,7 @@ When the inner "throw new UserException4" is executed, the exception handling fi
## Filter GC semantics
-Filters are invoked in the 1st pass of EH processing and as such execution might resume back at the faulting address, or in the filter-handler, or someplace else. Because the VM must allow GC's to occur during and after a filter invocation, but before the EH subsystem knows where it will resume, we need to keep everything alive at both the faulting address **and** within the filter. This is accomplished by 3 means: (1) the VM's stackwalker and GCInfoDecoder report as live both the filter frame and its corresponding parent frame, (2) the JIT encodes all stack slots that are live within the filter as being pinned, and (3) the JIT reports as live (and possible zero-initializes) anything live-out of the filter. Because of (1) it is likely that a stack variable that is live within the filter and the try body will be double reported. During the mark phase of the GC double reporting is not a problem. The problem only arises if the object is relocated: if the same location is reported twice, the GC will try to relocate the address stored at that location twice. Thus we prevent the object from being relocated by pinning it, which leads us to why we must do (2). (3) is done so that after the filter returns, we can still safely incur a GC before executing the filter-handler or any outer handler within the same frame.
+Filters are invoked in the 1st pass of EH processing and as such execution might resume back at the faulting address, or in the filter-handler, or someplace else. Because the VM must allow GC's to occur during and after a filter invocation, but before the EH subsystem knows where it will resume, we need to keep everything alive at both the faulting address **and** within the filter. This is accomplished by 3 means: (1) the VM's stackwalker and GCInfoDecoder report as live both the filter frame and its corresponding parent frame, (2) the JIT encodes all stack slots that are live within the filter as being pinned, and (3) the JIT reports as live (and possible zero-initializes) anything live-out of the filter. Because of (1) it is likely that a stack variable that is live within the filter and the try body will be double reported. During the mark phase of the GC double reporting is not a problem. The problem only arises if the object is relocated: if the same location is reported twice, the GC will try to relocate the address stored at that location twice. Thus we prevent the object from being relocated by pinning it, which leads us to why we must do (2). (3) is done so that after the filter returns, we can still safely incur a GC before executing the filter-handler or any outer handler within the same frame. For the same reason, control must exit a filter region via its final block (in other words, a filter region must terminate with the instruction that leaves the filter region, and the program may not exit the filter region via other paths).
## Duplicated Clauses