Skip to content

Commit

Permalink
JIT: Add basic support for Swift reverse pinvokes (dotnet#100018)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jakobbotsch authored Mar 21, 2024
1 parent 309185f commit de774ff
Show file tree
Hide file tree
Showing 19 changed files with 731 additions and 40 deletions.
20 changes: 20 additions & 0 deletions src/coreclr/jit/codegencommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2987,6 +2987,18 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere
}
}

#ifdef SWIFT_SUPPORT
// The Swift self parameter is passed in a callee save register and is
// not part of the arg register order that this function relies on to
// handle conflicts. For this reason we always mark it as DNER and
// handle it outside the normal register arguments.
// TODO-CQ: Fix this.
if (varNum == compiler->lvaSwiftSelfArg)
{
continue;
}
#endif

var_types regType = compiler->mangleVarArgsType(varDsc->TypeGet());
// Change regType to the HFA type when we have a HFA argument
if (varDsc->lvIsHfaRegArg())
Expand Down Expand Up @@ -6131,6 +6143,14 @@ void CodeGen::genFnProlog()
intRegState.rsCalleeRegArgMaskLiveIn &= ~RBM_SECRET_STUB_PARAM;
}

#ifdef SWIFT_SUPPORT
if ((compiler->lvaSwiftSelfArg != BAD_VAR_NUM) && ((intRegState.rsCalleeRegArgMaskLiveIn & RBM_SWIFT_SELF) != 0))
{
GetEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, REG_SWIFT_SELF, compiler->lvaSwiftSelfArg, 0);
intRegState.rsCalleeRegArgMaskLiveIn &= ~RBM_SWIFT_SELF;
}
#endif

//
// Zero out the frame as needed
//
Expand Down
6 changes: 6 additions & 0 deletions src/coreclr/jit/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10529,6 +10529,7 @@ HelperCallProperties Compiler::s_helperCallProperties;
// Return Value:
// true - tree kills GC refs on callee save registers
// false - tree doesn't affect GC refs on callee save registers
//
bool Compiler::killGCRefs(GenTree* tree)
{
if (tree->IsCall())
Expand Down Expand Up @@ -10825,6 +10826,10 @@ void Compiler::EnregisterStats::RecordLocal(const LclVarDsc* varDsc)
m_simdUserForcesDep++;
break;

case DoNotEnregisterReason::NonStandardParameter:
m_nonStandardParameter++;
break;

default:
unreached();
break;
Expand Down Expand Up @@ -10952,6 +10957,7 @@ void Compiler::EnregisterStats::Dump(FILE* fout) const
PRINT_STATS(m_returnSpCheck, notEnreg);
PRINT_STATS(m_callSpCheck, notEnreg);
PRINT_STATS(m_simdUserForcesDep, notEnreg);
PRINT_STATS(m_nonStandardParameter, notEnreg);

fprintf(fout, "\nAddr exposed details:\n");
if (m_addrExposed == 0)
Expand Down
24 changes: 16 additions & 8 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -456,13 +456,14 @@ enum class DoNotEnregisterReason
#endif
LclAddrNode, // the local is accessed with LCL_ADDR_VAR/FLD.
CastTakesAddr,
StoreBlkSrc, // the local is used as STORE_BLK source.
SwizzleArg, // the local is passed using LCL_FLD as another type.
BlockOpRet, // the struct is returned and it promoted or there is a cast.
ReturnSpCheck, // the local is used to do SP check on return from function
CallSpCheck, // the local is used to do SP check on every call
SimdUserForcesDep, // a promoted struct was used by a SIMD/HWI node; it must be dependently promoted
HiddenBufferStructArg // the argument is a hidden return buffer passed to a method.
StoreBlkSrc, // the local is used as STORE_BLK source.
SwizzleArg, // the local is passed using LCL_FLD as another type.
BlockOpRet, // the struct is returned and it promoted or there is a cast.
ReturnSpCheck, // the local is used to do SP check on return from function
CallSpCheck, // the local is used to do SP check on every call
SimdUserForcesDep, // a promoted struct was used by a SIMD/HWI node; it must be dependently promoted
HiddenBufferStructArg, // the argument is a hidden return buffer passed to a method.
NonStandardParameter, // local is a parameter that is passed in a register unhandled by genFnPrologCalleeRegArgs
};

