Ten Tzen via llvm-dev
2020-May-03 07:15 UTC
[llvm-dev] [RFC] [Windows SEH] Local_Unwind (Jumping out of a _finally)
Hi,
Per Reid’s feedback, I have separated two SEH missing features.
This thread now is only focusing on _local_unwind(), Jumping out of _finally.
The design is documented in Wiki here:
https://github.com/tentzen/llvm-project/wiki/Windows-SEH:-Local_Unwind-(aka:-Jumping-Out-of-_Finally)
The implementation can be seen here:
https://github.com/tentzen/llvm-project/compare/SEH-LU-base...SEH-LU?expand=1
There are only 13 files changed that is much less complicated than previously
when it’s combined with -EHa.
Major code is surrounding at SehTryStmt and _Finally in CGexception.cpp that is
the place to house Windows SEH specific code anyways.
The implementation chose to lever Parser/Semantic phase (SemaStmt.cpp,
JumpDiagnostics.cpp, scope.h, etc) to identify the right _Try scope for LU
dispatching because that is the place “warn_jump_out_of_seh_finally” is
diagnosed and reported. I feel this is the most robust approach.
Local_unwind is an important feature, broadly used in Windows Kernel for all
architectures.
Without LU, Windows SEH is incomplete!
Thanks,
--Ten
From: Ten Tzen <tentzen at microsoft.com>
Sent: Friday, April 3, 2020 9:43 PM
To: rnk at google.com
Cc: llvm-dev at lists.llvm.org; Aaron Smith <aaron.smith at microsoft.com>
Subject: RE: [EXTERNAL] Re: [llvm-dev] [RFC] [Windows SEH] Local_Unwind (Jumping
out of a _finally) and -EHa (Hardware Exception Handling)
Hi, Reid,
Nice to finally meet you😊.
Thank you for reading through the doc and providing insightful feedbacks.
Yes I definitely can separate these two features if it’s more convenient for
everyone.
For now, the local_unwind specific changes can be separated and reviewed between
these two commits:
git diff 9b48ea90f4c9ae7ef030719d6c0b49b00861cdde
06c81a4b6262445432a4166627b87bf595f5291b
the -EHa changes can be read :
git diff e943329ba00772f96fbc1fe5dec836cfd0707a38
9b48ea90f4c9ae7ef030719d6c0b49b00861cdde
My reply inline below in [Ten] lines.
--Ten
From: Reid Kleckner <rnk at google.com<mailto:rnk at google.com>>
Sent: Friday, April 3, 2020 3:36 PM
To: Ten Tzen <tentzen at microsoft.com<mailto:tentzen at
microsoft.com>>
Cc: llvm-dev at lists.llvm.org<mailto:llvm-dev at lists.llvm.org>; Aaron
Smith <aaron.smith at microsoft.com<mailto:aaron.smith at
microsoft.com>>
Subject: [EXTERNAL] Re: [llvm-dev] [RFC] [Windows SEH] Local_Unwind (Jumping out
of a _finally) and -EHa (Hardware Exception Handling)
UHi Ten,
Thanks for the writeup and implementation, nice to meet you.
I wonder if it would be best to try to discuss the features separately. My view
is that catching hardware exceptions (/EHa) is critical functionality, but
it's not clear to me if local unwind is truly worth implementing. Having
looked at the code briefly, it seemed like a large portion of the complexity
comes from local unwind. Today, clang crashes on this small example that jumps
out of a __finally block, but the intention was to reject the code and avoid
implementing the functionality. Clang does, in fact, emit a warning:
$ clang -c t.cpp
t.cpp:7:7: warning: jump out of __finally block has undefined behavior
[-Wjump-seh-finally]
goto lu1;
^
Local unwind, in my view, is the user saying, "I wrote __finally, but
actually I decided I wanted to catch the exception, so let's transfer to
normal control flow now." It seems to me that the user already has a way to
express this: __except. I know the mapping isn't trivial and it's not
exactly the same, but it seems feasible to rewrite most uses of local unwind
this way.
[Ten] Right, I agree that to some degree a local_unwind can be viewed as another
type of _except handler in the middle of unwinding. And true that some usage
patterns can be worked around by rewriting SEH hierarchy. But I believe the work
can be substantial and risky, especially in an OS Kernel. Furthermore, to
broaden the interpretation, local_unwind can also serve as a _filter (or even
rethrow-like handler in C++ EH), and the target block is the final handler. See
the multi-local-unwind example in the doc.
Can you estimate the prevalence of local unwind? What percent of __finally
blocks in your experience use non-local control flow? I see a lot of value in
supporting catching hardware exceptions, but if we can avoid carrying over the
complexity of this local unwind feature, it seems to me that future generations
of compiler engineers will thank us.
[Ten] I don’t have this data in hand. But what I know is that local_unwind is an
essential feature to build Windows Kernel. One most important SEH test (the
infamous xcpt4u.c) is composed of 88 tests; among them there are 25
jumping-out-of-finally occurrences. Of course this does not translate to a
percentage of local_unwind, but it does show us the significance of this feature
to Windows. FYI Passing xcpt4u.c is the very first fundamental requirement
before building Windows Kernel.
---
Regarding trap / non-call / hardware exception handling, I guess I am a bit more
blase about precisely modeling the control flow. As Eli mentions, we already
have setjmp, and we already don't model it. Users file bugs about problems
with setjmp, and we essentially close them as "wontfix" and tell them
to put more "volatile" on the problem until it stops hurting.
One thing that I am very concerned about is the implications for basic block
layout. Right now, machine basic block layout is very free-handed. Today,
CodeGen puts labels around every potentially-throwing call, does block layout
without considering try regions, and then collapses adjacent label regions with
the same landingpad during AsmPrinting. For MSVC C++ EH, state number stores and
the ip2state table achieve the same goal.
[Ten] Yes, I saw that (pretty nice implementation actually). This design and
implementation completely inherits current mechanism except that now it’s
allowed to report EH state ranges that only contain memory/computation
instructions (for obvious reason). I’m not sure which part of that concerns
you.
I think we need rules about how LLVM is allowed to transform the following code:
void foo(volatile int *pv) {
__try {
if (cond()) {
++*pv;
__builtin_unreachable();
}
} __except(1) { }
__try {
if (cond()) {
++*pv;
__builtin_unreachable();
}
} __except(1) { }
}
In this case, the *pv operation may throw, but I believe it would be semantics
preserving to merge the two identical if-then blocks. The call.setup proposal I
sent not long ago runs into the same issue. I have written a patch to tail merge
such similar blocks, but I have not landed it:
https://reviews.llvm.org/D29428<https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Freviews.llvm.org%2FD29428&data=02%7C01%7Ctentzen%40microsoft.com%7Cac3ebdd6804a46bedefd08d7d852ac14%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637215721902320037&sdata=gnc5zhiNpq1Cv2Of0VSl7nwcS8F6uPBprFT4ffQgDx0%3D&reserved=0>
Even though it's not yet landed, I think we need to know if the transform is
valid. If it is, then we need to do more than volatilize the try region to make
EHa work.
[Ten] The merging should not happen. Per C-standard, a volatile must be read
(or write) ONCE and only once (as long as it’s naturally aligned and can be
accessed in one operation by HW). So merging two volatiles violates the
standard. I’m sure it’s currently well-protected in LLVM today.
For a long time I've wanted regions of some kind in LLVM IR, and this use
case has made me want to pick it up again. However, assuming that you want to
land support for hardware exceptions without some kind of generalized region
support in the IR, I think we do need to do something about these blocks ending
in unreachable in __try regions. The simplest thing that could possibly work is
to make clang end the try region before unreachable. This would mean ending the
block and adding `invoke void @seh_try_end` after every unreachable. It would be
redundant for noreturn calls, since those will already have an unwind edge,
ensuring they remain in the try region.
[Ten] it’s interesting you mentioned this “blocks ending in unreachable in __try
regions" here. With these two features supported, two remaining bugs in my
ToDo list are; one setjmp() and one nested EH throw. The second one seems
caused by a _try_block ended with an unreachable. Yes, this is on my list.
Will discuss with you guys further when I look into it.
---
Another interesting aspect of /EHa is how it affects C++ destructor cleanups. I
am personally comfortable with the requirement that LLVM avoid moving around
volatile instructions in __try blocks. LLVM is already required to leave
volatile operations in order. But I *am* concerned about C++ destructor scopes,
which are much more frequent than __try. As you have described it, clang would
invoke eha_scope_begin() / eha_scope_end() around the object lifetime, but are
you proposing to volatilize all memory operations in the region? If not, I see
nothing that would prevent LLVM from moving potentially faulting operations in
or out of this scope. We cannot require passes to look for non-local EH regions
before doing code motion. Would that be acceptable behavior? It could lead to
some strange behavior, where a load is sunk to the point of use outside the
cleanup region, but maybe users don't care about this in practice.
[Ten] No, memory operations in C++ need not be volatilized. The order of
exception in C++ code does not matter for -EHa. Potential trap instructions are
free to move in/out of any EH region. The only criteria is that when a HW
exception is caught and handled, local live objects must be dtored gracefully,
the same manner as C++ Synchronous exception. By reporting the EH state of
those trap instructions, this is automatically done in LLVM today.
---
To summarize, my feedback would be:
1. Focus on __try and hardware exceptions first, the value proposition is clear
and large. In particular, something has to be done about unreachable. Clang
should already thread other abnormal control flow through the region exit.
2. Please gather some data on prevalence of local unwind to motivate the feature
3. Please elaborate on the design for /EHa C++ destructor cleanups and code
motion
I hope that helps, and I'm sorry if I'm slow to respond, this is a
tricky problem, and it's not my first priority.
Reid
On Wed, Apr 1, 2020 at 8:22 AM Ten Tzen via llvm-dev <llvm-dev at
lists.llvm.org<mailto:llvm-dev at lists.llvm.org>> wrote:
Hi, all,
The intend of this thread is to complete the support for Windows SEH.
Currently there are two major missing features: Jumping out of a _finally and
Hardware exception handling.
The document below is my proposed design and implementation to fully support SEH
on LLVM.
I have completely implemented this design on a branch in repo:
https://github.com/tentzen/llvm-project<https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Ftentzen%2Fllvm-project&data=02%7C01%7Ctentzen%40microsoft.com%7Cac3ebdd6804a46bedefd08d7d852ac14%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637215721902330030&sdata=%2BC8CO9VQMv6DZk0HabsMOswQ8YFvqjdZ%2B9dUhKtjsMo%3D&reserved=0>.
It now passes MSVC’s in-house SEH suite.
Sorry for this long write-up. For better readability, please read it on
https://github.com/tentzen/llvm-project/wiki<https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Ftentzen%2Fllvm-project%2Fwiki&data=02%7C01%7Ctentzen%40microsoft.com%7Cac3ebdd6804a46bedefd08d7d852ac14%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637215721902330030&sdata=XukIQtEqSpgL13dk57%2FV2gHUw7YOwseyPy7212U7uDM%3D&reserved=0>
Special thanks to Joseph Tremoulet for his earlier comments and suggestions.
Note: I just subscribed llvm-dev, probably not in the list yet. So please reply
with my email address (tentzen at microsoft.com<mailto:tentzen at
microsoft.com>) explicitly in To-list.
Thanks,
--Ten
-------------- next part --------------
An HTML attachment was scrubbed...
URL:
<http://lists.llvm.org/pipermail/llvm-dev/attachments/20200503/5410b062/attachment.html>
Ten Tzen via llvm-dev
2020-May-15 04:49 UTC
[llvm-dev] [RFC] [Windows SEH] Local_Unwind (Jumping out of a _finally)
Any more comment or concern?
Also + at Kaylor, Andrew<mailto:andrew.kaylor at intel.com> and
@pengfei.wang at intel.com<mailto:pengfei.wang at intel.com>
Thanks,
--Ten
From: Ten Tzen <tentzen at microsoft.com>
Sent: Sunday, May 3, 2020 12:16 AM
To: rnk at google.com; llvm-dev at lists.llvm.org
Cc: Aaron Smith <aaron.smith at microsoft.com>; Joseph Tremoulet
<jotrem at microsoft.com>; Eli Friedman <efriedma at quicinc.com>
Subject: [RFC] [Windows SEH] Local_Unwind (Jumping out of a _finally)
Hi,
Per Reid’s feedback, I have separated two SEH missing features.
This thread now is only focusing on _local_unwind(), Jumping out of _finally.
The design is documented in Wiki here:
https://github.com/tentzen/llvm-project/wiki/Windows-SEH:-Local_Unwind-(aka:-Jumping-Out-of-_Finally)<https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Ftentzen%2Fllvm-project%2Fwiki%2FWindows-SEH%3A-Local_Unwind-(aka%3A-Jumping-Out-of-_Finally)&data=02%7C01%7Ctentzen%40microsoft.com%7Cd0e64871c8744c276ebf08d7ef31cf12%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637240869510467777&sdata=2EibdLkpyu6wYMunUS8U3wQ6IwgXYjh3O9LxVzuCmik%3D&reserved=0>
The implementation can be seen here:
https://github.com/tentzen/llvm-project/compare/SEH-LU-base...SEH-LU?expand=1<https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Ftentzen%2Fllvm-project%2Fcompare%2FSEH-LU-base...SEH-LU%3Fexpand%3D1&data=02%7C01%7Ctentzen%40microsoft.com%7Cd0e64871c8744c276ebf08d7ef31cf12%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637240869510477774&sdata=xY2ds9NGDTMTFBeWx87mTRUBO7cUuiRGWTomhMJa4Yc%3D&reserved=0>
There are only 13 files changed that is much less complicated than previously
when it’s combined with -EHa.
Major code is surrounding at SehTryStmt and _Finally in CGexception.cpp that is
the place to house Windows SEH specific code anyways.
The implementation chose to lever Parser/Semantic phase (SemaStmt.cpp,
JumpDiagnostics.cpp, scope.h, etc) to identify the right _Try scope for LU
dispatching because that is the place “warn_jump_out_of_seh_finally” is
diagnosed and reported. I feel this is the most robust approach.
Local_unwind is an important feature, broadly used in Windows Kernel for all
architectures.
Without LU, Windows SEH is incomplete!
Thanks,
--Ten
From: Ten Tzen <tentzen at microsoft.com<mailto:tentzen at
microsoft.com>>
Sent: Friday, April 3, 2020 9:43 PM
To: rnk at google.com<mailto:rnk at google.com>
Cc: llvm-dev at lists.llvm.org<mailto:llvm-dev at lists.llvm.org>; Aaron
Smith <aaron.smith at microsoft.com<mailto:aaron.smith at
microsoft.com>>
Subject: RE: [EXTERNAL] Re: [llvm-dev] [RFC] [Windows SEH] Local_Unwind (Jumping
out of a _finally) and -EHa (Hardware Exception Handling)
Hi, Reid,
Nice to finally meet you😊.
Thank you for reading through the doc and providing insightful feedbacks.
Yes I definitely can separate these two features if it’s more convenient for
everyone.
For now, the local_unwind specific changes can be separated and reviewed between
these two commits:
git diff 9b48ea90f4c9ae7ef030719d6c0b49b00861cdde
06c81a4b6262445432a4166627b87bf595f5291b
the -EHa changes can be read :
git diff e943329ba00772f96fbc1fe5dec836cfd0707a38
9b48ea90f4c9ae7ef030719d6c0b49b00861cdde
My reply inline below in [Ten] lines.
--Ten
From: Reid Kleckner <rnk at google.com<mailto:rnk at google.com>>
Sent: Friday, April 3, 2020 3:36 PM
To: Ten Tzen <tentzen at microsoft.com<mailto:tentzen at
microsoft.com>>
Cc: llvm-dev at lists.llvm.org<mailto:llvm-dev at lists.llvm.org>; Aaron
Smith <aaron.smith at microsoft.com<mailto:aaron.smith at
microsoft.com>>
Subject: [EXTERNAL] Re: [llvm-dev] [RFC] [Windows SEH] Local_Unwind (Jumping out
of a _finally) and -EHa (Hardware Exception Handling)
UHi Ten,
Thanks for the writeup and implementation, nice to meet you.
I wonder if it would be best to try to discuss the features separately. My view
is that catching hardware exceptions (/EHa) is critical functionality, but
it's not clear to me if local unwind is truly worth implementing. Having
looked at the code briefly, it seemed like a large portion of the complexity
comes from local unwind. Today, clang crashes on this small example that jumps
out of a __finally block, but the intention was to reject the code and avoid
implementing the functionality. Clang does, in fact, emit a warning:
$ clang -c t.cpp
t.cpp:7:7: warning: jump out of __finally block has undefined behavior
[-Wjump-seh-finally]
goto lu1;
^
Local unwind, in my view, is the user saying, "I wrote __finally, but
actually I decided I wanted to catch the exception, so let's transfer to
normal control flow now." It seems to me that the user already has a way to
express this: __except. I know the mapping isn't trivial and it's not
exactly the same, but it seems feasible to rewrite most uses of local unwind
this way.
[Ten] Right, I agree that to some degree a local_unwind can be viewed as another
type of _except handler in the middle of unwinding. And true that some usage
patterns can be worked around by rewriting SEH hierarchy. But I believe the work
can be substantial and risky, especially in an OS Kernel. Furthermore, to
broaden the interpretation, local_unwind can also serve as a _filter (or even
rethrow-like handler in C++ EH), and the target block is the final handler. See
the multi-local-unwind example in the doc.
Can you estimate the prevalence of local unwind? What percent of __finally
blocks in your experience use non-local control flow? I see a lot of value in
supporting catching hardware exceptions, but if we can avoid carrying over the
complexity of this local unwind feature, it seems to me that future generations
of compiler engineers will thank us.
[Ten] I don’t have this data in hand. But what I know is that local_unwind is an
essential feature to build Windows Kernel. One most important SEH test (the
infamous xcpt4u.c) is composed of 88 tests; among them there are 25
jumping-out-of-finally occurrences. Of course this does not translate to a
percentage of local_unwind, but it does show us the significance of this feature
to Windows. FYI Passing xcpt4u.c is the very first fundamental requirement
before building Windows Kernel.
---
Regarding trap / non-call / hardware exception handling, I guess I am a bit more
blase about precisely modeling the control flow. As Eli mentions, we already
have setjmp, and we already don't model it. Users file bugs about problems
with setjmp, and we essentially close them as "wontfix" and tell them
to put more "volatile" on the problem until it stops hurting.
One thing that I am very concerned about is the implications for basic block
layout. Right now, machine basic block layout is very free-handed. Today,
CodeGen puts labels around every potentially-throwing call, does block layout
without considering try regions, and then collapses adjacent label regions with
the same landingpad during AsmPrinting. For MSVC C++ EH, state number stores and
the ip2state table achieve the same goal.
[Ten] Yes, I saw that (pretty nice implementation actually). This design and
implementation completely inherits current mechanism except that now it’s
allowed to report EH state ranges that only contain memory/computation
instructions (for obvious reason). I’m not sure which part of that concerns
you.
I think we need rules about how LLVM is allowed to transform the following code:
void foo(volatile int *pv) {
__try {
if (cond()) {
++*pv;
__builtin_unreachable();
}
} __except(1) { }
__try {
if (cond()) {
++*pv;
__builtin_unreachable();
}
} __except(1) { }
}
In this case, the *pv operation may throw, but I believe it would be semantics
preserving to merge the two identical if-then blocks. The call.setup proposal I
sent not long ago runs into the same issue. I have written a patch to tail merge
such similar blocks, but I have not landed it:
https://reviews.llvm.org/D29428<https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Freviews.llvm.org%2FD29428&data=02%7C01%7Ctentzen%40microsoft.com%7Cd0e64871c8744c276ebf08d7ef31cf12%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637240869510477774&sdata=fvrdYeblC8XU18uV9UdfIhi23KidvDkoVycIXytlNLI%3D&reserved=0>
Even though it's not yet landed, I think we need to know if the transform is
valid. If it is, then we need to do more than volatilize the try region to make
EHa work.
[Ten] The merging should not happen. Per C-standard, a volatile must be read
(or write) ONCE and only once (as long as it’s naturally aligned and can be
accessed in one operation by HW). So merging two volatiles violates the
standard. I’m sure it’s currently well-protected in LLVM today.
For a long time I've wanted regions of some kind in LLVM IR, and this use
case has made me want to pick it up again. However, assuming that you want to
land support for hardware exceptions without some kind of generalized region
support in the IR, I think we do need to do something about these blocks ending
in unreachable in __try regions. The simplest thing that could possibly work is
to make clang end the try region before unreachable. This would mean ending the
block and adding `invoke void @seh_try_end` after every unreachable. It would be
redundant for noreturn calls, since those will already have an unwind edge,
ensuring they remain in the try region.
[Ten] it’s interesting you mentioned this “blocks ending in unreachable in __try
regions" here. With these two features supported, two remaining bugs in my
ToDo list are; one setjmp() and one nested EH throw. The second one seems
caused by a _try_block ended with an unreachable. Yes, this is on my list.
Will discuss with you guys further when I look into it.
---
Another interesting aspect of /EHa is how it affects C++ destructor cleanups. I
am personally comfortable with the requirement that LLVM avoid moving around
volatile instructions in __try blocks. LLVM is already required to leave
volatile operations in order. But I *am* concerned about C++ destructor scopes,
which are much more frequent than __try. As you have described it, clang would
invoke eha_scope_begin() / eha_scope_end() around the object lifetime, but are
you proposing to volatilize all memory operations in the region? If not, I see
nothing that would prevent LLVM from moving potentially faulting operations in
or out of this scope. We cannot require passes to look for non-local EH regions
before doing code motion. Would that be acceptable behavior? It could lead to
some strange behavior, where a load is sunk to the point of use outside the
cleanup region, but maybe users don't care about this in practice.
[Ten] No, memory operations in C++ need not be volatilized. The order of
exception in C++ code does not matter for -EHa. Potential trap instructions are
free to move in/out of any EH region. The only criteria is that when a HW
exception is caught and handled, local live objects must be dtored gracefully,
the same manner as C++ Synchronous exception. By reporting the EH state of
those trap instructions, this is automatically done in LLVM today.
---
To summarize, my feedback would be:
1. Focus on __try and hardware exceptions first, the value proposition is clear
and large. In particular, something has to be done about unreachable. Clang
should already thread other abnormal control flow through the region exit.
2. Please gather some data on prevalence of local unwind to motivate the feature
3. Please elaborate on the design for /EHa C++ destructor cleanups and code
motion
I hope that helps, and I'm sorry if I'm slow to respond, this is a
tricky problem, and it's not my first priority.
Reid
On Wed, Apr 1, 2020 at 8:22 AM Ten Tzen via llvm-dev <llvm-dev at
lists.llvm.org<mailto:llvm-dev at lists.llvm.org>> wrote:
Hi, all,
The intend of this thread is to complete the support for Windows SEH.
Currently there are two major missing features: Jumping out of a _finally and
Hardware exception handling.
The document below is my proposed design and implementation to fully support SEH
on LLVM.
I have completely implemented this design on a branch in repo:
https://github.com/tentzen/llvm-project<https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Ftentzen%2Fllvm-project&data=02%7C01%7Ctentzen%40microsoft.com%7Cd0e64871c8744c276ebf08d7ef31cf12%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637240869510487764&sdata=ZV0oGjuXtMosMGxkx%2BB9gX0yz0XVSzbS5sb12c6BZeY%3D&reserved=0>.
It now passes MSVC’s in-house SEH suite.
Sorry for this long write-up. For better readability, please read it on
https://github.com/tentzen/llvm-project/wiki<https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Ftentzen%2Fllvm-project%2Fwiki&data=02%7C01%7Ctentzen%40microsoft.com%7Cd0e64871c8744c276ebf08d7ef31cf12%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637240869510487764&sdata=LBwRpq4dZvH%2F85NTSMKSUSIw7GNrBcDoeD3kn4apatg%3D&reserved=0>
Special thanks to Joseph Tremoulet for his earlier comments and suggestions.
Note: I just subscribed llvm-dev, probably not in the list yet. So please reply
with my email address (tentzen at microsoft.com<mailto:tentzen at
microsoft.com>) explicitly in To-list.
Thanks,
--Ten
-------------- next part --------------
An HTML attachment was scrubbed...
URL:
<http://lists.llvm.org/pipermail/llvm-dev/attachments/20200515/7b0679d8/attachment-0001.html>
Kaylor, Andrew via llvm-dev
2020-May-16 01:16 UTC
[llvm-dev] [RFC] [Windows SEH] Local_Unwind (Jumping out of a _finally)
Hi Ten,
I haven’t had a chance to fully digest your design, but I read through it and it
sounds like a good direction. I’ll take a closer look next week and give you
more specific feedback.
For now, I just wanted to say that I’m very happy to see some progress being
made on this. Thanks for the RFC!
BTW, is there a reason you did not put the patch in Phabricator?
-Andy
From: Ten Tzen <tentzen at microsoft.com>
Sent: Thursday, May 14, 2020 9:50 PM
To: rnk at google.com; llvm-dev at lists.llvm.org; Kaylor, Andrew
<andrew.kaylor at intel.com>; Wang, Pengfei <pengfei.wang at
intel.com>
Cc: Aaron Smith <aaron.smith at microsoft.com>; Joseph Tremoulet
<jotrem at microsoft.com>; Eli Friedman <efriedma at quicinc.com>
Subject: RE: [RFC] [Windows SEH] Local_Unwind (Jumping out of a _finally)
Any more comment or concern?
Also + at Kaylor, Andrew<mailto:andrew.kaylor at intel.com> and
@pengfei.wang at intel.com<mailto:pengfei.wang at intel.com>
Thanks,
--Ten
From: Ten Tzen <tentzen at microsoft.com<mailto:tentzen at
microsoft.com>>
Sent: Sunday, May 3, 2020 12:16 AM
To: rnk at google.com<mailto:rnk at google.com>; llvm-dev at
lists.llvm.org<mailto:llvm-dev at lists.llvm.org>
Cc: Aaron Smith <aaron.smith at microsoft.com<mailto:aaron.smith at
microsoft.com>>; Joseph Tremoulet <jotrem at
microsoft.com<mailto:jotrem at microsoft.com>>; Eli Friedman
<efriedma at quicinc.com<mailto:efriedma at quicinc.com>>
Subject: [RFC] [Windows SEH] Local_Unwind (Jumping out of a _finally)
Hi,
Per Reid’s feedback, I have separated two SEH missing features.
This thread now is only focusing on _local_unwind(), Jumping out of _finally.
The design is documented in Wiki here:
https://github.com/tentzen/llvm-project/wiki/Windows-SEH:-Local_Unwind-(aka:-Jumping-Out-of-_Finally)<https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Ftentzen%2Fllvm-project%2Fwiki%2FWindows-SEH%3A-Local_Unwind-(aka%3A-Jumping-Out-of-_Finally)&data=02%7C01%7Ctentzen%40microsoft.com%7Cd0e64871c8744c276ebf08d7ef31cf12%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637240869510467777&sdata=2EibdLkpyu6wYMunUS8U3wQ6IwgXYjh3O9LxVzuCmik%3D&reserved=0>
The implementation can be seen here:
https://github.com/tentzen/llvm-project/compare/SEH-LU-base...SEH-LU?expand=1<https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Ftentzen%2Fllvm-project%2Fcompare%2FSEH-LU-base...SEH-LU%3Fexpand%3D1&data=02%7C01%7Ctentzen%40microsoft.com%7Cd0e64871c8744c276ebf08d7ef31cf12%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637240869510477774&sdata=xY2ds9NGDTMTFBeWx87mTRUBO7cUuiRGWTomhMJa4Yc%3D&reserved=0>
There are only 13 files changed that is much less complicated than previously
when it’s combined with -EHa.
Major code is surrounding at SehTryStmt and _Finally in CGexception.cpp that is
the place to house Windows SEH specific code anyways.
The implementation chose to lever Parser/Semantic phase (SemaStmt.cpp,
JumpDiagnostics.cpp, scope.h, etc) to identify the right _Try scope for LU
dispatching because that is the place “warn_jump_out_of_seh_finally” is
diagnosed and reported. I feel this is the most robust approach.
Local_unwind is an important feature, broadly used in Windows Kernel for all
architectures.
Without LU, Windows SEH is incomplete!
Thanks,
--Ten
From: Ten Tzen <tentzen at microsoft.com<mailto:tentzen at
microsoft.com>>
Sent: Friday, April 3, 2020 9:43 PM
To: rnk at google.com<mailto:rnk at google.com>
Cc: llvm-dev at lists.llvm.org<mailto:llvm-dev at lists.llvm.org>; Aaron
Smith <aaron.smith at microsoft.com<mailto:aaron.smith at
microsoft.com>>
Subject: RE: [EXTERNAL] Re: [llvm-dev] [RFC] [Windows SEH] Local_Unwind (Jumping
out of a _finally) and -EHa (Hardware Exception Handling)
Hi, Reid,
Nice to finally meet you😊.
Thank you for reading through the doc and providing insightful feedbacks.
Yes I definitely can separate these two features if it’s more convenient for
everyone.
For now, the local_unwind specific changes can be separated and reviewed between
these two commits:
git diff 9b48ea90f4c9ae7ef030719d6c0b49b00861cdde
06c81a4b6262445432a4166627b87bf595f5291b
the -EHa changes can be read :
git diff e943329ba00772f96fbc1fe5dec836cfd0707a38
9b48ea90f4c9ae7ef030719d6c0b49b00861cdde
My reply inline below in [Ten] lines.
--Ten
From: Reid Kleckner <rnk at google.com<mailto:rnk at google.com>>
Sent: Friday, April 3, 2020 3:36 PM
To: Ten Tzen <tentzen at microsoft.com<mailto:tentzen at
microsoft.com>>
Cc: llvm-dev at lists.llvm.org<mailto:llvm-dev at lists.llvm.org>; Aaron
Smith <aaron.smith at microsoft.com<mailto:aaron.smith at
microsoft.com>>
Subject: [EXTERNAL] Re: [llvm-dev] [RFC] [Windows SEH] Local_Unwind (Jumping out
of a _finally) and -EHa (Hardware Exception Handling)
UHi Ten,
Thanks for the writeup and implementation, nice to meet you.
I wonder if it would be best to try to discuss the features separately. My view
is that catching hardware exceptions (/EHa) is critical functionality, but
it's not clear to me if local unwind is truly worth implementing. Having
looked at the code briefly, it seemed like a large portion of the complexity
comes from local unwind. Today, clang crashes on this small example that jumps
out of a __finally block, but the intention was to reject the code and avoid
implementing the functionality. Clang does, in fact, emit a warning:
$ clang -c t.cpp
t.cpp:7:7: warning: jump out of __finally block has undefined behavior
[-Wjump-seh-finally]
goto lu1;
^
Local unwind, in my view, is the user saying, "I wrote __finally, but
actually I decided I wanted to catch the exception, so let's transfer to
normal control flow now." It seems to me that the user already has a way to
express this: __except. I know the mapping isn't trivial and it's not
exactly the same, but it seems feasible to rewrite most uses of local unwind
this way.
[Ten] Right, I agree that to some degree a local_unwind can be viewed as another
type of _except handler in the middle of unwinding. And true that some usage
patterns can be worked around by rewriting SEH hierarchy. But I believe the work
can be substantial and risky, especially in an OS Kernel. Furthermore, to
broaden the interpretation, local_unwind can also serve as a _filter (or even
rethrow-like handler in C++ EH), and the target block is the final handler. See
the multi-local-unwind example in the doc.
Can you estimate the prevalence of local unwind? What percent of __finally
blocks in your experience use non-local control flow? I see a lot of value in
supporting catching hardware exceptions, but if we can avoid carrying over the
complexity of this local unwind feature, it seems to me that future generations
of compiler engineers will thank us.
[Ten] I don’t have this data in hand. But what I know is that local_unwind is an
essential feature to build Windows Kernel. One most important SEH test (the
infamous xcpt4u.c) is composed of 88 tests; among them there are 25
jumping-out-of-finally occurrences. Of course this does not translate to a
percentage of local_unwind, but it does show us the significance of this feature
to Windows. FYI Passing xcpt4u.c is the very first fundamental requirement
before building Windows Kernel.
---
Regarding trap / non-call / hardware exception handling, I guess I am a bit more
blase about precisely modeling the control flow. As Eli mentions, we already
have setjmp, and we already don't model it. Users file bugs about problems
with setjmp, and we essentially close them as "wontfix" and tell them
to put more "volatile" on the problem until it stops hurting.
One thing that I am very concerned about is the implications for basic block
layout. Right now, machine basic block layout is very free-handed. Today,
CodeGen puts labels around every potentially-throwing call, does block layout
without considering try regions, and then collapses adjacent label regions with
the same landingpad during AsmPrinting. For MSVC C++ EH, state number stores and
the ip2state table achieve the same goal.
[Ten] Yes, I saw that (pretty nice implementation actually). This design and
implementation completely inherits current mechanism except that now it’s
allowed to report EH state ranges that only contain memory/computation
instructions (for obvious reason). I’m not sure which part of that concerns
you.
I think we need rules about how LLVM is allowed to transform the following code:
void foo(volatile int *pv) {
__try {
if (cond()) {
++*pv;
__builtin_unreachable();
}
} __except(1) { }
__try {
if (cond()) {
++*pv;
__builtin_unreachable();
}
} __except(1) { }
}
In this case, the *pv operation may throw, but I believe it would be semantics
preserving to merge the two identical if-then blocks. The call.setup proposal I
sent not long ago runs into the same issue. I have written a patch to tail merge
such similar blocks, but I have not landed it:
https://reviews.llvm.org/D29428<https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Freviews.llvm.org%2FD29428&data=02%7C01%7Ctentzen%40microsoft.com%7Cd0e64871c8744c276ebf08d7ef31cf12%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637240869510477774&sdata=fvrdYeblC8XU18uV9UdfIhi23KidvDkoVycIXytlNLI%3D&reserved=0>
Even though it's not yet landed, I think we need to know if the transform is
valid. If it is, then we need to do more than volatilize the try region to make
EHa work.
[Ten] The merging should not happen. Per C-standard, a volatile must be read
(or write) ONCE and only once (as long as it’s naturally aligned and can be
accessed in one operation by HW). So merging two volatiles violates the
standard. I’m sure it’s currently well-protected in LLVM today.
For a long time I've wanted regions of some kind in LLVM IR, and this use
case has made me want to pick it up again. However, assuming that you want to
land support for hardware exceptions without some kind of generalized region
support in the IR, I think we do need to do something about these blocks ending
in unreachable in __try regions. The simplest thing that could possibly work is
to make clang end the try region before unreachable. This would mean ending the
block and adding `invoke void @seh_try_end` after every unreachable. It would be
redundant for noreturn calls, since those will already have an unwind edge,
ensuring they remain in the try region.
[Ten] it’s interesting you mentioned this “blocks ending in unreachable in __try
regions" here. With these two features supported, two remaining bugs in my
ToDo list are; one setjmp() and one nested EH throw. The second one seems
caused by a _try_block ended with an unreachable. Yes, this is on my list.
Will discuss with you guys further when I look into it.
---
Another interesting aspect of /EHa is how it affects C++ destructor cleanups. I
am personally comfortable with the requirement that LLVM avoid moving around
volatile instructions in __try blocks. LLVM is already required to leave
volatile operations in order. But I *am* concerned about C++ destructor scopes,
which are much more frequent than __try. As you have described it, clang would
invoke eha_scope_begin() / eha_scope_end() around the object lifetime, but are
you proposing to volatilize all memory operations in the region? If not, I see
nothing that would prevent LLVM from moving potentially faulting operations in
or out of this scope. We cannot require passes to look for non-local EH regions
before doing code motion. Would that be acceptable behavior? It could lead to
some strange behavior, where a load is sunk to the point of use outside the
cleanup region, but maybe users don't care about this in practice.
[Ten] No, memory operations in C++ need not be volatilized. The order of
exception in C++ code does not matter for -EHa. Potential trap instructions are
free to move in/out of any EH region. The only criteria is that when a HW
exception is caught and handled, local live objects must be dtored gracefully,
the same manner as C++ Synchronous exception. By reporting the EH state of
those trap instructions, this is automatically done in LLVM today.
---
To summarize, my feedback would be:
1. Focus on __try and hardware exceptions first, the value proposition is clear
and large. In particular, something has to be done about unreachable. Clang
should already thread other abnormal control flow through the region exit.
2. Please gather some data on prevalence of local unwind to motivate the feature
3. Please elaborate on the design for /EHa C++ destructor cleanups and code
motion
I hope that helps, and I'm sorry if I'm slow to respond, this is a
tricky problem, and it's not my first priority.
Reid
On Wed, Apr 1, 2020 at 8:22 AM Ten Tzen via llvm-dev <llvm-dev at
lists.llvm.org<mailto:llvm-dev at lists.llvm.org>> wrote:
Hi, all,
The intend of this thread is to complete the support for Windows SEH.
Currently there are two major missing features: Jumping out of a _finally and
Hardware exception handling.
The document below is my proposed design and implementation to fully support SEH
on LLVM.
I have completely implemented this design on a branch in repo:
https://github.com/tentzen/llvm-project<https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Ftentzen%2Fllvm-project&data=02%7C01%7Ctentzen%40microsoft.com%7Cd0e64871c8744c276ebf08d7ef31cf12%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637240869510487764&sdata=ZV0oGjuXtMosMGxkx%2BB9gX0yz0XVSzbS5sb12c6BZeY%3D&reserved=0>.
It now passes MSVC’s in-house SEH suite.
Sorry for this long write-up. For better readability, please read it on
https://github.com/tentzen/llvm-project/wiki<https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Ftentzen%2Fllvm-project%2Fwiki&data=02%7C01%7Ctentzen%40microsoft.com%7Cd0e64871c8744c276ebf08d7ef31cf12%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637240869510487764&sdata=LBwRpq4dZvH%2F85NTSMKSUSIw7GNrBcDoeD3kn4apatg%3D&reserved=0>
Special thanks to Joseph Tremoulet for his earlier comments and suggestions.
Note: I just subscribed llvm-dev, probably not in the list yet. So please reply
with my email address (tentzen at microsoft.com<mailto:tentzen at
microsoft.com>) explicitly in To-list.
Thanks,
--Ten
-------------- next part --------------
An HTML attachment was scrubbed...
URL:
<http://lists.llvm.org/pipermail/llvm-dev/attachments/20200516/e98aad95/attachment.html>
Seemingly Similar Threads
- [RFC] [Windows SEH][-EHa] Support Hardware Exception Handling
- [RFC] [Windows SEH] Local_Unwind (Jumping out of a _finally) and -EHa (Hardware Exception Handling)
- [RFC] [Windows SEH][-EHa] Support Hardware Exception Handling
- [RFC] [Windows SEH] Local_Unwind (Jumping out of a _finally) and -EHa (Hardware Exception Handling)
- [RFC] [Windows SEH] Local_Unwind (Jumping out of a _finally) and -EHa (Hardware Exception Handling)