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 fa1164c commit e0a37a6
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 30 deletions.
16 changes: 14 additions & 2 deletions src/coreclr/jit/codegenarmarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3394,8 +3394,6 @@ void CodeGen::genCall(GenTreeCall* call)

genCallInstruction(call);

genDefinePendingCallLabel(call);

#ifdef DEBUG
// We should not have GC pointers in killed registers live around the call.
// GC info for arg registers were cleared when consuming arg nodes above
Expand All @@ -3411,6 +3409,20 @@ 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;
}

genDefinePendingCallLabel(call);

var_types returnType = call->TypeGet();
if (returnType != TYP_VOID)
{
Expand Down
36 changes: 14 additions & 22 deletions src/coreclr/jit/codegenlinear.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -695,39 +695,31 @@ 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))
//
// 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 check that invariant in the most common case - when the throw ends the block.
GenTree* call = block->lastNode();
if ((call != nullptr) && (call->gtOper == GT_CALL))
{
instGen(INS_BREAKPOINT); // This should never get executed
assert(GetEmitter()->emitIsLastInsBreakpoint() || call->AsCall()->IsFastTailCall());
}
// 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
{
GenTree* call = block->lastNode();

if ((call != nullptr) && (call->gtOper == GT_CALL))
{
if (call->AsCall()->IsNoReturn())
{
instGen(INS_BREAKPOINT); // This should never get executed
}
}
}
#endif // DEBUG

break;

Expand Down
16 changes: 14 additions & 2 deletions src/coreclr/jit/codegenloongarch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6402,8 +6402,6 @@ void CodeGen::genCall(GenTreeCall* call)

genCallInstruction(call);

genDefinePendingCallLabel(call);

#ifdef DEBUG
// We should not have GC pointers in killed registers live around the call.
// GC info for arg registers were cleared when consuming arg nodes above
Expand All @@ -6419,6 +6417,20 @@ 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;
}

genDefinePendingCallLabel(call);

var_types returnType = call->TypeGet();
if (returnType != TYP_VOID)
{
Expand Down
16 changes: 14 additions & 2 deletions src/coreclr/jit/codegenriscv64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6478,8 +6478,6 @@ void CodeGen::genCall(GenTreeCall* call)

genCallInstruction(call);

genDefinePendingCallLabel(call);

#ifdef DEBUG
// We should not have GC pointers in killed registers live around the call.
// GC info for arg registers were cleared when consuming arg nodes above
Expand All @@ -6495,6 +6493,20 @@ 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;
}

genDefinePendingCallLabel(call);

var_types returnType = call->TypeGet();
if (returnType != TYP_VOID)
{
Expand Down
16 changes: 14 additions & 2 deletions src/coreclr/jit/codegenxarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6028,8 +6028,6 @@ void CodeGen::genCall(GenTreeCall* call)

genCallInstruction(call X86_ARG(stackArgBytes));

genDefinePendingCallLabel(call);

#ifdef DEBUG
// We should not have GC pointers in killed registers live around the call.
// GC info for arg registers were cleared when consuming arg nodes above
Expand All @@ -6045,6 +6043,20 @@ 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;
}

genDefinePendingCallLabel(call);

var_types returnType = call->TypeGet();
if (returnType != TYP_VOID)
{
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 e0a37a6

Please sign in to comment.