enum class AddressExposedReason
Expand All @@ -489,7 +490,6 @@ class LclVarDsc
// The constructor. Most things can just be zero'ed.
//
// Initialize the ArgRegs to REG_STK.
// Morph will update if this local is passed in a register.
LclVarDsc()
: _lvArgReg(REG_STK)
#if FEATURE_MULTIREG_ARGS
Expand Down Expand Up @@ -3865,6 +3865,10 @@ class Compiler
// where it is used to detect tail-call chains.
unsigned lvaRetAddrVar;

#ifdef SWIFT_SUPPORT
unsigned lvaSwiftSelfArg;
#endif

#if defined(DEBUG) && defined(TARGET_XARCH)

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

bool lvaInitSpecialSwiftParam(InitVarDscInfo* varDscInfo, CorInfoType type, CORINFO_CLASS_HANDLE typeHnd);

var_types lvaGetActualType(unsigned lclNum);
var_types lvaGetRealType(unsigned lclNum);

Expand Down Expand Up @@ -7984,6 +7990,7 @@ class Compiler

public:
regNumber raUpdateRegStateForArg(RegState* regState, LclVarDsc* argDsc);
void raCheckValidIntParamReg(LclVarDsc* dsc, regNumber inArgReg);

void raMarkStkVars();

Expand Down Expand Up @@ -10636,6 +10643,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
unsigned m_returnSpCheck;
unsigned m_callSpCheck;
unsigned m_simdUserForcesDep;
unsigned m_nonStandardParameter;
unsigned m_liveInOutHndlr;
unsigned m_depField;
unsigned m_noRegVars;
Expand Down
65 changes: 61 additions & 4 deletions src/coreclr/jit/lclvars.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ void Compiler::lvaInit()
lvaMonAcquired = BAD_VAR_NUM;
lvaRetAddrVar = BAD_VAR_NUM;

#ifdef SWIFT_SUPPORT
lvaSwiftSelfArg = BAD_VAR_NUM;
#endif

lvaInlineeReturnSpillTemp = BAD_VAR_NUM;

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

// The final home for this incoming parameter might be our local stack frame.
varDsc->lvOnFrame = true;

#ifdef SWIFT_SUPPORT
if ((info.compCallConv == CorInfoCallConvExtension::Swift) &&
lvaInitSpecialSwiftParam(varDscInfo, strip(corInfoType), typeHnd))
{
continue;
}
#endif

// For ARM, ARM64, LOONGARCH64, RISCV64 and AMD64 varargs, all arguments go in integer registers
var_types argType = mangleVarArgsType(varDsc->TypeGet());

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

// The final home for this incoming register might be our local stack frame.
// For System V platforms the final home will always be on the local stack frame.
varDsc->lvOnFrame = true;

bool canPassArgInRegisters = false;

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

#ifdef SWIFT_SUPPORT
//-----------------------------------------------------------------------------
// lvaInitSpecialSwiftParam:
// If the parameter is a special Swift parameter then initialize it and return true.
//
// Parameters:
// varDsc - LclVarDsc* for the parameter
// type - Type of the parameter
// typeHnd - Class handle for the type of the parameter
//
// Remarks:
// Handles SwiftSelf.
//
bool Compiler::lvaInitSpecialSwiftParam(InitVarDscInfo* varDscInfo, CorInfoType type, CORINFO_CLASS_HANDLE typeHnd)
{
if (type != CORINFO_TYPE_VALUECLASS)
{
return false;
}

if (!info.compCompHnd->isIntrinsicType(typeHnd))
{
return false;
}

const char* namespaceName;
const char* className = info.compCompHnd->getClassNameFromMetadata(typeHnd, &namespaceName);
if ((strcmp(className, "SwiftSelf") == 0) && (strcmp(namespaceName, "System.Runtime.InteropServices.Swift") == 0))
{
LclVarDsc* varDsc = varDscInfo->varDsc;
varDsc->SetArgReg(REG_SWIFT_SELF);
varDsc->SetOtherArgReg(REG_NA);
varDsc->lvIsRegArg = true;
lvaSwiftSelfArg = varDscInfo->varNum;
lvaSetVarDoNotEnregister(lvaSwiftSelfArg DEBUGARG(DoNotEnregisterReason::NonStandardParameter));
return true;
}

return false;
}
#endif

