diff --git a/src/coreclr/jit/abi.cpp b/src/coreclr/jit/abi.cpp index 3dd8fcec32fcc..d54243aa47614 100644 --- a/src/coreclr/jit/abi.cpp +++ b/src/coreclr/jit/abi.cpp @@ -242,6 +242,17 @@ ABIPassingInformation SwiftABIClassifier::Classify(Compiler* comp, TARGET_POINTER_SIZE)); } + if (wellKnownParam == WellKnownArg::SwiftError) + { + // We aren't actually going to pass the SwiftError* parameter in REG_SWIFT_ERROR. + // We won't be using this parameter at all, and shouldn't allocate registers/stack space for it, + // as that will mess with other args. + // Quirk: To work around the JIT for now, "pass" it in REG_SWIFT_ERROR, + // and let CodeGen::genFnProlog handle the rest. + return ABIPassingInformation::FromSegment(comp, ABIPassingSegment::InRegister(REG_SWIFT_ERROR, 0, + TARGET_POINTER_SIZE)); + } + return m_classifier.Classify(comp, type, structLayout, wellKnownParam); } #endif diff --git a/src/coreclr/jit/codegenarm.cpp b/src/coreclr/jit/codegenarm.cpp index 65ba1bf5913c6..bddc03e0b41a5 100644 --- a/src/coreclr/jit/codegenarm.cpp +++ b/src/coreclr/jit/codegenarm.cpp @@ -2149,7 +2149,7 @@ void CodeGen::genPopCalleeSavedRegisters(bool jmpEpilog) { assert(compiler->compGeneratingEpilog); - regMaskTP maskPopRegs = regSet.rsGetModifiedRegsMask() & RBM_CALLEE_SAVED; + regMaskTP maskPopRegs = regSet.rsGetModifiedCalleeSavedRegsMask(); regMaskTP maskPopRegsFloat = maskPopRegs & RBM_ALLFLOAT; regMaskTP maskPopRegsInt = maskPopRegs & ~maskPopRegsFloat; diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp index ca9ab73224d7a..74258cdd55a73 100644 --- a/src/coreclr/jit/codegenarm64.cpp +++ b/src/coreclr/jit/codegenarm64.cpp @@ -36,7 +36,7 @@ void CodeGen::genPopCalleeSavedRegistersAndFreeLclFrame(bool jmpEpilog) { assert(compiler->compGeneratingEpilog); - regMaskTP rsRestoreRegs = regSet.rsGetModifiedRegsMask() & RBM_CALLEE_SAVED; + regMaskTP rsRestoreRegs = regSet.rsGetModifiedCalleeSavedRegsMask(); if (isFramePointerUsed()) { diff --git a/src/coreclr/jit/codegenarmarch.cpp b/src/coreclr/jit/codegenarmarch.cpp index 965b72721aaaa..12512a958e08e 100644 --- a/src/coreclr/jit/codegenarmarch.cpp +++ b/src/coreclr/jit/codegenarmarch.cpp @@ -4893,7 +4893,7 @@ void CodeGen::genPushCalleeSavedRegisters() intRegState.rsCalleeRegArgMaskLiveIn); #endif - regMaskTP rsPushRegs = regSet.rsGetModifiedRegsMask() & RBM_CALLEE_SAVED; + regMaskTP rsPushRegs = regSet.rsGetModifiedCalleeSavedRegsMask(); #if ETW_EBP_FRAMED if (!isFramePointerUsed() && regSet.rsRegsModified(RBM_FPBASE)) @@ -5540,8 +5540,8 @@ void CodeGen::genFnEpilog(BasicBlock* block) compiler->unwindSetFrameReg(REG_SAVED_LOCALLOC_SP, 0); } - if (jmpEpilog || genStackAllocRegisterMask(compiler->compLclFrameSize, - regSet.rsGetModifiedRegsMask() & RBM_FLT_CALLEE_SAVED) == RBM_NONE) + if (jmpEpilog || + genStackAllocRegisterMask(compiler->compLclFrameSize, regSet.rsGetModifiedFltCalleeSavedRegsMask()) == RBM_NONE) { genFreeLclFrame(compiler->compLclFrameSize, &unwindStarted); } diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 5c733c5457d94..0502339718f8f 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -2980,6 +2980,17 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere { continue; } + + // On a similar note, the SwiftError* parameter is not a real argument, + // and should not be allocated any registers/stack space. + // We mark it as being passed in REG_SWIFT_ERROR so it won't interfere with other args. + // In genFnProlog, we should have removed this callee-save register from intRegState.rsCalleeRegArgMaskLiveIn. + // TODO-CQ: Fix this. + if (varNum == compiler->lvaSwiftErrorArg) + { + assert((intRegState.rsCalleeRegArgMaskLiveIn & RBM_SWIFT_ERROR) == 0); + continue; + } #endif var_types regType = compiler->mangleVarArgsType(varDsc->TypeGet()); @@ -5382,7 +5393,7 @@ void CodeGen::genFinalizeFrame() noway_assert(!regSet.rsRegsModified(RBM_FPBASE)); #endif - regMaskTP maskCalleeRegsPushed = regSet.rsGetModifiedRegsMask() & RBM_CALLEE_SAVED; + regMaskTP maskCalleeRegsPushed = regSet.rsGetModifiedCalleeSavedRegsMask(); #ifdef TARGET_ARMARCH if (isFramePointerUsed()) @@ -6062,7 +6073,7 @@ void CodeGen::genFnProlog() #ifdef TARGET_ARM maskStackAlloc = genStackAllocRegisterMask(compiler->compLclFrameSize + extraFrameSize, - regSet.rsGetModifiedRegsMask() & RBM_FLT_CALLEE_SAVED); + regSet.rsGetModifiedFltCalleeSavedRegsMask()); #endif // TARGET_ARM if (maskStackAlloc == RBM_NONE) @@ -6137,6 +6148,10 @@ void CodeGen::genFnProlog() GetEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, REG_SWIFT_SELF, compiler->lvaSwiftSelfArg, 0); intRegState.rsCalleeRegArgMaskLiveIn &= ~RBM_SWIFT_SELF; } + else if (compiler->lvaSwiftErrorArg != BAD_VAR_NUM) + { + intRegState.rsCalleeRegArgMaskLiveIn &= ~RBM_SWIFT_ERROR; + } #endif // @@ -7820,6 +7835,17 @@ void CodeGen::genReturn(GenTree* treeNode) genStackPointerCheck(doStackPointerCheck, compiler->lvaReturnSpCheck); #endif // defined(DEBUG) && defined(TARGET_XARCH) + +#ifdef SWIFT_SUPPORT + // If this method has a SwiftError* out parameter, load the SwiftError pseudolocal value into the error register. + // TODO-CQ: Introduce GenTree node that models returning a normal and Swift error value. + if (compiler->lvaSwiftErrorArg != BAD_VAR_NUM) + { + assert(compiler->info.compCallConv == CorInfoCallConvExtension::Swift); + assert(compiler->lvaSwiftErrorLocal != BAD_VAR_NUM); + GetEmitter()->emitIns_R_S(ins_Load(TYP_I_IMPL), EA_PTRSIZE, REG_SWIFT_ERROR, compiler->lvaSwiftErrorLocal, 0); + } +#endif // SWIFT_SUPPORT } //------------------------------------------------------------------------ diff --git a/src/coreclr/jit/codegenloongarch64.cpp b/src/coreclr/jit/codegenloongarch64.cpp index ec27d2ff8ab4d..0762291af9da0 100644 --- a/src/coreclr/jit/codegenloongarch64.cpp +++ b/src/coreclr/jit/codegenloongarch64.cpp @@ -7714,7 +7714,7 @@ void CodeGen::genPushCalleeSavedRegisters(regNumber initReg, bool* pInitRegZeroe { assert(compiler->compGeneratingProlog); - regMaskTP rsPushRegs = regSet.rsGetModifiedRegsMask() & RBM_CALLEE_SAVED; + regMaskTP rsPushRegs = regSet.rsGetModifiedCalleeSavedRegsMask(); #if ETW_EBP_FRAMED if (!isFramePointerUsed() && regSet.rsRegsModified(RBM_FPBASE)) @@ -7879,7 +7879,7 @@ void CodeGen::genPopCalleeSavedRegisters(bool jmpEpilog) { assert(compiler->compGeneratingEpilog); - regMaskTP regsToRestoreMask = regSet.rsGetModifiedRegsMask() & RBM_CALLEE_SAVED; + regMaskTP regsToRestoreMask = regSet.rsGetModifiedCalleeSavedRegsMask(); assert(isFramePointerUsed()); diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index 1d48582c6c316..0b0199b064615 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -7792,7 +7792,7 @@ void CodeGen::genPushCalleeSavedRegisters(regNumber initReg, bool* pInitRegZeroe // beforehand. We don't care if REG_SCRATCH will be overwritten, so we'll skip 'RegZeroed check'. // // Unlike on x86/x64, we can also push float registers to stack - regMaskTP rsPushRegs = regSet.rsGetModifiedRegsMask() & RBM_CALLEE_SAVED; + regMaskTP rsPushRegs = regSet.rsGetModifiedCalleeSavedRegsMask(); #if ETW_EBP_FRAMED if (!isFramePointerUsed() && regSet.rsRegsModified(RBM_FPBASE)) @@ -7955,7 +7955,7 @@ void CodeGen::genPopCalleeSavedRegisters(bool jmpEpilog) { assert(compiler->compGeneratingEpilog); - regMaskTP regsToRestoreMask = regSet.rsGetModifiedRegsMask() & RBM_CALLEE_SAVED; + regMaskTP regsToRestoreMask = regSet.rsGetModifiedCalleeSavedRegsMask(); // On RV64 we always use the FP (frame-pointer) assert(isFramePointerUsed()); diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index ede5df1bea39d..132d4b01ccd24 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -9756,7 +9756,7 @@ void CodeGen::genOSRSaveRemainingCalleeSavedRegisters() // x86/x64 doesn't support push of xmm/ymm regs, therefore consider only integer registers for pushing onto stack // here. Space for float registers to be preserved is stack allocated and saved as part of prolog sequence and not // here. - regMaskTP rsPushRegs = regSet.rsGetModifiedRegsMask() & RBM_OSR_INT_CALLEE_SAVED; + regMaskTP rsPushRegs = regSet.rsGetModifiedOsrIntCalleeSavedRegsMask(); #if ETW_EBP_FRAMED if (!isFramePointerUsed() && regSet.rsRegsModified(RBM_FPBASE)) @@ -9837,7 +9837,7 @@ void CodeGen::genPushCalleeSavedRegisters() // x86/x64 doesn't support push of xmm/ymm regs, therefore consider only integer registers for pushing onto stack // here. Space for float registers to be preserved is stack allocated and saved as part of prolog sequence and not // here. - regMaskTP rsPushRegs = regSet.rsGetModifiedRegsMask() & RBM_INT_CALLEE_SAVED; + regMaskTP rsPushRegs = regSet.rsGetModifiedIntCalleeSavedRegsMask(); #if ETW_EBP_FRAMED if (!isFramePointerUsed() && regSet.rsRegsModified(RBM_FPBASE)) @@ -9895,7 +9895,7 @@ void CodeGen::genPopCalleeSavedRegisters(bool jmpEpilog) // if (doesSupersetOfNormalPops) { - regMaskTP rsPopRegs = regSet.rsGetModifiedRegsMask() & RBM_OSR_INT_CALLEE_SAVED; + regMaskTP rsPopRegs = regSet.rsGetModifiedOsrIntCalleeSavedRegsMask(); regMaskTP tier0CalleeSaves = ((regMaskTP)compiler->info.compPatchpointInfo->CalleeSaveRegisters()) & RBM_OSR_INT_CALLEE_SAVED; regMaskTP additionalCalleeSaves = rsPopRegs & ~tier0CalleeSaves; @@ -9915,7 +9915,7 @@ void CodeGen::genPopCalleeSavedRegisters(bool jmpEpilog) // Registers saved by a normal prolog // - regMaskTP rsPopRegs = regSet.rsGetModifiedRegsMask() & RBM_INT_CALLEE_SAVED; + regMaskTP rsPopRegs = regSet.rsGetModifiedIntCalleeSavedRegsMask(); const unsigned popCount = genPopCalleeSavedRegistersFromMask(rsPopRegs); noway_assert(compiler->compCalleeRegsPushed == popCount); } @@ -10102,7 +10102,7 @@ void CodeGen::genFnEpilog(BasicBlock* block) regMaskTP const tier0CalleeSaves = (regMaskTP)patchpointInfo->CalleeSaveRegisters(); regMaskTP const tier0IntCalleeSaves = tier0CalleeSaves & RBM_OSR_INT_CALLEE_SAVED; - regMaskTP const osrIntCalleeSaves = regSet.rsGetModifiedRegsMask() & RBM_OSR_INT_CALLEE_SAVED; + regMaskTP const osrIntCalleeSaves = regSet.rsGetModifiedOsrIntCalleeSavedRegsMask(); regMaskTP const allIntCalleeSaves = osrIntCalleeSaves | tier0IntCalleeSaves; unsigned const tier0FrameSize = patchpointInfo->TotalFrameSize() + REGSIZE_BYTES; unsigned const tier0IntCalleeSaveUsedSize = genCountBits(allIntCalleeSaves) * REGSIZE_BYTES; diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 10dc0ef5f07e5..3661e8005cb4c 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -5925,7 +5925,7 @@ void Compiler::generatePatchpointInfo() // Record callee save registers. // Currently only needed for x64. // - regMaskTP rsPushRegs = codeGen->regSet.rsGetModifiedRegsMask() & RBM_CALLEE_SAVED; + regMaskTP rsPushRegs = codeGen->regSet.rsGetModifiedCalleeSavedRegsMask(); rsPushRegs |= RBM_FPBASE; patchpointInfo->SetCalleeSaveRegisters((uint64_t)rsPushRegs); JITDUMP("--OSR-- Tier0 callee saves: "); diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 30fedcd9cd56c..6ad178b32feed 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -3876,6 +3876,8 @@ class Compiler #ifdef SWIFT_SUPPORT unsigned lvaSwiftSelfArg; + unsigned lvaSwiftErrorArg; + unsigned lvaSwiftErrorLocal; #endif #if defined(DEBUG) && defined(TARGET_XARCH) @@ -4005,7 +4007,7 @@ class Compiler void lvaClassifyParameterABI(); - bool lvaInitSpecialSwiftParam(InitVarDscInfo* varDscInfo, CorInfoType type, CORINFO_CLASS_HANDLE typeHnd); + bool lvaInitSpecialSwiftParam(CORINFO_ARG_LIST_HANDLE argHnd, InitVarDscInfo* varDscInfo, CorInfoType type, CORINFO_CLASS_HANDLE typeHnd); var_types lvaGetActualType(unsigned lclNum); var_types lvaGetRealType(unsigned lclNum); @@ -4420,12 +4422,12 @@ class Compiler void impCheckForPInvokeCall( GenTreeCall* call, CORINFO_METHOD_HANDLE methHnd, CORINFO_SIG_INFO* sig, unsigned mflags, BasicBlock* block); GenTreeCall* impImportIndirectCall(CORINFO_SIG_INFO* sig, const DebugInfo& di = DebugInfo()); - void impPopArgsForUnmanagedCall(GenTreeCall* call, CORINFO_SIG_INFO* sig, CallArg** swiftErrorArg); - void impPopArgsForSwiftCall(GenTreeCall* call, CORINFO_SIG_INFO* sig, CallArg** swiftErrorArg); + void impPopArgsForUnmanagedCall(GenTreeCall* call, CORINFO_SIG_INFO* sig, GenTree** swiftErrorNode); + void impPopArgsForSwiftCall(GenTreeCall* call, CORINFO_SIG_INFO* sig, GenTree** swiftErrorNode); void impRetypeUnmanagedCallArgs(GenTreeCall* call); #ifdef SWIFT_SUPPORT - void impAppendSwiftErrorStore(GenTreeCall* call, CallArg* const swiftErrorArg); + void impAppendSwiftErrorStore(GenTree* const swiftErrorNode); #endif // SWIFT_SUPPORT void impInsertHelperCall(CORINFO_HELPER_DESC* helperCall); diff --git a/src/coreclr/jit/gtlist.h b/src/coreclr/jit/gtlist.h index 00c117f8eb83c..817b27a936a56 100644 --- a/src/coreclr/jit/gtlist.h +++ b/src/coreclr/jit/gtlist.h @@ -37,7 +37,6 @@ GTNODE(LABEL , GenTree ,0,0,GTK_LEAF) // Jump- GTNODE(JMP , GenTreeVal ,0,0,GTK_LEAF|GTK_NOVALUE) // Jump to another function GTNODE(FTN_ADDR , GenTreeFptrVal ,0,0,GTK_LEAF) // Address of a function GTNODE(RET_EXPR , GenTreeRetExpr ,0,0,GTK_LEAF|DBK_NOTLIR) // Place holder for the return expression from an inline candidate -GTNODE(SWIFT_ERROR , GenTree ,0,0,GTK_LEAF) // Error register value post-Swift call //----------------------------------------------------------------------------- // Constant nodes: @@ -287,6 +286,12 @@ GTNODE(RETFILT , GenTreeOp ,0,1,GTK_UNOP|GTK_NOVALUE) // End f GTNODE(END_LFIN , GenTreeVal ,0,0,GTK_LEAF|GTK_NOVALUE) // End locally-invoked finally. #endif // !FEATURE_EH_FUNCLETS +//----------------------------------------------------------------------------- +// Swift interop-specific nodes: +//----------------------------------------------------------------------------- + +GTNODE(SWIFT_ERROR , GenTree ,0,0,GTK_LEAF) // Error register value post-Swift call + //----------------------------------------------------------------------------- // Nodes used by Lower to generate a closer CPU representation of other nodes //----------------------------------------------------------------------------- diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index bb5231b11ce63..4a1590fb7e0bf 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -10462,6 +10462,21 @@ void Compiler::impLoadArg(unsigned ilArgNum, IL_OFFSET offset) { lclNum = lvaArg0Var; } +#ifdef SWIFT_SUPPORT + else if (lclNum == lvaSwiftErrorArg) + { + // Convert any usages of the SwiftError pointer/ref parameter to pointers/refs to the SwiftError pseudolocal + // (set side effect flags so usages of references to pseudolocal aren't removed) + assert(info.compCallConv == CorInfoCallConvExtension::Swift); + assert(lvaSwiftErrorArg != BAD_VAR_NUM); + assert(lvaSwiftErrorLocal != BAD_VAR_NUM); + const var_types type = lvaGetDesc(lvaSwiftErrorArg)->TypeGet(); + GenTree* const swiftErrorLocalRef = gtNewLclVarAddrNode(lvaSwiftErrorLocal, type); + impPushOnStack(swiftErrorLocalRef, typeInfo(type)); + JITDUMP("\nCreated GT_LCL_ADDR of SwiftError pseudolocal\n"); + return; + } +#endif // SWIFT_SUPPORT impLoadVar(lclNum, offset); } diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 52fdf5ab3cd47..24d7370b8f985 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -100,7 +100,7 @@ var_types Compiler::impImportCall(OPCODE opcode, // Swift calls that might throw use a SwiftError* arg that requires additional IR to handle, // so if we're importing a Swift call, look for this type in the signature - CallArg* swiftErrorArg = nullptr; + GenTree* swiftErrorNode = nullptr; /*------------------------------------------------------------------------- * First create the call node @@ -669,7 +669,7 @@ var_types Compiler::impImportCall(OPCODE opcode, checkForSmallType = true; - impPopArgsForUnmanagedCall(call->AsCall(), sig, &swiftErrorArg); + impPopArgsForUnmanagedCall(call->AsCall(), sig, &swiftErrorNode); goto DONE; } @@ -1502,9 +1502,9 @@ var_types Compiler::impImportCall(OPCODE opcode, #ifdef SWIFT_SUPPORT // If call is a Swift call with error handling, append additional IR // to handle storing the error register's value post-call. - if (swiftErrorArg != nullptr) + if (swiftErrorNode != nullptr) { - impAppendSwiftErrorStore(call->AsCall(), swiftErrorArg); + impAppendSwiftErrorStore(swiftErrorNode); } #endif // SWIFT_SUPPORT @@ -1844,17 +1844,18 @@ GenTreeCall* Compiler::impImportIndirectCall(CORINFO_SIG_INFO* sig, const DebugI // Arguments: // call - The unmanaged call // sig - The signature of the call site -// swiftErrorArg - [out] If this is a Swift call with a SwiftError* argument, then the argument is returned here. -// Otherwise left at its existing value. +// swiftErrorNode - [out] If this is a Swift call with a SwiftError* argument, +// then swiftErrorNode points to the node. +// Otherwise left at its existing value. // -void Compiler::impPopArgsForUnmanagedCall(GenTreeCall* call, CORINFO_SIG_INFO* sig, CallArg** swiftErrorArg) +void Compiler::impPopArgsForUnmanagedCall(GenTreeCall* call, CORINFO_SIG_INFO* sig, GenTree** swiftErrorNode) { assert(call->gtFlags & GTF_CALL_UNMANAGED); #ifdef SWIFT_SUPPORT if (call->unmgdCallConv == CorInfoCallConvExtension::Swift) { - impPopArgsForSwiftCall(call, sig, swiftErrorArg); + impPopArgsForSwiftCall(call, sig, swiftErrorNode); return; } #endif @@ -2000,12 +2001,12 @@ const CORINFO_SWIFT_LOWERING* Compiler::GetSwiftLowering(CORINFO_CLASS_HANDLE hC // impPopArgsForSwiftCall: Pop arguments from IL stack to a Swift pinvoke node. // // Arguments: -// call - The Swift call -// sig - The signature of the call site -// swiftErrorArg - [out] An argument that represents the SwiftError* -// argument. Left at its existing value if no such argument exists. +// call - The Swift call +// sig - The signature of the call site +// swiftErrorNode - [out] Pointer to the SwiftError* argument. +// Left at its existing value if no such argument exists. // -void Compiler::impPopArgsForSwiftCall(GenTreeCall* call, CORINFO_SIG_INFO* sig, CallArg** swiftErrorArg) +void Compiler::impPopArgsForSwiftCall(GenTreeCall* call, CORINFO_SIG_INFO* sig, GenTree** swiftErrorNode) { JITDUMP("Creating args for Swift call [%06u]\n", dspTreeID(call)); @@ -2023,13 +2024,12 @@ void Compiler::impPopArgsForSwiftCall(GenTreeCall* call, CORINFO_SIG_INFO* sig, { CORINFO_CLASS_HANDLE argClass; CorInfoType argType = strip(info.compCompHnd->getArgType(sig, sigArg, &argClass)); - bool argIsByrefOrPtr = false; + const bool argIsByrefOrPtr = (argType == CORINFO_TYPE_BYREF) || (argType == CORINFO_TYPE_PTR); - if ((argType == CORINFO_TYPE_BYREF) || (argType == CORINFO_TYPE_PTR)) + if (argIsByrefOrPtr) { - argClass = info.compCompHnd->getArgClass(sig, sigArg); - argType = info.compCompHnd->getChildType(argClass, &argClass); - argIsByrefOrPtr = true; + argClass = info.compCompHnd->getArgClass(sig, sigArg); + argType = info.compCompHnd->getChildType(argClass, &argClass); } if (argType != CORINFO_TYPE_VALUECLASS) @@ -2115,10 +2115,9 @@ void Compiler::impPopArgsForSwiftCall(GenTreeCall* call, CORINFO_SIG_INFO* sig, DISPTREE(call); JITDUMP("\n"); - if (swiftErrorIndex != sig->numArgs) - { - *swiftErrorArg = call->gtArgs.GetArgByIndex(swiftErrorIndex); - } + // Get SwiftError* arg (if it exists) before modifying the arg list + CallArg* const swiftErrorArg = + (swiftErrorIndex != sig->numArgs) ? call->gtArgs.GetArgByIndex(swiftErrorIndex) : nullptr; // Now expand struct args that must be lowered into primitives unsigned argIndex = 0; @@ -2261,6 +2260,23 @@ void Compiler::impPopArgsForSwiftCall(GenTreeCall* call, CORINFO_SIG_INFO* sig, arg = insertAfter->GetNext(); } + if (swiftErrorArg != nullptr) + { + // Before calling a Swift method that may throw, the error register must be cleared, + // as we will check for a nonzero error value after the call returns. + // By adding a well-known "sentinel" argument that uses the error register, + // the JIT will emit code for clearing the error register before the call, + // and will mark the error register as busy so it isn't used to hold the function call's address. + GenTree* const errorSentinelValueNode = gtNewIconNode(0); + call->gtArgs.InsertAfter(this, swiftErrorArg, + NewCallArg::Primitive(errorSentinelValueNode).WellKnown(WellKnownArg::SwiftError)); + + // Swift call isn't going to use the SwiftError* arg, so don't bother emitting it + assert(swiftErrorNode != nullptr); + *swiftErrorNode = swiftErrorArg->GetNode(); + call->gtArgs.Remove(swiftErrorArg); + } + #ifdef DEBUG if (verbose && call->TypeIs(TYP_STRUCT) && (sig->retTypeClass != NO_CLASS_HANDLE)) { @@ -2291,39 +2307,22 @@ void Compiler::impPopArgsForSwiftCall(GenTreeCall* call, CORINFO_SIG_INFO* sig, //------------------------------------------------------------------------ // impAppendSwiftErrorStore: Append IR to store the Swift error register value -// to the SwiftError* argument specified by swiftErrorArg, post-Swift call +// to the SwiftError* argument represented by swiftErrorNode, post-Swift call // // Arguments: -// call - the Swift call -// swiftErrorArg - the SwiftError* argument passed to call +// swiftErrorNode - the SwiftError* argument // -void Compiler::impAppendSwiftErrorStore(GenTreeCall* call, CallArg* const swiftErrorArg) +void Compiler::impAppendSwiftErrorStore(GenTree* const swiftErrorNode) { - assert(call != nullptr); - assert(call->unmgdCallConv == CorInfoCallConvExtension::Swift); - assert(swiftErrorArg != nullptr); - - GenTree* const argNode = swiftErrorArg->GetNode(); - assert(argNode != nullptr); + assert(swiftErrorNode != nullptr); // Store the error register value to where the SwiftError* points to GenTree* errorRegNode = new (this, GT_SWIFT_ERROR) GenTree(GT_SWIFT_ERROR, TYP_I_IMPL); errorRegNode->SetHasOrderingSideEffect(); errorRegNode->gtFlags |= (GTF_CALL | GTF_GLOB_REF); - GenTreeStoreInd* swiftErrorStore = gtNewStoreIndNode(argNode->TypeGet(), argNode, errorRegNode); + GenTreeStoreInd* swiftErrorStore = gtNewStoreIndNode(swiftErrorNode->TypeGet(), swiftErrorNode, errorRegNode); impAppendTree(swiftErrorStore, CHECK_SPILL_ALL, impCurStmtDI, false); - - // Before calling a Swift method that may throw, the error register must be cleared for the error check to work. - // By adding a well-known "sentinel" argument that uses the error register, - // the JIT will emit code for clearing the error register before the call, and will mark the error register as busy - // so that it isn't used to hold the function call's address. - GenTree* errorSentinelValueNode = gtNewIconNode(0); - call->gtArgs.InsertAfter(this, swiftErrorArg, - NewCallArg::Primitive(errorSentinelValueNode).WellKnown(WellKnownArg::SwiftError)); - - // Swift call isn't going to use the SwiftError* arg, so don't bother emitting it - call->gtArgs.Remove(swiftErrorArg); } #endif // SWIFT_SUPPORT diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 0de4f52eee756..6b3650416574c 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -72,7 +72,8 @@ void Compiler::lvaInit() lvaRetAddrVar = BAD_VAR_NUM; #ifdef SWIFT_SUPPORT - lvaSwiftSelfArg = BAD_VAR_NUM; + lvaSwiftSelfArg = BAD_VAR_NUM; + lvaSwiftErrorArg = BAD_VAR_NUM; #endif lvaInlineeReturnSpillTemp = BAD_VAR_NUM; @@ -662,7 +663,7 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un #ifdef SWIFT_SUPPORT if ((info.compCallConv == CorInfoCallConvExtension::Swift) && - lvaInitSpecialSwiftParam(varDscInfo, strip(corInfoType), typeHnd)) + lvaInitSpecialSwiftParam(argLst, varDscInfo, strip(corInfoType), typeHnd)) { continue; } @@ -1366,19 +1367,32 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un #ifdef SWIFT_SUPPORT //----------------------------------------------------------------------------- -// lvaInitSpecialSwiftParam: -// If the parameter is a special Swift parameter then initialize it and return true. +// lvaInitSpecialSwiftParam: Initialize SwiftSelf/SwiftError* parameters. // // Parameters: +// argHnd - Handle for this parameter in the method's signature // varDsc - LclVarDsc* for the parameter // type - Type of the parameter // typeHnd - Class handle for the type of the parameter // -// Remarks: -// Handles SwiftSelf. +// Returns: +// true if parameter was initialized // -bool Compiler::lvaInitSpecialSwiftParam(InitVarDscInfo* varDscInfo, CorInfoType type, CORINFO_CLASS_HANDLE typeHnd) +bool Compiler::lvaInitSpecialSwiftParam(CORINFO_ARG_LIST_HANDLE argHnd, + InitVarDscInfo* varDscInfo, + CorInfoType type, + CORINFO_CLASS_HANDLE typeHnd) { + const bool argIsByrefOrPtr = (type == CORINFO_TYPE_BYREF) || (type == CORINFO_TYPE_PTR); + + if (argIsByrefOrPtr) + { + // For primitive types, we don't expect to be passed a CORINFO_CLASS_HANDLE; look up the actual handle + assert(typeHnd == nullptr); + CORINFO_CLASS_HANDLE clsHnd = info.compCompHnd->getArgClass(&info.compMethodInfo->args, argHnd); + type = info.compCompHnd->getChildType(clsHnd, &typeHnd); + } + if (type != CORINFO_TYPE_VALUECLASS) { return false; @@ -1393,7 +1407,17 @@ bool Compiler::lvaInitSpecialSwiftParam(InitVarDscInfo* varDscInfo, CorInfoType const char* className = info.compCompHnd->getClassNameFromMetadata(typeHnd, &namespaceName); if ((strcmp(className, "SwiftSelf") == 0) && (strcmp(namespaceName, "System.Runtime.InteropServices.Swift") == 0)) { - LclVarDsc* varDsc = varDscInfo->varDsc; + if (argIsByrefOrPtr) + { + BADCODE("Expected SwiftSelf struct, got pointer/reference"); + } + + if (lvaSwiftSelfArg != BAD_VAR_NUM) + { + BADCODE("Duplicate SwiftSelf parameter"); + } + + LclVarDsc* const varDsc = varDscInfo->varDsc; varDsc->SetArgReg(REG_SWIFT_SELF); varDsc->SetOtherArgReg(REG_NA); varDsc->lvIsRegArg = true; @@ -1405,6 +1429,34 @@ bool Compiler::lvaInitSpecialSwiftParam(InitVarDscInfo* varDscInfo, CorInfoType return true; } + if ((strcmp(className, "SwiftError") == 0) && (strcmp(namespaceName, "System.Runtime.InteropServices.Swift") == 0)) + { + if (!argIsByrefOrPtr) + { + BADCODE("Expected SwiftError pointer/reference, got struct"); + } + + if (lvaSwiftErrorArg != BAD_VAR_NUM) + { + BADCODE("Duplicate SwiftError* parameter"); + } + + // We won't actually be passing this SwiftError* in REG_SWIFT_ERROR (or any register, for that matter). + // We will check for this quirk when generating the prolog, + // and ensure this fake parameter doesn't take any registers/stack space + LclVarDsc* const varDsc = varDscInfo->varDsc; + varDsc->SetArgReg(REG_SWIFT_ERROR); + varDsc->SetOtherArgReg(REG_NA); + varDsc->lvIsRegArg = true; + lvaSwiftErrorArg = varDscInfo->varNum; + + // Instead, all usages of the SwiftError* parameter will be redirected to this pseudolocal. + lvaSwiftErrorLocal = lvaGrabTempWithImplicitUse(false DEBUGARG("SwiftError pseudolocal")); + lvaSetStruct(lvaSwiftErrorLocal, typeHnd, false); + lvaSetVarAddrExposed(lvaSwiftErrorLocal DEBUGARG(AddressExposedReason::ESCAPE_ADDRESS)); + return true; + } + return false; } #endif @@ -1644,6 +1696,10 @@ void Compiler::lvaClassifyParameterABI(Classifier& classifier) { wellKnownArg = WellKnownArg::SwiftSelf; } + else if (i == lvaSwiftErrorArg) + { + wellKnownArg = WellKnownArg::SwiftError; + } #endif lvaParameterPassingInfo[i] = classifier.Classify(this, dsc->TypeGet(), structLayout, wellKnownArg); @@ -1761,7 +1817,7 @@ void Compiler::lvaClassifyParameterABI() } } } -#endif +#endif // DEBUG } /***************************************************************************** diff --git a/src/coreclr/jit/regset.cpp b/src/coreclr/jit/regset.cpp index 5f5c80a4a19d6..12975850a404b 100644 --- a/src/coreclr/jit/regset.cpp +++ b/src/coreclr/jit/regset.cpp @@ -117,6 +117,16 @@ void RegSet::rsClearRegsModified() #endif // DEBUG rsModifiedRegsMask = RBM_NONE; + +#ifdef SWIFT_SUPPORT + // If this method has a SwiftError* parameter, we will return SwiftError::Value in REG_SWIFT_ERROR, + // so don't treat it as callee-save. + if (m_rsCompiler->lvaSwiftErrorArg != BAD_VAR_NUM) + { + rsAllCalleeSavedMask &= ~RBM_SWIFT_ERROR; + rsIntCalleeSavedMask &= ~RBM_SWIFT_ERROR; + } +#endif // SWIFT_SUPPORT } void RegSet::rsSetRegsModified(regMaskTP mask DEBUGARG(bool suppressDump)) @@ -258,6 +268,11 @@ RegSet::RegSet(Compiler* compiler, GCInfo& gcInfo) rsMaskPreSpillAlign = RBM_NONE; #endif +#ifdef SWIFT_SUPPORT + rsAllCalleeSavedMask = RBM_CALLEE_SAVED; + rsIntCalleeSavedMask = RBM_INT_CALLEE_SAVED; +#endif // SWIFT_SUPPORT + #ifdef DEBUG rsModifiedRegsMaskInitialized = false; #endif // DEBUG diff --git a/src/coreclr/jit/regset.h b/src/coreclr/jit/regset.h index 0924c410e3b85..dae93baebad30 100644 --- a/src/coreclr/jit/regset.h +++ b/src/coreclr/jit/regset.h @@ -74,6 +74,14 @@ class RegSet bool rsModifiedRegsMaskInitialized; // Has rsModifiedRegsMask been initialized? Guards against illegal use. #endif // DEBUG +#ifdef SWIFT_SUPPORT + regMaskTP rsAllCalleeSavedMask; + regMaskTP rsIntCalleeSavedMask; +#else // !SWIFT_SUPPORT + static constexpr regMaskTP rsAllCalleeSavedMask = RBM_CALLEE_SAVED; + static constexpr regMaskTP rsIntCalleeSavedMask = RBM_INT_CALLEE_SAVED; +#endif // !SWIFT_SUPPORT + public: regMaskTP rsGetModifiedRegsMask() const { @@ -81,6 +89,32 @@ class RegSet return rsModifiedRegsMask; } + regMaskTP rsGetModifiedCalleeSavedRegsMask() const + { + assert(rsModifiedRegsMaskInitialized); + return (rsModifiedRegsMask & rsAllCalleeSavedMask); + } + + regMaskTP rsGetModifiedIntCalleeSavedRegsMask() const + { + assert(rsModifiedRegsMaskInitialized); + return (rsModifiedRegsMask & rsIntCalleeSavedMask); + } + +#ifdef TARGET_AMD64 + regMaskTP rsGetModifiedOsrIntCalleeSavedRegsMask() const + { + assert(rsModifiedRegsMaskInitialized); + return (rsModifiedRegsMask & (rsIntCalleeSavedMask | RBM_EBP)); + } +#endif // TARGET_AMD64 + + regMaskTP rsGetModifiedFltCalleeSavedRegsMask() const + { + assert(rsModifiedRegsMaskInitialized); + return (rsModifiedRegsMask & RBM_FLT_CALLEE_SAVED); + } + void rsClearRegsModified(); void rsSetRegsModified(regMaskTP mask DEBUGARG(bool suppressDump = false)); diff --git a/src/coreclr/jit/target.h b/src/coreclr/jit/target.h index 4b1461efde0fe..06777fa9d5f70 100644 --- a/src/coreclr/jit/target.h +++ b/src/coreclr/jit/target.h @@ -498,6 +498,12 @@ inline regMaskTP fullIntArgRegMask(CorInfoCallConvExtension callConv) if (callConv == CorInfoCallConvExtension::Swift) { result |= RBM_SWIFT_SELF; + + // We don't pass any arguments in REG_SWIFT_ERROR, but as a quirk, + // we set the SwiftError* parameter to be passed in this register, + // and later ensure the parameter isn't given any registers/stack space + // to avoid interfering with other arguments. + result |= RBM_SWIFT_ERROR; } #endif diff --git a/src/coreclr/jit/targetamd64.h b/src/coreclr/jit/targetamd64.h index a15029c3c3924..5d37870d03b03 100644 --- a/src/coreclr/jit/targetamd64.h +++ b/src/coreclr/jit/targetamd64.h @@ -575,6 +575,6 @@ #define REG_SWIFT_ARG_RET_BUFF REG_RAX #define RBM_SWIFT_ARG_RET_BUFF RBM_RAX #define SWIFT_RET_BUFF_ARGNUM MAX_REG_ARG -#endif +#endif // UNIX_AMD64_ABI // clang-format on diff --git a/src/tests/Interop/Swift/SwiftErrorHandling/SwiftErrorHandling.cs b/src/tests/Interop/Swift/SwiftErrorHandling/SwiftErrorHandling.cs index d4b81bafcd4c8..b1575e04deabd 100644 --- a/src/tests/Interop/Swift/SwiftErrorHandling/SwiftErrorHandling.cs +++ b/src/tests/Interop/Swift/SwiftErrorHandling/SwiftErrorHandling.cs @@ -23,6 +23,40 @@ public class ErrorHandlingTests [DllImport(SwiftLib, EntryPoint = "$s18SwiftErrorHandling018conditionallyThrowB004willE0s5Int32VAE_tKF")] public static extern nint conditionallyThrowErrorOnStack(int willThrow, int dummy1, int dummy2, int dummy3, int dummy4, int dummy5, int dummy6, int dummy7, int dummy8, int dummy9, ref SwiftError error); + [UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvSwift) })] + [DllImport(SwiftLib, EntryPoint = "$s18SwiftErrorHandling26nativeFunctionWithCallback03setB0_ys5Int32V_yAEXEtF")] + public static extern unsafe void NativeFunctionWithCallback(int setError, delegate* unmanaged[Swift] callback, SwiftError* error); + + [UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvSwift) })] + [DllImport(SwiftLib, EntryPoint = "$s18SwiftErrorHandling26nativeFunctionWithCallback5value03setB0_s5Int32VAF_A3F_AFtXEtF")] + public static extern unsafe int NativeFunctionWithCallback(int value, int setError, delegate* unmanaged[Swift] callback, SwiftError* error); + + [UnmanagedCallersOnly(CallConvs = new Type[] { typeof(CallConvSwift) })] + private static unsafe void ConditionallySetErrorTo21(SwiftError* error, int setError) { + if (setError != 0) + { + *error = new SwiftError((void*)21); + } + else + { + *error = new SwiftError(null); + } + } + + [UnmanagedCallersOnly(CallConvs = new Type[] { typeof(CallConvSwift) })] + private static unsafe int ConditionallySetErrorAndReturn(SwiftError* error, int value, int setError) { + if (setError != 0) + { + *error = new SwiftError((void*)value); + } + else + { + *error = new SwiftError(null); + } + + return (value * 2); + } + [DllImport(SwiftLib, EntryPoint = "$s18SwiftErrorHandling05getMyB7Message4from13messageLengthSPys6UInt16VGSgs0B0_p_s5Int32VztF")] public unsafe static extern void* GetErrorMessage(void* handle, out int length); @@ -92,6 +126,40 @@ public unsafe static void TestSwiftErrorOnStackNotThrown() Assert.True(error.Value == null, "No Swift error was expected to be thrown."); Assert.True(result == 42, "The result from Swift does not match the expected value."); } + + [Fact] + [SkipOnMono("needs reverse P/Invoke support")] + public static unsafe void TestUnmanagedCallersOnly() + { + SwiftError error; + int expectedValue = 21; + NativeFunctionWithCallback(1, &ConditionallySetErrorTo21, &error); + + int value = (int)error.Value; + Assert.True(value == expectedValue, string.Format("The value retrieved does not match the expected value. Expected: {0}, Actual: {1}", expectedValue, value)); + + NativeFunctionWithCallback(0, &ConditionallySetErrorTo21, &error); + + Assert.True(error.Value == null, "Expected SwiftError value to be null."); + } + + [Fact] + [SkipOnMono("needs reverse P/Invoke support")] + public static unsafe void TestUnmanagedCallersOnlyWithReturn() + { + SwiftError error; + int expectedValue = 42; + int retValue = NativeFunctionWithCallback(expectedValue, 1, &ConditionallySetErrorAndReturn, &error); + + int value = (int)error.Value; + Assert.True(value == expectedValue, string.Format("The value retrieved does not match the expected value. Expected: {0}, Actual: {1}", expectedValue, value)); + Assert.True(retValue == (expectedValue * 2), string.Format("Return value does not match expected value. Expected: {0}, Actual: {1}", (expectedValue * 2), retValue)); + + retValue = NativeFunctionWithCallback(expectedValue, 0, &ConditionallySetErrorAndReturn, &error); + + Assert.True(error.Value == null, "Expected SwiftError value to be null."); + Assert.True(retValue == (expectedValue * 2), string.Format("Return value does not match expected value. Expected: {0}, Actual: {1}", (expectedValue * 2), retValue)); + } private static void SetErrorMessageForSwift(string message) { diff --git a/src/tests/Interop/Swift/SwiftErrorHandling/SwiftErrorHandling.swift b/src/tests/Interop/Swift/SwiftErrorHandling/SwiftErrorHandling.swift index 5058014a42ce3..9067ea2372dba 100644 --- a/src/tests/Interop/Swift/SwiftErrorHandling/SwiftErrorHandling.swift +++ b/src/tests/Interop/Swift/SwiftErrorHandling/SwiftErrorHandling.swift @@ -37,3 +37,11 @@ public func getMyErrorMessage(from error: Error, messageLength: inout Int32) -> public func freeStringBuffer(buffer: UnsafeMutablePointer) { buffer.deallocate() } + +public func nativeFunctionWithCallback(setError: Int32, _ callback: (Int32) -> Void) { + callback(setError) +} + +public func nativeFunctionWithCallback(value: Int32, setError: Int32, _ callback: (Int32, Int32) -> Int32) -> Int32 { + return callback(value, setError) +}