Skip to content

Commit de774ff

Browse files
authored
JIT: Add basic support for Swift reverse pinvokes (#100018)
Adds very rudimentary support for Swift reverse pinvokes. Since Swift function pointers are based on `SwiftSelf` also adds basic support for the `SwiftSelf` parameter. Structs are not supported yet, and neither is `SwiftError`. Adds 10 simple tests just so that we have something. These will be regenerated (with a larger number of probably 100 tests) once we have the struct support. The `SwiftSelf` parameter is not enregisterable because it is passed in a register that `genFnPrologCalleeRegArgs` does not handle. In the future this function should be rewritten to support this register, but there are plans to rewrite it to handle float and integer registers simultaneously, so that work can happen at that time.
1 parent 309185f commit de774ff

19 files changed

+731
-40
lines changed

src/coreclr/jit/codegencommon.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2987,6 +2987,18 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere
29872987
}
29882988
}
29892989

2990+
#ifdef SWIFT_SUPPORT
2991+
// The Swift self parameter is passed in a callee save register and is
2992+
// not part of the arg register order that this function relies on to
2993+
// handle conflicts. For this reason we always mark it as DNER and
2994+
// handle it outside the normal register arguments.
2995+
// TODO-CQ: Fix this.
2996+
if (varNum == compiler->lvaSwiftSelfArg)
2997+
{
2998+
continue;
2999+
}
3000+
#endif
3001+
29903002
var_types regType = compiler->mangleVarArgsType(varDsc->TypeGet());
29913003
// Change regType to the HFA type when we have a HFA argument
29923004
if (varDsc->lvIsHfaRegArg())
@@ -6131,6 +6143,14 @@ void CodeGen::genFnProlog()
61316143
intRegState.rsCalleeRegArgMaskLiveIn &= ~RBM_SECRET_STUB_PARAM;
61326144
}
61336145

6146+
#ifdef SWIFT_SUPPORT
6147+
if ((compiler->lvaSwiftSelfArg != BAD_VAR_NUM) && ((intRegState.rsCalleeRegArgMaskLiveIn & RBM_SWIFT_SELF) != 0))
6148+
{
6149+
GetEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, REG_SWIFT_SELF, compiler->lvaSwiftSelfArg, 0);
6150+
intRegState.rsCalleeRegArgMaskLiveIn &= ~RBM_SWIFT_SELF;
6151+
}
6152+
#endif
6153+
61346154
//
61356155
// Zero out the frame as needed
61366156
//

src/coreclr/jit/compiler.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10529,6 +10529,7 @@ HelperCallProperties Compiler::s_helperCallProperties;
1052910529
// Return Value:
1053010530
// true - tree kills GC refs on callee save registers
1053110531
// false - tree doesn't affect GC refs on callee save registers
10532+
//
1053210533
bool Compiler::killGCRefs(GenTree* tree)
1053310534
{
1053410535
if (tree->IsCall())
@@ -10825,6 +10826,10 @@ void Compiler::EnregisterStats::RecordLocal(const LclVarDsc* varDsc)
1082510826
m_simdUserForcesDep++;
1082610827
break;
1082710828

10829+
case DoNotEnregisterReason::NonStandardParameter:
10830+
m_nonStandardParameter++;
10831+
break;
10832+
1082810833
default:
1082910834
unreached();
1083010835
break;
@@ -10952,6 +10957,7 @@ void Compiler::EnregisterStats::Dump(FILE* fout) const
1095210957
PRINT_STATS(m_returnSpCheck, notEnreg);
1095310958
PRINT_STATS(m_callSpCheck, notEnreg);
1095410959
PRINT_STATS(m_simdUserForcesDep, notEnreg);
10960+
PRINT_STATS(m_nonStandardParameter, notEnreg);
1095510961

1095610962
fprintf(fout, "\nAddr exposed details:\n");
1095710963
if (m_addrExposed == 0)

src/coreclr/jit/compiler.h

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -456,13 +456,14 @@ enum class DoNotEnregisterReason
456456
#endif
457457
LclAddrNode, // the local is accessed with LCL_ADDR_VAR/FLD.
458458
CastTakesAddr,
459-
StoreBlkSrc, // the local is used as STORE_BLK source.
460-
SwizzleArg, // the local is passed using LCL_FLD as another type.
461-
BlockOpRet, // the struct is returned and it promoted or there is a cast.
462-
ReturnSpCheck, // the local is used to do SP check on return from function
463-
CallSpCheck, // the local is used to do SP check on every call
464-
SimdUserForcesDep, // a promoted struct was used by a SIMD/HWI node; it must be dependently promoted
465-
HiddenBufferStructArg // the argument is a hidden return buffer passed to a method.
459+
StoreBlkSrc, // the local is used as STORE_BLK source.
460+
SwizzleArg, // the local is passed using LCL_FLD as another type.
461+
BlockOpRet, // the struct is returned and it promoted or there is a cast.
462+
ReturnSpCheck, // the local is used to do SP check on return from function
463+
CallSpCheck, // the local is used to do SP check on every call
464+
SimdUserForcesDep, // a promoted struct was used by a SIMD/HWI node; it must be dependently promoted
465+
HiddenBufferStructArg, // the argument is a hidden return buffer passed to a method.
466+
NonStandardParameter, // local is a parameter that is passed in a register unhandled by genFnPrologCalleeRegArgs
466467
};
467468

