Skip to content

Commit

Permalink
BRK after throwing calls
Browse files Browse the repository at this point in the history
  • Loading branch information
VSadov committed Apr 15, 2024
1 parent 8a1e0b7 commit a3874e8
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 34 deletions.
12 changes: 12 additions & 0 deletions src/coreclr/jit/codegenarmarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3411,6 +3411,18 @@ void CodeGen::genCall(GenTreeCall* call)
assert((gcInfo.gcRegByrefSetCur & killMask) == 0);
#endif

if (call->IsNoReturn())
{
// There are several situations when we need to add another instruction
// after a throwing call to help the OS unwinder determine the correct context during unwind.
// It also ensures that the gc register liveness doesn't change across throwing call instructions
// in fully-interruptible mode.
instGen(INS_BREAKPOINT);

// nothing else needs to be emitted for this call
return;
}

var_types returnType = call->TypeGet();
if (returnType != TYP_VOID)
{
Expand Down
40 changes: 18 additions & 22 deletions src/coreclr/jit/codegenlinear.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -695,41 +695,37 @@ void CodeGen::genCodeForBBlist()
break;

case BBJ_THROW:
#ifdef DEBUG
{
// If we have a throw at the end of a function or funclet, we need to emit another instruction
// afterwards to help the OS unwinder determine the correct context during unwind.
// We insert an unexecuted breakpoint instruction in several situations
// following a throw instruction:
// We need that in the following situations:
// 1. If the throw is the last instruction of the function or funclet. This helps
// the OS unwinder determine the correct context during an unwind from the
// thrown exception.
// 2. If this is this is the last block of the hot section.
// 3. If the subsequent block is a special throw block.
// 4. On AMD64, if the next block is in a different EH region.
if (block->IsLast() || block->Next()->HasFlag(BBF_FUNCLET_BEG) ||
!BasicBlock::sameEHRegion(block, block->Next()) ||
(!isFramePointerUsed() && compiler->fgIsThrowHlpBlk(block->Next())) ||
block->IsLastHotBlock(compiler))
{
instGen(INS_BREAKPOINT); // This should never get executed
}
// Do likewise for blocks that end in DOES_NOT_RETURN calls
// that were not caught by the above rules. This ensures that
// gc register liveness doesn't change across call instructions
// in fully-interruptible mode.
else
//
// The extra instruction is also needed to ensure that gc register liveness doesn't change
// across call instructions in fully-interruptible mode.
//
// Considering that the throwing code is typically rare, and is not on the common/fast path
// we just add the instruction after all throwing calls.
// We will test that invariant in the most common case - when the throw ends the block.
GenTree* last = block->lastNode();
if ((last != nullptr) && (last->gtOper == GT_CALL))
{
GenTree* call = block->lastNode();

if ((call != nullptr) && (call->gtOper == GT_CALL))
GenTreeCall* call = last->AsCall();
if (call->IsNoReturn())
{
if (call->AsCall()->IsNoReturn())
{
instGen(INS_BREAKPOINT); // This should never get executed
}
assert(GetEmitter()->emitIsLastInsBreakpoint() || call->IsFastTailCall());
}
}
}
#endif // DEBUG

break;
break;

case BBJ_CALLFINALLY:
block = genCallFinally(block);
Expand Down
12 changes: 12 additions & 0 deletions src/coreclr/jit/codegenloongarch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6419,6 +6419,18 @@ void CodeGen::genCall(GenTreeCall* call)
assert((gcInfo.gcRegByrefSetCur & killMask) == 0);
#endif

if (call->IsNoReturn())
{
// There are several situations when we need to add another instruction
// after a throwing call to help the OS unwinder determine the correct context during unwind.
// It also ensures that the gc register liveness doesn't change across throwing call instructions
// in fully-interruptible mode.
instGen(INS_BREAKPOINT);

// nothing else needs to be emitted for this call
return;
}

var_types returnType = call->TypeGet();
if (returnType != TYP_VOID)
{
Expand Down
12 changes: 12 additions & 0 deletions src/coreclr/jit/codegenriscv64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6495,6 +6495,18 @@ void CodeGen::genCall(GenTreeCall* call)
assert((gcInfo.gcRegByrefSetCur & killMask) == 0);
#endif

if (call->IsNoReturn())
{
// There are several situations when we need to add another instruction
// after a throwing call to help the OS unwinder determine the correct context during unwind.
// It also ensures that the gc register liveness doesn't change across throwing call instructions
// in fully-interruptible mode.
instGen(INS_BREAKPOINT);

// nothing else needs to be emitted for this call
return;
}

var_types returnType = call->TypeGet();
if (returnType != TYP_VOID)
{
Expand Down
36 changes: 24 additions & 12 deletions src/coreclr/jit/codegenxarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6045,6 +6045,30 @@ void CodeGen::genCall(GenTreeCall* call)
assert((gcInfo.gcRegByrefSetCur & killMask) == 0);
#endif

unsigned stackAdjustBias = 0;

#if defined(TARGET_X86)
// Is the caller supposed to pop the arguments?
if (call->CallerPop() && (stackArgBytes != 0))
{
stackAdjustBias = stackArgBytes;
}

SubtractStackLevel(stackArgBytes);
#endif // TARGET_X86

if (call->IsNoReturn())
{
// There are several situations when we need to add another instruction
// after a throwing call to help the OS unwinder determine the correct context during unwind.
// It also ensures that the gc register liveness doesn't change across throwing call instructions
// in fully-interruptible mode.
instGen(INS_BREAKPOINT);

// nothing else needs to be emitted for this call
return;
}

var_types returnType = call->TypeGet();
if (returnType != TYP_VOID)
{
Expand Down Expand Up @@ -6169,18 +6193,6 @@ void CodeGen::genCall(GenTreeCall* call)
}
#endif // FEATURE_EH_WINDOWS_X86

unsigned stackAdjustBias = 0;

#if defined(TARGET_X86)
// Is the caller supposed to pop the arguments?
if (call->CallerPop() && (stackArgBytes != 0))
{
stackAdjustBias = stackArgBytes;
}

SubtractStackLevel(stackArgBytes);
#endif // TARGET_X86

genRemoveAlignmentAfterCall(call, stackAdjustBias);
}

Expand Down
11 changes: 11 additions & 0 deletions src/coreclr/jit/emit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2569,6 +2569,17 @@ bool emitter::emitHasEpilogEnd()

#endif // JIT32_GCENCODER

// Is the last instruction emitted a breakpoint instruction?
bool emitter::emitIsLastInsBreakpoint()
{
if (emitHasLastIns() && (emitLastIns->idIns() == INS_BREAKPOINT))
{
return true;
}

return false;
}

#ifdef TARGET_XARCH

/*****************************************************************************
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/jit/emit.h
Original file line number Diff line number Diff line change
Expand Up @@ -2757,6 +2757,9 @@ class emitter
return (emitLastIns != nullptr);
}

// Is the last instruction emitted a breakpoint instruction?
bool emitIsLastInsBreakpoint();

// Checks to see if we can cross between the two given IG boundaries.
//
// We have the following checks:
Expand Down

0 comments on commit a3874e8

Please sign in to comment.