diff --git a/src/coreclr/jit/lsra.h b/src/coreclr/jit/lsra.h index fe09a2e456441..09f8b8d63e581 100644 --- a/src/coreclr/jit/lsra.h +++ b/src/coreclr/jit/lsra.h @@ -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); diff --git a/src/coreclr/jit/lsraarmarch.cpp b/src/coreclr/jit/lsraarmarch.cpp index cf5302dee9c71..64bbe23b06a66 100644 --- a/src/coreclr/jit/lsraarmarch.cpp +++ b/src/coreclr/jit/lsraarmarch.cpp @@ -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 diff --git a/src/coreclr/jit/lsrabuild.cpp b/src/coreclr/jit/lsrabuild.cpp index 4a4af7725dd4d..41b375acd4392 100644 --- a/src/coreclr/jit/lsrabuild.cpp +++ b/src/coreclr/jit/lsrabuild.cpp @@ -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 diff --git a/src/coreclr/jit/lsraxarch.cpp b/src/coreclr/jit/lsraxarch.cpp index 7109c9087ac4e..3d19214f8acbd 100644 --- a/src/coreclr/jit/lsraxarch.cpp +++ b/src/coreclr/jit/lsraxarch.cpp @@ -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