468469
enum class AddressExposedReason
@@ -489,7 +490,6 @@ class LclVarDsc
489490
// The constructor. Most things can just be zero'ed.
490491
//
491492
// Initialize the ArgRegs to REG_STK.
492-
// Morph will update if this local is passed in a register.
493493
LclVarDsc()
494494
: _lvArgReg(REG_STK)
495495
#if FEATURE_MULTIREG_ARGS
@@ -3865,6 +3865,10 @@ class Compiler
38653865
// where it is used to detect tail-call chains.
38663866
unsigned lvaRetAddrVar;
38673867

3868+
#ifdef SWIFT_SUPPORT
3869+
unsigned lvaSwiftSelfArg;
3870+
#endif
3871+
38683872
#if defined(DEBUG) && defined(TARGET_XARCH)
38693873

38703874
unsigned lvaReturnSpCheck; // Stores SP to confirm it is not corrupted on return.
@@ -3987,6 +3991,8 @@ class Compiler
39873991
CORINFO_ARG_LIST_HANDLE varList,
39883992
CORINFO_SIG_INFO* varSig);
39893993

3994+
bool lvaInitSpecialSwiftParam(InitVarDscInfo* varDscInfo, CorInfoType type, CORINFO_CLASS_HANDLE typeHnd);
3995+
39903996
var_types lvaGetActualType(unsigned lclNum);
39913997
var_types lvaGetRealType(unsigned lclNum);
39923998

@@ -7984,6 +7990,7 @@ class Compiler
79847990

79857991
public:
79867992
regNumber raUpdateRegStateForArg(RegState* regState, LclVarDsc* argDsc);
7993+
void raCheckValidIntParamReg(LclVarDsc* dsc, regNumber inArgReg);
79877994

79887995
void raMarkStkVars();
79897996

@@ -10636,6 +10643,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1063610643
unsigned m_returnSpCheck;
1063710644
unsigned m_callSpCheck;
1063810645
unsigned m_simdUserForcesDep;
10646+
unsigned m_nonStandardParameter;
1063910647
unsigned m_liveInOutHndlr;
1064010648
unsigned m_depField;
1064110649
unsigned m_noRegVars;

src/coreclr/jit/lclvars.cpp

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ void Compiler::lvaInit()
6969
lvaMonAcquired = BAD_VAR_NUM;
7070
lvaRetAddrVar = BAD_VAR_NUM;
7171

72+
#ifdef SWIFT_SUPPORT
73+
lvaSwiftSelfArg = BAD_VAR_NUM;
74+
#endif
75+
7276
lvaInlineeReturnSpillTemp = BAD_VAR_NUM;
7377

7478
gsShadowVarInfo = nullptr;
@@ -622,6 +626,17 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un
622626
lvaSetClass(varDscInfo->varNum, clsHnd);
623627
}
624628

