Skip to content
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 generation for EH funclets #1923

Merged
merged 2 commits into from
Jul 1, 2022
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
28 changes: 27 additions & 1 deletion src/coreclr/jit/flowgraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3443,6 +3443,8 @@ PhaseStatus Compiler::fgDetermineFirstColdBlock()
}
else
{
bool inFuncletSection = false;

for (lblk = nullptr, block = fgFirstBB; block != nullptr; lblk = block, block = block->bbNext)
{
bool blockMustBeInHotSection = false;
Expand All @@ -3454,6 +3456,15 @@ PhaseStatus Compiler::fgDetermineFirstColdBlock()
}
#endif // HANDLER_ENTRY_MUST_BE_IN_HOT_SECTION

#ifdef FEATURE_EH_FUNCLETS
// Make note of if we're in the funclet section,
// so we can stop the search early.
if (block == fgFirstFuncletBB)
{
inFuncletSection = true;
}
#endif // FEATURE_EH_FUNCLETS

// Do we have a candidate for the first cold block?
if (firstColdBlock != nullptr)
{
Expand All @@ -3465,6 +3476,21 @@ PhaseStatus Compiler::fgDetermineFirstColdBlock()
// We have to restart the search for the first cold block
firstColdBlock = nullptr;
prevToFirstColdBlock = nullptr;

#ifdef FEATURE_EH_FUNCLETS
// If we're already in the funclet section, try to split
// at fgFirstFuncletBB, and stop the search.
if (inFuncletSection)
{
if (fgFuncletsAreCold())
{
firstColdBlock = fgFirstFuncletBB;
prevToFirstColdBlock = fgFirstFuncletBB->bbPrev;
}

break;
}
#endif // FEATURE_EH_FUNCLETS
}
}
else // (firstColdBlock == NULL) -- we don't have a candidate for first cold block
Expand All @@ -3476,7 +3502,7 @@ PhaseStatus Compiler::fgDetermineFirstColdBlock()
// consider splitting at the first funclet; do not consider splitting between funclets,
// as this may break unwind info.
//
if (block == fgFirstFuncletBB)
if (inFuncletSection)
{
if (fgFuncletsAreCold())
{
Expand Down
10 changes: 9 additions & 1 deletion src/coreclr/jit/unwind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,15 @@ void Compiler::unwindGetFuncLocations(FuncInfoDsc* func,
assert(fgFirstColdBlock != nullptr); // There better be a cold section!

*ppStartLoc = new (this, CMK_UnwindInfo) emitLocation(ehEmitCookie(fgFirstColdBlock));
*ppEndLoc = nullptr; // nullptr end location means the end of the code

if (fgFirstFuncletBB != nullptr)
{
*ppEndLoc = new (this, CMK_UnwindInfo) emitLocation(ehEmitCookie(fgFirstFuncletBB));
}
else
{
*ppEndLoc = nullptr; // nullptr end location means the end of the code
}
}
}
else
Expand Down
111 changes: 74 additions & 37 deletions src/coreclr/jit/unwindamd64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -656,17 +656,34 @@ void Compiler::unwindReserve()
//
void Compiler::unwindReserveFunc(FuncInfoDsc* func)
{
unwindReserveFuncHelper(func, true);

if (fgFirstColdBlock != nullptr)
{
#ifdef DEBUG
if (!JitConfig.JitFakeProcedureSplitting())
// If fake-splitting, treat all unwind info as hot.
if (JitConfig.JitFakeProcedureSplitting())
{
unwindReserveFuncHelper(func, true);
return;
}
#endif // DEBUG

if (func->funKind == FUNC_ROOT)
{
unwindReserveFuncHelper(func, true);

// If the function's main body is split, reserve unwind info of size 0 for the cold section.
// If only funclets are cold, the main body is hot, so don't make a second call.
const bool isFunctionSplit = ((fgFirstColdBlock != nullptr) && (fgFirstColdBlock != fgFirstFuncletBB));
if (isFunctionSplit)
{
unwindReserveFuncHelper(func, false);
}
}
else
{
// Make only one call for funclets.
// If function is split and has EH, the funclets will be cold.
const bool isFuncletHot = (fgFirstColdBlock == nullptr);
unwindReserveFuncHelper(func, isFuncletHot);
}
}

//------------------------------------------------------------------------
Expand All @@ -679,8 +696,10 @@ void Compiler::unwindReserveFunc(FuncInfoDsc* func)
//
void Compiler::unwindReserveFuncHelper(FuncInfoDsc* func, bool isHotCode)
{
DWORD unwindCodeBytes = 0;
if (isHotCode)
const bool isFunclet = (func->funKind != FUNC_ROOT);
DWORD unwindCodeBytes = 0;

if (isHotCode || isFunclet)
{
#ifdef UNIX_AMD64_ABI
if (generateCFIUnwindCodes())
Expand Down Expand Up @@ -717,9 +736,7 @@ void Compiler::unwindReserveFuncHelper(FuncInfoDsc* func, bool isHotCode)
}
}

bool isFunclet = (func->funKind != FUNC_ROOT);
bool isColdCode = !isHotCode;

const bool isColdCode = !isHotCode;
eeReserveUnwindInfo(isFunclet, isColdCode, unwindCodeBytes);
}

Expand Down Expand Up @@ -779,7 +796,32 @@ void Compiler::unwindEmitFuncHelper(FuncInfoDsc* func, void* pHotCode, void* pCo
{
endOffset = func->endLoc->CodeOffset(GetEmitter());
}
}
else
{
assert(fgFirstColdBlock != nullptr);

if (func->coldStartLoc == nullptr)
{
startOffset = 0;
}
else
{
startOffset = func->coldStartLoc->CodeOffset(GetEmitter());
}

if (func->coldEndLoc == nullptr)
{
endOffset = info.compNativeCodeSize;
}
else
{
endOffset = func->coldEndLoc->CodeOffset(GetEmitter());
}
}

if (isHotCode || (func->funKind != FUNC_ROOT))
{
#ifdef UNIX_AMD64_ABI
if (generateCFIUnwindCodes())
{
Expand Down Expand Up @@ -807,28 +849,6 @@ void Compiler::unwindEmitFuncHelper(FuncInfoDsc* func, void* pHotCode, void* pCo
pUnwindBlock = &func->unwindCodes[func->unwindCodeSlot];
}
}
else
{
assert(fgFirstColdBlock != nullptr);

if (func->coldStartLoc == nullptr)
{
startOffset = 0;
}
else
{
startOffset = func->coldStartLoc->CodeOffset(GetEmitter());
}

if (func->coldEndLoc == nullptr)
{
endOffset = info.compNativeCodeSize;
}
else
{
endOffset = func->coldEndLoc->CodeOffset(GetEmitter());
}
}

#ifdef DEBUG
if (opts.dspUnwind)
Expand Down Expand Up @@ -894,17 +914,34 @@ void Compiler::unwindEmitFunc(FuncInfoDsc* func, void* pHotCode, void* pColdCode
static_assert_no_msg(FUNC_HANDLER == (FuncKind)CORJIT_FUNC_HANDLER);
static_assert_no_msg(FUNC_FILTER == (FuncKind)CORJIT_FUNC_FILTER);

unwindEmitFuncHelper(func, pHotCode, pColdCode, true);

if (pColdCode != nullptr)
{
#ifdef DEBUG
if (!JitConfig.JitFakeProcedureSplitting())
// If fake-splitting, treat all unwind info as hot.
if (JitConfig.JitFakeProcedureSplitting())
{
unwindEmitFuncHelper(func, pHotCode, pColdCode, true);
return;
}
#endif // DEBUG

if (func->funKind == FUNC_ROOT)
{
unwindEmitFuncHelper(func, pHotCode, pColdCode, true);

// If the function's main body is split, reserve unwind info of size 0 for the cold section.
// If only funclets are cold, the main body is hot, so don't make a second call.
const bool isFunctionSplit = ((fgFirstColdBlock != nullptr) && (fgFirstColdBlock != fgFirstFuncletBB));
if (isFunctionSplit)
{
unwindEmitFuncHelper(func, pHotCode, pColdCode, false);
}
}
else
{
// Make only one call for funclets.
// If function is split and has EH, the funclets will be cold.
const bool isFuncletHot = (fgFirstColdBlock == nullptr);
unwindEmitFuncHelper(func, pHotCode, pColdCode, isFuncletHot);
}
}

#endif // TARGET_AMD64