-
Notifications
You must be signed in to change notification settings - Fork 199
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix unwind info for cold EH funclets on ARM64 #1930
Fix unwind info for cold EH funclets on ARM64 #1930
Conversation
@cshung @BruceForstall PTAL, thank you! |
Is there some explanation for this? |
I do not know the answer to this, but perhaps chained unwind info was never considered for ARM64 since hot/cold splitting wasn't implemented previously. It may be more complicated to implement here since we split each function/funclet into 512KB fragments, but the current crossgen/VM implementation relies on the use of chained unwind info, so this would improve portability. |
There's no concept of chained unwind for arm64. Every function fragment (including "whole" functions) gets its own unique and separate RUNTIME_FUNCTION (.pdata) and .xdata entry (we never use the compact form; we always have .xdata). Funclets have both a prolog and epilog, so have both kinds of unwind codes. Fragments without a prolog have a "phantom" prolog. There is no reference from one to another. See https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling for details. Chained unwind info for x64 is described here: https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64 |
nit: btw, fragments on arm64 are 1MB; on arm32 they are 512KB. |
src/coreclr/jit/unwind.cpp
Outdated
if (isFunclet) | ||
{ | ||
eeAllocUnwindInfo((BYTE*)pHotCode, (BYTE*)pColdCode, startOffset, endOffset, unwindCodeBytes, pUnwindBlock, | ||
(CorJitFuncKind)func->funKind); | ||
} | ||
else | ||
{ | ||
// If the function is split, EH funclets are always cold, so pass in pColdCode to indicate so. | ||
eeAllocUnwindInfo((BYTE*)pHotCode, nullptr /* pColdCode */, startOffset, endOffset, unwindCodeBytes, | ||
pUnwindBlock, (CorJitFuncKind)func->funKind); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused by this:
- unwindEmitFuncCFI() should never get called, so why update it?
- Do we have no incoming (function argument) indication if the funclet has a hot part? I guess we're currently assuming a funclet is always all cold. In that case, shouldn't we just skip the entire "hot" part here, and let the cold code below execute instead?
- It seems confusing to me that the "else" branch comment isn't in the "if" part instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The cold code path below seems to be specifically for chained unwind info (see unwindCodeBytes = 0
, pUnwindBlock = nullptr
). I thought I would lay the groundwork here for if/when we enable hot/cold splitting on NativeAOT, though it's probably better we leave it unimplemented until we start testing it. I'll remove it.
src/coreclr/jit/unwindarm.cpp
Outdated
else | ||
{ | ||
// If the function is split, EH funclets are always cold. | ||
const bool isHotCode = (fgFirstColdBlock == nullptr); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const bool isHotCode = (fgFirstColdBlock == nullptr); | |
const bool isHotCode = (func->funKind == FUNC_ROOT) || (fgFirstColdBlock == nullptr); |
then get rid of the if
condition.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for catching that. This logic will probably be removed altogether in my next refactor, though.
src/coreclr/jit/unwindarm.cpp
Outdated
// func->uwiCold is used only for the cold part of the main function, | ||
// and is always NULL for funclets. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's something confusing about the model here. In particular, it looks like cold funclets have their unwind info in the "hot" uwi
structure instead of having an empty uwi
and putting their unwind info in the uwiCold
structure.
Wouldn't it be better to introduce a notion of the "hot" unwind info (in uwi
) being empty, if the entire funclet is cold?
One of the things I'm worried about here is distributing the logic about "if a function with EH is split then all funclets are cold" instead of just setting up the FuncInfoDsc with the right information and simply iterating over it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. I was thinking it would make more sense to use uwiCold
for cold EH funclets, but the current implementation always utilizes uwi
(and thus makes the assumption uwi
has unwind info), so this may require some non-trivial refactoring. If we're okay with the higher churn, I can try this out.
Edit: I kept uwi
initialized for cold funclets and just initialized uwiCold
for them, and this allowed for a simpler implementation.
b29e63a
to
c3c6ada
Compare
Pushed changes addressing @BruceForstall's feedback. Most notably, cold EH funclets now use Main body split, no funclets:
Main body split, cold funclets:
Main body NOT split, hot funclets:
Main body NOT split, cold funclets:
|
src/coreclr/jit/unwind.cpp
Outdated
@@ -311,7 +311,6 @@ void Compiler::unwindEmitFuncCFI(FuncInfoDsc* func, void* pHotCode, void* pColdC | |||
#endif // DEBUG | |||
|
|||
assert(endOffset <= info.compTotalHotCodeSize); | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like the only change in unwind.cpp is an arbitrary whitespace change. Maybe remove unwind.cpp from the PR.
c3c6ada
to
793abf5
Compare
Follow-up to #1923. When reserving and allocating unwind info on ARM64, we now consider the possibility that EH funclets are in the cold section. Below are some examples of reserving/allocating unwind info with this new behavior:
Main body split, no EH funclets:
Main body split, with cold EH funclets:
Main body NOT split, with cold EH funclets:
I apologize if I'm stating the obvious here, but note that when the main body is split,
unwindSize > 0
for the cold part's unwind info; it looks like we don't use chained unwind info on ARM64. If this is true, then we will likely have to modify the Scratch table's lookup implementation to correctly find cold functions/funclets on ARM64.