629+
// The final home for this incoming parameter might be our local stack frame.
630+
varDsc->lvOnFrame = true;
631+
632+
#ifdef SWIFT_SUPPORT
633+
if ((info.compCallConv == CorInfoCallConvExtension::Swift) &&
634+
lvaInitSpecialSwiftParam(varDscInfo, strip(corInfoType), typeHnd))
635+
{
636+
continue;
637+
}
638+
#endif
639+
625640
// For ARM, ARM64, LOONGARCH64, RISCV64 and AMD64 varargs, all arguments go in integer registers
626641
var_types argType = mangleVarArgsType(varDsc->TypeGet());
627642

@@ -821,10 +836,6 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un
821836
}
822837
#endif // UNIX_AMD64_ABI
823838

824-
// The final home for this incoming register might be our local stack frame.
825-
// For System V platforms the final home will always be on the local stack frame.
826-
varDsc->lvOnFrame = true;
827-
828839
bool canPassArgInRegisters = false;
829840

830841
#if defined(UNIX_AMD64_ABI)
@@ -1301,6 +1312,48 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un
13011312
#endif // TARGET_ARM
13021313
}
13031314

1315+
#ifdef SWIFT_SUPPORT
1316+
//-----------------------------------------------------------------------------
1317+
// lvaInitSpecialSwiftParam:
1318+
// If the parameter is a special Swift parameter then initialize it and return true.
1319+
//
1320+
// Parameters:
1321+
// varDsc - LclVarDsc* for the parameter
1322+
// type - Type of the parameter
1323+
// typeHnd - Class handle for the type of the parameter
1324+
//
1325+
// Remarks:
1326+
// Handles SwiftSelf.
1327+
//
1328+
bool Compiler::lvaInitSpecialSwiftParam(InitVarDscInfo* varDscInfo, CorInfoType type, CORINFO_CLASS_HANDLE typeHnd)
1329+
{
1330+
if (type != CORINFO_TYPE_VALUECLASS)
1331+
{
1332+
return false;
1333+
}
1334+
1335+
if (!info.compCompHnd->isIntrinsicType(typeHnd))
1336+
{
1337+
return false;
1338+
}
1339+
1340+
const char* namespaceName;
1341+
const char* className = info.compCompHnd->getClassNameFromMetadata(typeHnd, &namespaceName);
1342+
if ((strcmp(className, "SwiftSelf") == 0) && (strcmp(namespaceName, "System.Runtime.InteropServices.Swift") == 0))
1343+
{
1344+
LclVarDsc* varDsc = varDscInfo->varDsc;
1345+
varDsc->SetArgReg(REG_SWIFT_SELF);
1346+
varDsc->SetOtherArgReg(REG_NA);
1347+
varDsc->lvIsRegArg = true;
1348+
lvaSwiftSelfArg = varDscInfo->varNum;
1349+
lvaSetVarDoNotEnregister(lvaSwiftSelfArg DEBUGARG(DoNotEnregisterReason::NonStandardParameter));
1350+
return true;
1351+
}
1352+
1353+
return false;
1354+
}
1355+
#endif
1356+
13041357
/*****************************************************************************/
13051358
void Compiler::lvaInitGenericsCtxt(InitVarDscInfo* varDscInfo)
13061359
{
@@ -2752,6 +2805,10 @@ void Compiler::lvaSetVarDoNotEnregister(unsigned varNum DEBUGARG(DoNotEnregister
27522805
JITDUMP("Promoted struct used by a SIMD/HWI node\n");
27532806
break;
27542807

2808+
case DoNotEnregisterReason::NonStandardParameter:
2809+
JITDUMP("Non-standard parameter\n");
2810+
break;
2811+
27552812
default:
27562813
unreached();
27572814
break;

src/coreclr/jit/lsrabuild.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2126,14 +2126,14 @@ void LinearScan::UpdateRegStateForStructArg(LclVarDsc* argDsc)
21262126

21272127
if ((argDsc->GetArgReg() != REG_STK) && (argDsc->GetArgReg() != REG_NA))
21282128
{
2129-
if (genRegMask(argDsc->GetArgReg()) & (RBM_ALLFLOAT))
2129+
if ((genRegMask(argDsc->GetArgReg()) & RBM_ALLFLOAT) != RBM_NONE)
21302130
{
2131-
assert(genRegMask(argDsc->GetArgReg()) & (RBM_FLTARG_REGS));
2131+
assert((genRegMask(argDsc->GetArgReg()) & RBM_FLTARG_REGS) != RBM_NONE);
21322132
floatRegState->rsCalleeRegArgMaskLiveIn |= genRegMask(argDsc->GetArgReg());
21332133
}
21342134
else
21352135
{
2136-
assert(genRegMask(argDsc->GetArgReg()) & (RBM_ARG_REGS));
2136+
compiler->raCheckValidIntParamReg(argDsc, argDsc->GetArgReg());
21372137
intRegState->rsCalleeRegArgMaskLiveIn |= genRegMask(argDsc->GetArgReg());
21382138
}
21392139
}
@@ -2147,7 +2147,7 @@ void LinearScan::UpdateRegStateForStructArg(LclVarDsc* argDsc)
21472147
}
21482148
else
21492149
{
2150-
assert(genRegMask(argDsc->GetOtherArgReg()) & (RBM_ARG_REGS));
2150+
compiler->raCheckValidIntParamReg(argDsc, argDsc->GetOtherArgReg());
21512151
intRegState->rsCalleeRegArgMaskLiveIn |= genRegMask(argDsc->GetOtherArgReg());
21522152
}
21532153
}

src/coreclr/jit/regalloc.cpp

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -104,23 +104,11 @@ regNumber Compiler::raUpdateRegStateForArg(RegState* regState, LclVarDsc* argDsc
104104

105105
if (regState->rsIsFloat)
106106
{
107-
noway_assert(inArgMask & RBM_FLTARG_REGS);
107+
assert(inArgMask & RBM_FLTARG_REGS);
108108
}
109-
else // regState is for the integer registers
109+
else
110110
{
111-
// This might be the fixed return buffer register argument (on ARM64)
112-
// We check and allow inArgReg to be theFixedRetBuffReg
113-
if (hasFixedRetBuffReg() && (inArgReg == theFixedRetBuffReg()))
114-
{
115-
// We should have a TYP_BYREF or TYP_I_IMPL arg and not a TYP_STRUCT arg
116-
noway_assert(argDsc->lvType == TYP_BYREF || argDsc->lvType == TYP_I_IMPL);
117-
// We should have recorded the variable number for the return buffer arg
118-
noway_assert(info.compRetBuffArg != BAD_VAR_NUM);
119-
}
120-
else // we have a regular arg
121-
{
122-
noway_assert(inArgMask & RBM_ARG_REGS);
123-
}
111+
raCheckValidIntParamReg(argDsc, inArgReg);
124112
}
125113

126114
regState->rsCalleeRegArgMaskLiveIn |= inArgMask;
@@ -181,6 +169,42 @@ regNumber Compiler::raUpdateRegStateForArg(RegState* regState, LclVarDsc* argDsc
181169
return inArgReg;
182170
}
183171

172+
//------------------------------------------------------------------------
173+
// raCheckValidIntParamReg:
174+
// Check that an integer parameter's register information matches what we
175+
// expect.
176+
//
177+
// Parameters:
178+
// argDsc - LclVarDsc* for the parameter
179+
// inArgReg - Register used by the parameter
180+
//
181+
void Compiler::raCheckValidIntParamReg(LclVarDsc* argDsc, regNumber inArgReg)
182+
{
183+
#ifdef DEBUG
184+
// This might be the fixed return buffer register argument (on ARM64)
185+
// We check and allow inArgReg to be theFixedRetBuffReg
186+
if (hasFixedRetBuffReg() && (inArgReg == theFixedRetBuffReg()))
187+
{
188+
// We should have a TYP_BYREF or TYP_I_IMPL arg and not a TYP_STRUCT arg
189+
assert(argDsc->lvType == TYP_BYREF || argDsc->lvType == TYP_I_IMPL);
190+
// We should have recorded the variable number for the return buffer arg
191+
assert(info.compRetBuffArg != BAD_VAR_NUM);
192+
}
193+
#ifdef SWIFT_SUPPORT
194+
else if ((info.compCallConv == CorInfoCallConvExtension::Swift) && (inArgReg == REG_SWIFT_SELF))
195+
{
196+
assert((lvaSwiftSelfArg != BAD_VAR_NUM) &&
197+
((argDsc == lvaGetDesc(lvaSwiftSelfArg)) ||
198+
(argDsc->lvIsStructField && argDsc->lvParentLcl == lvaSwiftSelfArg)));
199+
}
200+
#endif
201+
else // we have a regular arg
202+
{
203+
assert((genRegMask(inArgReg) & RBM_ARG_REGS) != RBM_NONE);
204+
}
205+
#endif
206+
}
207+
184208
//------------------------------------------------------------------------
185209
// rpMustCreateEBPFrame:
186210
// Returns true when we must create an EBP frame

src/coreclr/jit/scopeinfo.cpp

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1727,8 +1727,6 @@ void CodeGen::psiBegProlog()
17271727
regNumber otherRegNum = REG_NA;
17281728
for (unsigned nCnt = 0; nCnt < structDesc.eightByteCount; nCnt++)
17291729
{
1730-
var_types regType = TYP_UNDEF;
1731-
17321730
if (nCnt == 0)
17331731
{
17341732
regNum = lclVarDsc->GetArgReg();
@@ -1741,12 +1739,6 @@ void CodeGen::psiBegProlog()
17411739
{
17421740
assert(false && "Invalid eightbyte number.");
17431741
}
1744-
1745-
regType = compiler->GetEightByteType(structDesc, nCnt);
1746-
#ifdef DEBUG
1747-
regType = compiler->mangleVarArgsType(regType);
1748-
assert(genMapRegNumToRegArgNum((nCnt == 0 ? regNum : otherRegNum), regType) != (unsigned)-1);
1749-
#endif // DEBUG
17501742
}
17511743

17521744
varLocation.storeVariableInRegisters(regNum, otherRegNum);
@@ -1795,7 +1787,6 @@ void CodeGen::psiBegProlog()
17951787
regType = lclVarDsc->GetHfaType();
17961788
}
17971789
#endif // defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
1798-
assert(genMapRegNumToRegArgNum(lclVarDsc->GetArgReg(), regType) != (unsigned)-1);
17991790
#endif // DEBUG
18001791
varLocation.storeVariableInRegisters(lclVarDsc->GetArgReg(), REG_NA);
18011792
}

src/coreclr/jit/targetamd64.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,7 @@
568568
#define REG_SWIFT_ERROR REG_R12
569569
#define RBM_SWIFT_ERROR RBM_R12
570570
#define REG_SWIFT_SELF REG_R13
571+
#define RBM_SWIFT_SELF RBM_R13
571572

572573
#define REG_SWIFT_INTRET_ORDER REG_RAX,REG_RDX,REG_RCX,REG_R8
573574
#define REG_SWIFT_FLOATRET_ORDER REG_XMM0,REG_XMM1,REG_XMM2,REG_XMM3

src/coreclr/jit/targetarm64.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,7 @@
385385
#define REG_SWIFT_ERROR REG_R21
386386
#define RBM_SWIFT_ERROR RBM_R21
387387
#define REG_SWIFT_SELF REG_R20
388+
#define RBM_SWIFT_SELF RBM_R20
388389
#define REG_SWIFT_INTRET_ORDER REG_R0,REG_R1,REG_R2,REG_R3
389390
#define REG_SWIFT_FLOATRET_ORDER REG_V0,REG_V1,REG_V2,REG_V3
390391

src/tests/Interop/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,5 @@ if(CLR_CMAKE_TARGET_APPLE)
107107
add_subdirectory(Swift/SwiftInvalidCallConv)
108108
add_subdirectory(Swift/SwiftAbiStress)
109109
add_subdirectory(Swift/SwiftRetAbiStress)
110+
add_subdirectory(Swift/SwiftCallbackAbiStress)
110111
endif()

src/tests/Interop/Swift/SwiftAbiStress/SwiftAbiStress.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
14
using System;
25
using System.Runtime.CompilerServices;
36
using System.Runtime.InteropServices;

src/tests/Interop/Swift/SwiftAbiStress/SwiftAbiStress.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
14
import Foundation
25

36
struct HasherFNV1a {

0 commit comments

Comments
 (0)