/*****************************************************************************/
void Compiler::lvaInitGenericsCtxt(InitVarDscInfo* varDscInfo)
{
Expand Down Expand Up @@ -2752,6 +2805,10 @@ void Compiler::lvaSetVarDoNotEnregister(unsigned varNum DEBUGARG(DoNotEnregister
JITDUMP("Promoted struct used by a SIMD/HWI node\n");
break;

case DoNotEnregisterReason::NonStandardParameter:
JITDUMP("Non-standard parameter\n");
break;

default:
unreached();
break;
Expand Down
8 changes: 4 additions & 4 deletions src/coreclr/jit/lsrabuild.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2126,14 +2126,14 @@ void LinearScan::UpdateRegStateForStructArg(LclVarDsc* argDsc)

if ((argDsc->GetArgReg() != REG_STK) && (argDsc->GetArgReg() != REG_NA))
{
if (genRegMask(argDsc->GetArgReg()) & (RBM_ALLFLOAT))
if ((genRegMask(argDsc->GetArgReg()) & RBM_ALLFLOAT) != RBM_NONE)
{
assert(genRegMask(argDsc->GetArgReg()) & (RBM_FLTARG_REGS));
assert((genRegMask(argDsc->GetArgReg()) & RBM_FLTARG_REGS) != RBM_NONE);
floatRegState->rsCalleeRegArgMaskLiveIn |= genRegMask(argDsc->GetArgReg());
}
else
{
assert(genRegMask(argDsc->GetArgReg()) & (RBM_ARG_REGS));
compiler->raCheckValidIntParamReg(argDsc, argDsc->GetArgReg());
intRegState->rsCalleeRegArgMaskLiveIn |= genRegMask(argDsc->GetArgReg());
}
}
Expand All @@ -2147,7 +2147,7 @@ void LinearScan::UpdateRegStateForStructArg(LclVarDsc* argDsc)
}
else
{
assert(genRegMask(argDsc->GetOtherArgReg()) & (RBM_ARG_REGS));
compiler->raCheckValidIntParamReg(argDsc, argDsc->GetOtherArgReg());
intRegState->rsCalleeRegArgMaskLiveIn |= genRegMask(argDsc->GetOtherArgReg());
}
}
Expand Down
54 changes: 39 additions & 15 deletions src/coreclr/jit/regalloc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,23 +104,11 @@ regNumber Compiler::raUpdateRegStateForArg(RegState* regState, LclVarDsc* argDsc

if (regState->rsIsFloat)
{
noway_assert(inArgMask & RBM_FLTARG_REGS);
assert(inArgMask & RBM_FLTARG_REGS);
}
else // regState is for the integer registers
else
{
// This might be the fixed return buffer register argument (on ARM64)
// We check and allow inArgReg to be theFixedRetBuffReg
if (hasFixedRetBuffReg() && (inArgReg == theFixedRetBuffReg()))
{
// We should have a TYP_BYREF or TYP_I_IMPL arg and not a TYP_STRUCT arg
noway_assert(argDsc->lvType == TYP_BYREF || argDsc->lvType == TYP_I_IMPL);
// We should have recorded the variable number for the return buffer arg
noway_assert(info.compRetBuffArg != BAD_VAR_NUM);
}
else // we have a regular arg
{
noway_assert(inArgMask & RBM_ARG_REGS);
}
raCheckValidIntParamReg(argDsc, inArgReg);
}

regState->rsCalleeRegArgMaskLiveIn |= inArgMask;
Expand Down Expand Up @@ -181,6 +169,42 @@ regNumber Compiler::raUpdateRegStateForArg(RegState* regState, LclVarDsc* argDsc
return inArgReg;
}

//------------------------------------------------------------------------
// raCheckValidIntParamReg:
// Check that an integer parameter's register information matches what we
// expect.
//
// Parameters:
// argDsc - LclVarDsc* for the parameter
// inArgReg - Register used by the parameter
//
void Compiler::raCheckValidIntParamReg(LclVarDsc* argDsc, regNumber inArgReg)
{
#ifdef DEBUG
// This might be the fixed return buffer register argument (on ARM64)
// We check and allow inArgReg to be theFixedRetBuffReg
if (hasFixedRetBuffReg() && (inArgReg == theFixedRetBuffReg()))
{
// We should have a TYP_BYREF or TYP_I_IMPL arg and not a TYP_STRUCT arg
assert(argDsc->lvType == TYP_BYREF || argDsc->lvType == TYP_I_IMPL);
// We should have recorded the variable number for the return buffer arg
assert(info.compRetBuffArg != BAD_VAR_NUM);
}
#ifdef SWIFT_SUPPORT
else if ((info.compCallConv == CorInfoCallConvExtension::Swift) && (inArgReg == REG_SWIFT_SELF))
{
assert((lvaSwiftSelfArg != BAD_VAR_NUM) &&
((argDsc == lvaGetDesc(lvaSwiftSelfArg)) ||
(argDsc->lvIsStructField && argDsc->lvParentLcl == lvaSwiftSelfArg)));
}
#endif
else // we have a regular arg
{
assert((genRegMask(inArgReg) & RBM_ARG_REGS) != RBM_NONE);
}
#endif
}

//------------------------------------------------------------------------
// rpMustCreateEBPFrame:
// Returns true when we must create an EBP frame
Expand Down
9 changes: 0 additions & 9 deletions src/coreclr/jit/scopeinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1727,8 +1727,6 @@ void CodeGen::psiBegProlog()
regNumber otherRegNum = REG_NA;
for (unsigned nCnt = 0; nCnt < structDesc.eightByteCount; nCnt++)
{
var_types regType = TYP_UNDEF;

if (nCnt == 0)
{
regNum = lclVarDsc->GetArgReg();
Expand All @@ -1741,12 +1739,6 @@ void CodeGen::psiBegProlog()
{
assert(false && "Invalid eightbyte number.");
}

regType = compiler->GetEightByteType(structDesc, nCnt);
#ifdef DEBUG
regType = compiler->mangleVarArgsType(regType);
assert(genMapRegNumToRegArgNum((nCnt == 0 ? regNum : otherRegNum), regType) != (unsigned)-1);
#endif // DEBUG
}

varLocation.storeVariableInRegisters(regNum, otherRegNum);
Expand Down Expand Up @@ -1795,7 +1787,6 @@ void CodeGen::psiBegProlog()
regType = lclVarDsc->GetHfaType();
}
#endif // defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
assert(genMapRegNumToRegArgNum(lclVarDsc->GetArgReg(), regType) != (unsigned)-1);
#endif // DEBUG
varLocation.storeVariableInRegisters(lclVarDsc->GetArgReg(), REG_NA);
}
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/jit/targetamd64.h
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,7 @@
#define REG_SWIFT_ERROR REG_R12
#define RBM_SWIFT_ERROR RBM_R12
#define REG_SWIFT_SELF REG_R13
#define RBM_SWIFT_SELF RBM_R13

#define REG_SWIFT_INTRET_ORDER REG_RAX,REG_RDX,REG_RCX,REG_R8
#define REG_SWIFT_FLOATRET_ORDER REG_XMM0,REG_XMM1,REG_XMM2,REG_XMM3
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/jit/targetarm64.h
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,7 @@
#define REG_SWIFT_ERROR REG_R21
#define RBM_SWIFT_ERROR RBM_R21
#define REG_SWIFT_SELF REG_R20
#define RBM_SWIFT_SELF RBM_R20
#define REG_SWIFT_INTRET_ORDER REG_R0,REG_R1,REG_R2,REG_R3
#define REG_SWIFT_FLOATRET_ORDER REG_V0,REG_V1,REG_V2,REG_V3

Expand Down
1 change: 1 addition & 0 deletions src/tests/Interop/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,5 @@ if(CLR_CMAKE_TARGET_APPLE)
add_subdirectory(Swift/SwiftInvalidCallConv)
add_subdirectory(Swift/SwiftAbiStress)
add_subdirectory(Swift/SwiftRetAbiStress)
add_subdirectory(Swift/SwiftCallbackAbiStress)
endif()
3 changes: 3 additions & 0 deletions src/tests/Interop/Swift/SwiftAbiStress/SwiftAbiStress.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
Expand Down
3 changes: 3 additions & 0 deletions src/tests/Interop/Swift/SwiftAbiStress/SwiftAbiStress.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

import Foundation

struct HasherFNV1a {
Expand Down
Loading

0 comments on commit de774ff

Please sign in to comment.