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

JIT: Mark swift error as busy before call definition RefPosition #101792

Merged
merged 3 commits into from
May 3, 2024
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
1 change: 1 addition & 0 deletions src/coreclr/jit/lsra.h
Original file line number Diff line number Diff line change
Expand Up @@ -2062,6 +2062,7 @@ class LinearScan : public LinearScanInterface
#endif
int BuildPutArgReg(GenTreeUnOp* node);
int BuildCall(GenTreeCall* call);
void MarkSwiftErrorBusyForCall(GenTreeCall* call);
int BuildCmp(GenTree* tree);
int BuildCmpOperands(GenTree* tree);
int BuildBlockStore(GenTreeBlk* blkNode);
Expand Down
18 changes: 1 addition & 17 deletions src/coreclr/jit/lsraarmarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -404,23 +404,7 @@ int LinearScan::BuildCall(GenTreeCall* call)
#ifdef SWIFT_SUPPORT
if (call->HasSwiftErrorHandling())
{
// Tree is a Swift call with error handling; error register should have been killed
assert((killMask & RBM_SWIFT_ERROR) != 0);

// After a Swift call that might throw returns, we expect the error register to be consumed
// by a GT_SWIFT_ERROR node. However, we want to ensure the error register won't be trashed
// before GT_SWIFT_ERROR can consume it.
// (For example, the PInvoke epilog comes before the error register store.)
// To do so, delay the freeing of the error register until the next node.
// This only works if the next node after the call is the GT_SWIFT_ERROR node.
// (InsertPInvokeCallEpilog should have moved the GT_SWIFT_ERROR node during lowering.)
assert(call->gtNext != nullptr);
assert(call->gtNext->OperIs(GT_SWIFT_ERROR));

// We could use RefTypeKill, but RefTypeFixedReg is used less commonly, so the check for delayRegFree
// during register allocation should be cheaper in terms of TP.
RefPosition* pos = newRefPosition(REG_SWIFT_ERROR, currentLoc + 1, RefTypeFixedReg, call, RBM_SWIFT_ERROR);
setDelayFree(pos);
MarkSwiftErrorBusyForCall(call);
}
#endif // SWIFT_SUPPORT

Expand Down
33 changes: 33 additions & 0 deletions src/coreclr/jit/lsrabuild.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4479,3 +4479,36 @@ int LinearScan::BuildCmpOperands(GenTree* tree)
srcCount += BuildOperandUses(op2, op2Candidates);
return srcCount;
}

#ifdef SWIFT_SUPPORT
//------------------------------------------------------------------------
// MarkSwiftErrorBusyForCall: Given a call set the appropriate RefTypeFixedReg
// RefPosition for the Swift error register as delay free to ensure the error
// register does not get allocated by LSRA before it has been consumed.
//
// Arguments:
// call - The call node
//
void LinearScan::MarkSwiftErrorBusyForCall(GenTreeCall* call)
{
assert(call->HasSwiftErrorHandling());
// After a Swift call that might throw returns, we expect the error register to be consumed
// by a GT_SWIFT_ERROR node. However, we want to ensure the error register won't be trashed
// before GT_SWIFT_ERROR can consume it.
// (For example, by LSRA allocating the call's result to the same register.)
// To do so, delay the freeing of the error register until the next node.
// This only works if the next node after the call is the GT_SWIFT_ERROR node.
// (LowerNonvirtPinvokeCall should have moved the GT_SWIFT_ERROR node.)
assert(call->gtNext != nullptr);
assert(call->gtNext->OperIs(GT_SWIFT_ERROR));

// Conveniently we model the zeroing of the register as a non-standard constant zero argument,
// which will have created a RefPosition corresponding to the use of the error at the location
// of the uses. Marking this RefPosition as delay freed has the effect of keeping the register
// busy at the location of the definition of the call.
RegRecord* swiftErrorRegRecord = getRegisterRecord(REG_SWIFT_ERROR);
assert((swiftErrorRegRecord != nullptr) && (swiftErrorRegRecord->lastRefPosition != nullptr) &&
(swiftErrorRegRecord->lastRefPosition->nodeLocation == currentLoc));
setDelayFree(swiftErrorRegRecord->lastRefPosition);
}
#endif
18 changes: 1 addition & 17 deletions src/coreclr/jit/lsraxarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1380,23 +1380,7 @@ int LinearScan::BuildCall(GenTreeCall* call)
#ifdef SWIFT_SUPPORT
if (call->HasSwiftErrorHandling())
{
// Tree is a Swift call with error handling; error register should have been killed
assert((killMask & RBM_SWIFT_ERROR) != 0);

// After a Swift call that might throw returns, we expect the error register to be consumed
// by a GT_SWIFT_ERROR node. However, we want to ensure the error register won't be trashed
// before GT_SWIFT_ERROR can consume it.
// (For example, the PInvoke epilog comes before the error register store.)
// To do so, delay the freeing of the error register until the next node.
// This only works if the next node after the call is the GT_SWIFT_ERROR node.
// (InsertPInvokeCallEpilog should have moved the GT_SWIFT_ERROR node during lowering.)
assert(call->gtNext != nullptr);
assert(call->gtNext->OperIs(GT_SWIFT_ERROR));

// We could use RefTypeKill, but RefTypeFixedReg is used less commonly, so the check for delayRegFree
// during register allocation should be cheaper in terms of TP.
RefPosition* pos = newRefPosition(REG_SWIFT_ERROR, currentLoc + 1, RefTypeFixedReg, call, RBM_SWIFT_ERROR);
setDelayFree(pos);
MarkSwiftErrorBusyForCall(call);
}
#endif // SWIFT_SUPPORT

Expand Down
Loading